Beste SQL-Interviewfragen mit mittlerer Schwierigkeit

Die ersten 70% des SQL-Kurses scheinen ziemlich einfach zu sein. Schwierigkeiten beginnen bei den restlichen 30%.

Von 2015 bis 2019 habe ich vier Interviews für die Positionen eines Datenanalysten und Datenanalysespezialisten in mehr als einem Dutzend Unternehmen durchgeführt. Nach einem weiteren erfolglosen Interview im Jahr 2017 - als ich über komplexe SQL-Fragen verwirrt war - begann ich, ein Problembuch mit SQL-Fragen mittlerer und hoher Komplexität zu erstellen, um mich besser auf Interviews vorzubereiten. Dieser Leitfaden hat sich in der letzten Runde der Interviews im Jahr 2019 als nützlich erwiesen. Im vergangenen Jahr habe ich diesen Leitfaden mit ein paar Freunden geteilt und dank der zusätzlichen Freizeit aufgrund einer Pandemie habe ich ihn poliert und dieses Dokument zusammengestellt.

Es gibt viele großartige SQL-Tutorials für Anfänger. Meine Favoriten sindInteraktive SQL- und Select Star SQL- Kurse der Codecademy von Zi Chung Kao. In Wirklichkeit sind die ersten 70% des SQL-Kurses recht einfach, und die eigentlichen Schwierigkeiten beginnen bei den verbleibenden 30%, die in den Anfängerhandbüchern nicht behandelt werden. Bei Interviews für Datenanalysten und Datenanalysespezialisten in Technologieunternehmen werden daher häufig Fragen zu diesen 30% gestellt.

Überraschenderweise habe ich keine erschöpfende Quelle für solche Probleme mit mittlerem Schwierigkeitsgrad gefunden, deshalb habe ich diesen Leitfaden zusammengestellt.

Es ist nützlich für Interviews, erhöht aber gleichzeitig Ihre Effektivität in Ihren aktuellen und zukünftigen Jobs. Persönlich glaube ich, dass einige der genannten SQL-Vorlagen auch für ETL-Systeme nützlich sind, auf denen Berichterstellungstools und Datenanalysefunktionen ausgeführt werden, um Trends zu identifizieren.

Inhalt



Sie müssen verstehen, dass sie bei Interviews mit Datenanalysten und Datenanalysten nicht nur Fragen zu SQL stellen. Weitere häufig verwendete Themen sind die Diskussion früherer Projekte, A / B-Tests, die Entwicklung von Metriken und offene Analyseprobleme. Vor ungefähr drei Jahren veröffentlichte Quora Tipps zum Vorstellungsgespräch für eine Position als Produktanalyst auf Facebook. Dort wird dieses Thema ausführlicher besprochen. Wenn Ihnen jedoch die Verbesserung Ihrer SQL-Kenntnisse in Ihrem Interview hilft, ist dieser Leitfaden die Zeit wert.

In Zukunft kann ich den Code aus diesem Handbuch auf eine Site wie Select Star SQL portierenum das Schreiben von SQL-Anweisungen zu vereinfachen - und das Ergebnis der Codeausführung in Echtzeit anzuzeigen. Fügen Sie der Plattform optional Fragen als Probleme hinzu, um sich auf LeetCode- Interviews vorzubereiten . In der Zwischenzeit wollte ich dieses Dokument nur veröffentlichen, damit die Leute diese Informationen jetzt kennenlernen können.

Annahmen getroffen und wie man das Handbuch verwendet


Annahmen zu Kenntnissen der SQL-Sprache: Es wird davon ausgegangen, dass Sie über Grundkenntnisse in SQL verfügen. Sie verwenden es wahrscheinlich häufig bei der Arbeit, möchten aber Ihre Fähigkeiten in Themen wie Selbstassoziationen und Fensterfunktionen verbessern.

Verwendung dieses Handbuchs: Da in einem Interview häufig eine Tafel oder ein virtuelles Notizbuch verwendet wird (ohne den Code zu kompilieren), empfehle ich, Bleistift und Papier zu nehmen und Lösungen für jedes Problem aufzuschreiben und nach Abschluss Ihre Notizen mit den Antworten zu vergleichen. Oder erarbeiten Sie Ihre Antworten mit einem Freund, der als Interviewer fungiert!

  • Kleinere Syntaxfehler spielen bei einem Interview mit einem Whiteboard oder Notizblock keine große Rolle. Aber sie können den Interviewer ablenken, also versuchen Sie im Idealfall, ihre Anzahl zu reduzieren, um alle Aufmerksamkeit auf die Logik zu konzentrieren.
  • Die gegebenen Antworten sind nicht unbedingt die einzige Möglichkeit, jedes Problem zu lösen. Fühlen Sie sich frei, Kommentare mit zusätzlichen Lösungen zu schreiben, die Sie diesem Handbuch hinzufügen können!

Tipps zum Lösen komplexer Aufgaben in SQL-Interviews


Zunächst Standardtipps für alle Programmierinterviews ...

  1. Hören Sie sich die Beschreibung des Problems genau an und wiederholen Sie dem Interviewer die Essenz des Problems
  2. Formulieren Sie einen Grenzfall, um zu demonstrieren, dass Sie das Problem wirklich verstehen (d. H. Eine Zeile, die nicht in der endgültigen SQL-Abfrage enthalten ist, die Sie schreiben werden).
  3. ( ) , — : ,
    • , ,
  4. SQL, , . , .


Einige der hier aufgeführten Probleme stammen aus alten Periscope-Blogeinträgen (die größtenteils von Sean Cook um 2014 verfasst wurden, obwohl seine Urheberschaft nach der Fusion von SiSense mit Periscope aus den Materialien entfernt worden zu sein scheint ) sowie aus Diskussionen über StackOverflow. Falls erforderlich, werden die Quellen am Anfang jeder Frage markiert.

Auf dem Select Star SQL finden Sie auch eine gute Auswahl an Denksportaufgaben , ergänzende Ausgaben dieses Dokuments.

Bitte beachten Sie, dass diese Fragen keine wörtlichen Kopien von Fragen aus meinen eigenen Interviews sind und nicht in den Unternehmen verwendet wurden, in denen ich gearbeitet oder gearbeitet habe.

Selbstassoziationsaufgaben


Nr. 1. Prozentuale Änderung von Monat zu Monat


Kontext: Es ist oft hilfreich zu wissen, wie sich eine Schlüsselmetrik von Monat zu Monat ändert, z. B. die monatliche Zielgruppe aktiver Benutzer. Nehmen wir an, wir haben eine Tabelle loginsin dieser Form:

| user_id | Datum |
| --------- | ------------ |
| 1 | 2018-07-01 |
| 234 | 2018-07-02 |
| 3 | 2018-07-02 |
| 1 | 2018-07-02 |
| ... | ... |
| 234 | 2018-10-04 |

Ziel : Ermittlung der monatlichen prozentualen Änderung in der monatlichen Zielgruppe der aktiven Benutzer (MAU).

Lösung:
(Diese Lösung enthält wie die anderen Codeblöcke in diesem Dokument Kommentare zu SQL-Syntaxelementen, die sich zwischen verschiedenen SQL-Varianten unterscheiden können, und andere Hinweise.)

WITH mau AS 
(
  SELECT 
   /* 
    *       
    *  , . .   ,    . 
    *    ,   
    *
    *  Postgres  DATE_TRUNC(),   
    *      SQL   
    * . https://www.postgresql.org/docs/9.0/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
    */ 
    DATE_TRUNC('month', date) month_timestamp,
    COUNT(DISTINCT user_id) mau
  FROM 
    logins 
  GROUP BY 
    DATE_TRUNC('month', date)
  )
 
 SELECT 
    /*
    *    SELECT      . 
    * 
    *        ,   
    *    ,   , 
    *        ..
    */ 
    a.month_timestamp previous_month, 
    a.mau previous_mau, 
    b.month_timestamp current_month, 
    b.mau current_mau, 
    ROUND(100.0*(b.mau - a.mau)/a.mau,2) AS percent_change 
 FROM
    mau a 
 JOIN 
    /*
    *   `ON b.month_timestamp = a.month_timestamp + interval '1 month'` 
    */
    mau b ON a.month_timestamp = b.month_timestamp - interval '1 month' 

Nr. 2. Baumstruktur markieren


Kontext: Angenommen, Sie haben eine Tabelle treemit zwei Spalten: Die erste gibt die Knoten an und die zweite die übergeordneten Knoten.

Knoten übergeordnet
12
2 5
3 5
4 3
5 NULL 

Aufgabe: Schreiben Sie SQL so, dass wir jeden Knoten als inner, root oder leaf oder leaf bezeichnen, sodass wir für die obigen Werte Folgendes erhalten:

node    label  
1       Leaf
2       Inner
3       Inner
4       Leaf
5       Root

(Hinweis: Weitere Informationen zur Terminologie der baumartigen Datenstruktur finden Sie hier . Es ist jedoch nicht erforderlich, um dieses Problem zu lösen!)

Lösung:
Bestätigung: Fabian Hoffman schlug diese allgemeinere Lösung am 2. Mai 2020 vor. Danke Fabian

WITH join_table AS
(
    SELECT 
        cur.node, 
        cur.parent, 
        COUNT(next.node) AS num_children
    FROM 
        tree cur
    LEFT JOIN 
        tree next ON (next.parent = cur.node)
    GROUP BY 
        cur.node, 
        cur.parent
)

SELECT
    node,
    CASE
        WHEN parent IS NULL THEN "Root"
        WHEN num_children = 0 THEN "Leaf"
        ELSE "Inner"
    END AS label
FROM 
    join_table 

Alternative Lösung ohne explizite Zusammenhänge:

Danksagung: William Chardgin machte am 2. Mai 2020 auf die Notwendigkeit aufmerksam, WHERE parent IS NOT NULLdass diese Lösung Leafstattdessen zurückkehrt NULL. Danke William!

SELECT 
    node,
    CASE 
        WHEN parent IS NULL THEN 'Root'
        WHEN node NOT IN 
            (SELECT parent FROM tree WHERE parent IS NOT NULL) THEN 'Leaf'
        WHEN node IN (SELECT parent FROM tree) AND parent IS NOT NULL THEN 'Inner'
    END AS label 
 from 
    tree

Nr. 3. Benutzerbindung pro Monat (mehrere Teile)


Danksagung: Diese Aufgabe wurde vom SiSense Blog - Artikel angepasst, „Verwenden von Self-Verbänden zu berechnen Retention, Outflow und Reaktivierung .

Teil 1


Kontext: Angenommen, wir haben Statistiken zur Benutzerautorisierung auf einer Site in der Tabelle logins:

| user_id | Datum |
| --------- | ------------ |
| 1 | 2018-07-01 |
| 234 | 2018-07-02 |
| 3 | 2018-07-02 |
| 1 | 2018-07-02 |
| ... | ... |
| 234 | 2018-10-04 |

Aufgabe: Schreiben Sie eine Anfrage, die die Anzahl der beibehaltenen Benutzer pro Monat erhält. In unserem Fall ist dieser Parameter definiert als die Anzahl der Benutzer, die sich in diesem und im vorherigen Monat beim System angemeldet haben.

Entscheidung:

SELECT 
    DATE_TRUNC('month', a.date) month_timestamp, 
    COUNT(DISTINCT a.user_id) retained_users 
 FROM 
    logins a 
 JOIN 
    logins b ON a.user_id = b.user_id 
        AND DATE_TRUNC('month', a.date) = DATE_TRUNC('month', b.date) + 
                                             interval '1 month'
 GROUP BY 
    date_trunc('month', a.date)

Bestätigung:
Tom Moertel wies darauf hin, dass das Vor-Duplizieren von user_id vor dem Selbstbeitritt die Lösung effizienter macht, und schlug den folgenden Code vor. Danke Tom!

Alternative Lösung:

WITH DistinctMonthlyUsers AS (
  /*
  *     ** , 
  *  
  */
    SELECT DISTINCT
      DATE_TRUNC('MONTH', a.date) AS month_timestamp,
      user_id
    FROM logins
  )

SELECT
  CurrentMonth.month_timestamp month_timestamp,
  COUNT(PriorMonth.user_id) AS retained_user_count
FROM 
    DistinctMonthlyUsers AS CurrentMonth
LEFT JOIN 
    DistinctMonthlyUsers AS PriorMonth
  ON
    CurrentMonth.month_timestamp = PriorMonth.month_timestamp + INTERVAL '1 MONTH'
    AND 
    CurrentMonth.user_id = PriorMonth.user_id

Teil 2


Aufgabe: Jetzt übernehmen wir die vorherige Aufgabe, die Anzahl der zurückbehaltenen Benutzer pro Monat zu berechnen - und stellen sie auf den Kopf. Wir werden eine Anfrage schreiben, um Benutzer zu zählen , die diesen Monat nicht auf die Website zurückgekehrt sind. Das heißt, "verlorene" Benutzer.

Entscheidung:

SELECT 
    DATE_TRUNC('month', a.date) month_timestamp, 
    COUNT(DISTINCT b.user_id) churned_users 
FROM 
    logins a 
FULL OUTER JOIN 
    logins b ON a.user_id = b.user_id 
        AND DATE_TRUNC('month', a.date) = DATE_TRUNC('month', b.date) + 
                                         interval '1 month'
WHERE 
    a.user_id IS NULL 
GROUP BY 
    DATE_TRUNC('month', a.date)

Bitte beachten Sie, dass dieses Problem auch mit LEFToder behoben werden kann RIGHT.

Teil 3


Hinweis: Dies ist wahrscheinlich eine schwierigere Aufgabe, als Ihnen bei einem echten Interview angeboten wird. Stellen Sie sich das eher wie ein Puzzle vor - oder Sie können überspringen und mit der nächsten Aufgabe fortfahren.

Kontext : Wir haben also zwei frühere Probleme gut gelöst. Unter den Bedingungen der neuen Aufgabe haben wir jetzt eine Tabelle mit verlorenen Benutzern user_churns. Wenn der Benutzer im letzten Monat aktiv war, dann aber nicht aktiv war, wird er in die Tabelle für diesen Monat eingetragen. So sieht es aus user_churns:

| user_id | month_date |
| --------- | ------------ |
| 1 | 2018-05-01 |
| 234 | 2018-05-01 |
| 3 | 2018-05-01 |
| 12 | 2018-05-01 |
| ... | ... |
| 234 | 2018-10-01 |

Aufgabe : Jetzt möchten Sie eine Kohortenanalyse durchführen, dh eine Analyse der Gesamtheit der aktiven Benutzer, die in der Vergangenheit reaktiviert wurden . Erstellen Sie mit diesen Benutzern eine Tabelle. Sie können Tabellen verwenden user_churnsund eine Kohorte erstellen logins. In Postgres ist der aktuelle Zeitstempel über zugänglich current_timestamp.

Entscheidung:

WITH user_login_data AS 
(
    SELECT 
        DATE_TRUNC('month', a.date) month_timestamp,
        a.user_id,
        /* 
        *   ,    SQL,   , 
        *      SELECT   HAVING.
        *       .  
        */ 
        MAX(b.month_date) as most_recent_churn, 
        MAX(DATE_TRUNC('month', c.date)) as most_recent_active 
     FROM 
        logins a
     JOIN 
        user_churns b 
            ON a.user_id = b.user_id AND DATE_TRUNC('month', a.date) > b.month_date 
     JOIN
        logins c 
            ON a.user_id = c.user_id 
            AND 
            DATE_TRUNC('month', a.date) > DATE_TRUNC('month', c.date)
     WHERE 
        DATE_TRUNC('month', a.date) = DATE_TRUNC('month', current_timestamp)
     GROUP BY 
        DATE_TRUNC('month', a.date),
        a.user_id
     HAVING 
        most_recent_churn > most_recent_active

Nr. 4. Zunehmende Summe


Bestätigung: Diese Aufgabe wurde aus dem SiSense-Blogartikel Cash Flow Modeling in SQL übernommen .

Kontext: Angenommen, wir haben eine Tabelle transactionsin dieser Form:

| Datum | cash_flow |
| ------------ | ----------- |
| 2018-01-01 | -1000 |
| 2018-01-02 | -100 |
| 2018-01-03 | 50 |
| ... | ... |

Wo cash_flowist der Umsatz abzüglich der Kosten für jeden Tag.

Ziel: Schreiben Sie eine Anfrage, um jeden Tag eine laufende Summe für den Cashflow zu erhalten, so dass Sie am Ende eine Tabelle in dieser Form erhalten:

| Datum | cumulative_cf |
| ------------ | --------------- |
| 2018-01-01 | -1000 |
| 2018-01-02 | -1100 |
| 2018-01-03 | -1050 |
| ... | ... |

Entscheidung:

SELECT 
    a.date date, 
    SUM(b.cash_flow) as cumulative_cf 
FROM
    transactions a
JOIN b 
    transactions b ON a.date >= b.date 
GROUP BY 
    a.date 
ORDER BY 
    date ASC

Eine alternative Lösung mit einer Fensterfunktion (effizienter!):

SELECT 
    date, 
    SUM(cash_flow) OVER (ORDER BY date ASC) as cumulative_cf 
FROM
    transactions 
ORDER BY 
    date ASC

Nr. 5. Gleitender Durchschnitt


Bestätigung: Diese Aufgabe wurde aus dem SiSense-Blogartikel Moving Averages in MySQL und SQL Server übernommen .

Hinweis: Der gleitende Durchschnitt kann auf verschiedene Arten berechnet werden. Hier verwenden wir den vorherigen Durchschnitt. Somit ist die Metrik für den siebten Tag des Monats der Durchschnitt der vorherigen sechs Tage und für sich.

Kontext : Angenommen, wir haben eine Tabelle signupsin dieser Form:

| Datum | sign_ups |
| ------------ | ---------- |
| 2018-01-01 | 10 |
| 2018-01-02 | 20 |
| 2018-01-03 | 50 |
| ... | ... |
| 2018-10-01 | 35 |

Aufgabe : Schreiben Sie eine Anfrage, um einen gleitenden 7-Tage-Durchschnitt der täglichen Registrierungen zu erhalten.

Entscheidung:

SELECT 
  a.date, 
  AVG(b.sign_ups) average_sign_ups 
FROM 
  signups a 
JOIN 
  signups b ON a.date <= b.date + interval '6 days' AND a.date >= b.date
GROUP BY 
  a.date

Nr. 6. Mehrere Verbindungsbedingungen


Bestätigung: Diese Aufgabe wurde aus dem SiSense-Blogartikel „Analysieren Ihrer E-Mail mit SQL“ übernommen .

Kontext: Nehmen wir an, unsere Tabelle emailsenthält E-Mails, die von der Adresse gesendet zach@g.comund dort empfangen wurden:

| id | Betreff | von | zu | Zeitstempel |
| ---- | ---------- | -------------- | -------------- | --- ------------------ |
| 1 | Yosemite | zach@g.com | thomas@g.com | 2018-01-02 12:45:03 |
| 2 | Big Sur | sarah@g.com | thomas@g.com | 2018-01-02 16:30:01 |
| 3 | Yosemite | thomas@g.com | zach@g.com | 2018-01-02 16:35:04 |
| 4 | Laufen | jill@g.com | zach@g.com | 2018-01-03 08:12:45 |
| 5 | Yosemite | zach@g.com | thomas@g.com | 2018-01-03 14:02:01 |
| 6 | Yosemite | thomas@g.com | zach@g.com | 2018-01-03 15:01:05 |
| .. | .. | .. | .. | .. |

Aufgabe: Schreiben Sie eine Anfrage, um die Antwortzeit für jeden an (( id) gesendeten Brief ( ) zu erhalten zach@g.com. Fügen Sie keine Briefe an andere Adressen hinzu. Angenommen, jeder Thread hat ein eindeutiges Thema. Beachten Sie, dass der Thread möglicherweise mehrere Roundtrip-Buchstaben zwischen zach@g.comund anderen Empfängern enthält.

Entscheidung:

SELECT 
    a.id, 
    MIN(b.timestamp) - a.timestamp as time_to_respond 
FROM 
    emails a 
JOIN
    emails b 
        ON 
            b.subject = a.subject 
        AND 
            a.to = b.from
        AND 
            a.from = b.to 
        AND 
            a.timestamp < b.timestamp 
 WHERE 
    a.to = 'zach@g.com' 
 GROUP BY 
    a.id 

Aufgaben für Fensterfunktionen


Nr. 1. Suchen Sie die Kennung mit dem Maximalwert


Kontext: Angenommen, wir haben eine Tabelle salariesmit Daten zu Abteilungen und Mitarbeitergehältern im folgenden Format:

  depname | empno | Gehalt |     
----------- + ------- + -------- +
 entwickeln | 11 | 5200 |
 entwickeln | 7 | 4200 |
 entwickeln | 9 | 4500 |
 entwickeln | 8 | 6000 |
 entwickeln | 10 | 5200 |
 Personal | 5 | 3500 |
 Personal | 2 | 3900 |
 Vertrieb | 3 | 4800 |
 Vertrieb | 1 | 5000 |
 Vertrieb | 4 | 4800 |

Aufgabe : Schreiben Sie eine Anfrage, um empnodas höchste Gehalt zu erhalten. Stellen Sie sicher, dass Ihre Lösung Fälle mit gleichen Gehältern behandelt!

Entscheidung:

WITH max_salary AS (
    SELECT 
        MAX(salary) max_salary
    FROM 
        salaries
    )
SELECT 
    s.empno
FROM 
    salaries s
JOIN 
    max_salary ms ON s.salary = ms.max_salary

Alternative Lösung mit RANK():

WITH sal_rank AS 
  (SELECT 
    empno, 
    RANK() OVER(ORDER BY salary DESC) rnk
  FROM 
    salaries)
SELECT 
  empno
FROM
  sal_rank
WHERE 
  rnk = 1;

Nr. 2. Mittelwert und Rangfolge mit einer Fensterfunktion (mehrere Teile)


Teil 1


Kontext : Angenommen, wir haben eine Tabelle salariesin diesem Format:

  depname | empno | Gehalt |     
----------- + ------- + -------- +
 entwickeln | 11 | 5200 |
 entwickeln | 7 | 4200 |
 entwickeln | 9 | 4500 |
 entwickeln | 8 | 6000 |
 entwickeln | 10 | 5200 |
 Personal | 5 | 3500 |
 Personal | 2 | 3900 |
 Vertrieb | 3 | 4800 |
 Vertrieb | 1 | 5000 |
 Vertrieb | 4 | 4800 |

Aufgabe: Schreiben Sie eine Abfrage, die dieselbe Tabelle zurückgibt, jedoch mit einer neuen Spalte, in der das durchschnittliche Gehalt für die Abteilung angezeigt wird. Wir würden einen Tisch wie diesen erwarten:

  depname | empno | Gehalt | avg_salary |     
----------- + ------- + -------- + ------------ +
 entwickeln | 11 | 5200 | 5020 |
 entwickeln | 7 | 4200 | 5020 |
 entwickeln | 9 | 4500 | 5020 |
 entwickeln | 8 | 6000 | 5020 |
 entwickeln | 10 | 5200 | 5020 |
 Personal | 5 | 3500 | 3700 |
 Personal | 2 | 3900 | 3700 |
 Vertrieb | 3 | 4800 | 4867 |
 Vertrieb | 1 | 5000 | 4867 |
 Vertrieb | 4 | 4800 | 4867 |

Entscheidung:

SELECT 
    *, 
    /*
    * AVG() is a Postgres command, but other SQL flavors like BigQuery use 
    * AVERAGE()
    */ 
    ROUND(AVG(salary),0) OVER (PARTITION BY depname) avg_salary
FROM
    salaries

Teil 2


Aufgabe: Schreiben Sie eine Abfrage, die eine Spalte mit der Position jedes Mitarbeiters im Arbeitszeitblatt basierend auf seinem Gehalt in seiner Abteilung hinzufügt, wobei der Mitarbeiter mit dem höchsten Gehalt Position 1 erhält. Wir würden eine Tabelle in dieser Form erwarten:

  depname | empno | Gehalt | Gehalt_Rang |     
----------- + ------- + -------- + ------------- +
 entwickeln | 11 | 5200 | 2 |
 entwickeln | 7 | 4200 | 5 |
 entwickeln | 9 | 4500 | 4 |
 entwickeln | 8 | 6000 | 1 |
 entwickeln | 10 | 5200 | 2 |
 Personal | 5 | 3500 | 2 |
 Personal | 2 | 3900 | 1 |
 Vertrieb | 3 | 4800 | 2 |
 Vertrieb | 1 | 5000 | 1 |
 Vertrieb | 4 | 4800 | 2 |

Entscheidung:

SELECT 
    *, 
    RANK() OVER(PARTITION BY depname ORDER BY salary DESC) salary_rank
 FROM  
    salaries 

Andere Aufgaben mittlerer und hoher Schwierigkeit


Nr. 1. Histogramme


Kontext: Nehmen wir an, wir haben eine Tabelle, sessionsin der jede Zeile eine Video-Streaming-Sitzung mit einer Länge in Sekunden darstellt:

| session_id | length_seconds |
| ------------ | ---------------- |
| 1 | 23 |
| 2 | 453 |
| 3 | 27 |
| .. | .. |

Aufgabe: Schreiben Sie eine Abfrage, um die Anzahl der Sitzungen zu berechnen, die in Intervallen von fünf Sekunden abfallen, d. H. Für das obige Fragment lautet das Ergebnis ungefähr so:

| Eimer | count |
| --------- | ------- |
| 20-25 | 2 |
| 450-455 | 1 |

Die maximale Punktzahl zählt für die richtigen Zeilenbeschriftungen ("5-10" usw.).

Lösung:

WITH bin_label AS 
(SELECT 
    session_id, 
    FLOOR(length_seconds/5) as bin_label 
 FROM
    sessions 
 )
 SELECT 
    CONCATENTATE(STR(bin_label*5), '-', STR(bin_label*5+5)) bucket, 
    COUNT(DISTINCT session_id) count 
 GROUP BY 
    bin_label
 ORDER BY 
    bin_label ASC 

Nr. 2. Querverbindung (mehrere Teile)


Teil 1


Kontext: Nehmen wir an, wir haben eine Tabelle, state_streamsin der der Name des Staates und die Gesamtzahl der Stunden für das Streaming vom Videohosting in jeder Zeile angegeben sind:

| Zustand | total_streams |
| ------- | --------------- |
| NC | 34569 |
| SC | 33999 |
| CA | 98324 |
| MA | 19345 |
| .. | .. |

(Tatsächlich haben aggregierte Tabellen dieses Typs normalerweise eine Datumsspalte, die wir jedoch für diese Aufgabe ausschließen.)

Aufgabe: Schreiben Sie eine Abfrage, um Statuspaare mit einer Gesamtzahl von Threads innerhalb von tausend voneinander abzurufen. Für das obige Snippet möchten wir etwas sehen wie:

| state_a | state_b |
| --------- | --------- |
| NC | SC |
| SC | NC |

Entscheidung:

SELECT
    a.state as state_a, 
    b.state as state_b 
 FROM   
    state_streams a
 CROSS JOIN 
    state_streams b 
 WHERE 
    ABS(a.total_streams - b.total_streams) < 1000
    AND 
    a.state <> b.state 

Zur Information können Cross-Joins auch ohne explizite Angabe von Joins geschrieben werden:

SELECT
    a.state as state_a, 
    b.state as state_b 
 FROM   
    state_streams a, state_streams b 
 WHERE 
    ABS(a.total_streams - b.total_streams) < 1000
    AND 
    a.state <> b.state 

Teil 2


Hinweis: Dies ist eher eine Bonusfrage als eine wirklich wichtige SQL-Vorlage. Sie können es überspringen!

Aufgabe: Wie kann ich SQL aus einer früheren Lösung ändern, um Duplikate zu entfernen? Zum Beispiel das Beispiel des gleichen Tisches, um zu dämpfen NCund SCes gab nur ein Mal, nicht zwei.

Entscheidung:

SELECT
    a.state as state_a, 
    b.state as state_b 
 FROM   
    state_streams a, state_streams b 
 WHERE 
    ABS(a.total_streams - b.total_streams) < 1000
    AND 
    a.state > b.state 

Nr. 3. Erweiterte Berechnungen


Bestätigung: Diese Aufgabe wurde aus einer Diskussion über eine Frage übernommen, die ich in StackOverflow gestellt habe (mein Spitzname ist zthomas.nc).

Hinweis: Dies ist wahrscheinlich eine schwierigere Aufgabe, als Ihnen bei einem echten Interview angeboten wird. Stellen Sie es sich eher wie ein Puzzle vor - oder Sie können es überspringen!

Kontext: Angenommen, wir haben eine Tabelle tabledieser Art, in der userverschiedene Werte einer Klasse demselben Benutzer entsprechen können class:

| Benutzer | Klasse |
| ------ | ------- |
| 1 | a |
| 1 | b |
| 1 | b |
| 2 | b |
| 3 | a |

Problem: Angenommen, es gibt nur zwei mögliche Werte für eine Klasse. Schreiben Sie eine Abfrage, um die Anzahl der Benutzer in jeder Klasse zu berechnen. In diesem Fall können Benutzer mit beiden Labels aund bmuss auf die Klasse verweisen b.

Für unsere Stichprobe erhalten wir folgendes Ergebnis:

| Klasse | count |
| ------- | ------- |
| a | 1 |
| b | 2 |

Entscheidung:

WITH usr_b_sum AS 
(
    SELECT 
        user, 
        SUM(CASE WHEN class = 'b' THEN 1 ELSE 0 END) num_b
    FROM 
        table
    GROUP BY 
        user
), 

usr_class_label AS 
(
    SELECT 
        user, 
        CASE WHEN num_b > 0 THEN 'b' ELSE 'a' END class 
    FROM 
        usr_b_sum
)

SELECT 
    class, 
    COUNT(DISTINCT user) count 
FROM
    usr_class_label
GROUP BY 
    class 
ORDER BY 
    class ASC

Eine alternative Lösung verwendet Anweisungen SELECTin den Bedienern SELECTund UNION:

SELECT 
    "a" class,
    COUNT(DISTINCT user_id) - 
        (SELECT COUNT(DISTINCT user_id) FROM table WHERE class = 'b') count 
UNION
SELECT 
    "b" class,
    (SELECT COUNT(DISTINCT user_id) FROM table WHERE class = 'b') count 

All Articles