Meilleures questions d'entrevue de difficulté moyenne SQL

Les premiers 70% du cours SQL semblent assez simples. Les difficultés commencent sur les 30% restants.

De 2015 à 2019, j'ai effectué quatre entretiens pour les postes d'analyste de données et de spécialiste en analyse de données dans plus d'une douzaine d'entreprises. Après une autre entrevue infructueuse en 2017 - lorsque je me suis trompé sur des questions SQL complexes - j'ai commencé à compiler un livre de tâches avec des questions SQL de complexité moyenne et élevée afin de mieux préparer les entretiens. Ce guide a été utile lors de la dernière série d'entretiens en 2019. Au cours de la dernière année, j'ai partagé ce guide avec quelques amis, et grâce au temps libre supplémentaire dû à une pandémie, je l'ai peaufiné et j'ai compilé ce document.

Il existe de nombreux didacticiels SQL pour les débutants. Mes favoris sontCours interactifs SQL et Select Star SQL de Codecademy par Zi Chung Kao. Mais en réalité, les premiers 70% du cours SQL sont assez simples, et les vraies difficultés commencent dans les 30% restants, qui ne sont pas couverts dans les guides du débutant. Ainsi, lors des entretiens avec les analystes de données et les spécialistes de l'analyse de données dans les entreprises technologiques, ils posent souvent des questions sur ces 30%.

Étonnamment, je n'ai pas trouvé de source exhaustive sur ces questions de difficulté moyenne, j'ai donc compilé ce guide.

Il est utile pour les entretiens, mais en même temps, il augmentera votre efficacité dans vos emplois actuels et futurs. Personnellement, je pense que certains des modèles SQL mentionnés sont également utiles pour les systèmes ETL qui exécutent des outils de reporting et des fonctions d'analyse de données pour identifier les tendances.

Contenu



Vous devez comprendre que lors des entretiens avec les analystes de données et les analystes de données, ils posent des questions non seulement sur SQL. D'autres sujets communs incluent la discussion de projets antérieurs, les tests A / B, le développement de métriques et les problèmes analytiques ouverts. Il y a environ trois ans, Quora a publié des conseils sur les entretiens pour un poste d'analyste produit sur Facebook. Là, ce sujet est discuté plus en détail. Cependant, si l'amélioration de vos connaissances de SQL vous aidera dans votre entretien, ce guide en vaut la peine.

À l'avenir, je pourrai porter le code de ce guide vers un site comme Select Star SQLpour faciliter l'écriture d'instructions SQL et voir le résultat de l'exécution du code en temps réel. En option, ajoutez des questions en tant que problèmes à la plate-forme pour préparer les entretiens LeetCode . En attendant, je voulais juste publier ce document pour que les gens puissent désormais se familiariser avec ces informations.

Hypothèses formulées et comment utiliser le manuel


Hypothèses sur la connaissance du langage SQL: On suppose que vous avez une connaissance pratique de SQL. Vous l'utilisez probablement souvent au travail, mais souhaitez perfectionner vos compétences dans des sujets tels que les auto-associations et les fonctions de fenêtre.

Comment utiliser ce manuel: Puisqu'un tableau ou un cahier virtuel est souvent utilisé dans une interview (sans compiler le code), je recommande de prendre un crayon et du papier et d'écrire des solutions pour chaque problème, et après avoir terminé, comparez vos notes avec les réponses. Ou élaborez vos réponses avec un ami qui fera office d'intervieweur!

  • Les erreurs de syntaxe mineures importent peu lors d'un entretien avec un tableau blanc ou un bloc-notes. Mais ils peuvent distraire l'intervieweur, essayez donc idĂ©alement de rĂ©duire leur nombre afin de concentrer toute votre attention sur la logique.
  • Les rĂ©ponses donnĂ©es ne sont pas nĂ©cessairement le seul moyen de rĂ©soudre chaque problème. N'hĂ©sitez pas Ă  Ă©crire des commentaires avec des solutions supplĂ©mentaires que vous pouvez ajouter Ă  ce guide!

Conseils pour résoudre des tâches complexes dans les entretiens SQL


Tout d'abord, des conseils standard pour toutes les interviews de programmation ...

  1. Écoutez attentivement la description du problème, répétez l'essence du problème à l'intervieweur
  2. Formulez un cas limite pour démontrer que vous comprenez vraiment le problème (c'est-à-dire une ligne qui ne sera pas incluse dans la requête SQL finale que vous allez écrire)
  3. ( ) , — : ,
    • , ,
  4. SQL, , . , .


Certains des problèmes répertoriés ici sont adaptés d'anciennes entrées du blog Periscope (principalement écrites par Sean Cook vers 2014, bien que sa paternité semble avoir été retirée des documents après la fusion de SiSense avec Periscope ), ainsi que des discussions sur StackOverflow. Si nécessaire, les sources sont marquées au début de chaque question.

On the Select Star SQL est également une bonne sélection de casse - tête , numéros complémentaires de ce document.

Veuillez noter que ces questions ne sont pas des copies littérales des questions de mes propres entretiens et qu'elles n'ont pas été utilisées dans les entreprises dans lesquelles j'ai travaillé ou travaille.

Tâches d'auto-association


No. 1. Variation en pourcentage d'un mois Ă  l'autre


Contexte: il est souvent utile de savoir comment une métrique clé change, par exemple, l'audience mensuelle des utilisateurs actifs, d'un mois à l'autre. Disons que nous avons un tableau loginssous cette forme:

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

Objectif : trouver la variation mensuelle en pourcentage de l'audience mensuelle des utilisateurs actifs (MAU).

Solution:
(Cette solution, comme les autres blocs de code de ce document, contient des commentaires sur les éléments de syntaxe SQL qui peuvent différer entre les différentes variantes SQL et d'autres notes)

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' 

No. 2. Structure de l'arbre de marquage


Contexte: supposons que vous ayez une table treeavec deux colonnes: la première indique les nœuds et la seconde les nœuds parents.

parent de nœud
12
2 5
3 5
4 3
5 NUL 

La tâche: écrire du SQL de manière à désigner chaque nœud comme interne (racine), racine (racine) ou nœud final / feuille (feuille), de sorte que pour les valeurs ci-dessus, vous obtenez les éléments suivants:

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

(Remarque: plus d'informations sur la terminologie de la structure de données arborescente peuvent être trouvées ici . Cependant, ce n'est pas nécessaire pour résoudre ce problème!)

Solution:
Remerciements: Fabian Hoffman a proposé cette solution plus généralisée le 2 mai 2020. Merci 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 

Solution alternative, sans connexions explicites:

Remerciements: William Chardgin le 2 mai 2020 a attiré l'attention sur la nécessité de la condition WHERE parent IS NOT NULLque cette solution revienne à la Leafplace NULL. Merci 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

No. 3. RĂ©tention des utilisateurs par mois (plusieurs parties)


Remerciements: Cette tâche a été adaptée de l'article de blog SiSense, «Utilisation des auto-associations pour calculer la rétention, le flux sortant et la réactivation » .

Partie 1


Contexte: supposons que nous ayons des statistiques sur l'autorisation des utilisateurs sur un site dans le tableau logins:

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

Tâche: rédiger une demande qui reçoit le nombre d'utilisateurs retenus par mois. Dans notre cas, ce paramètre est défini comme le nombre d'utilisateurs qui se sont connectés au système au cours de ce mois et du mois précédent.

DĂ©cision:

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)

Remerciements:
Tom Moertel a souligné que la pré-duplication de user_id avant l'auto-jonction rend la solution plus efficace et a suggéré le code ci-dessous. Merci Tom!

Solution alternative:

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

Partie 2


Tâche: nous prenons maintenant la tâche précédente de calculer le nombre d'utilisateurs retenus par mois - et la retournons. Nous rédigerons une demande pour compter les utilisateurs qui ne sont pas revenus sur le site ce mois-ci. Autrement dit, les utilisateurs "perdus".

DĂ©cision:

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)

Veuillez noter que ce problème peut également être résolu avec LEFTou RIGHT.

3e partie


Remarque: il s'agit probablement d'une tâche plus difficile que celle qui vous sera proposée lors d'un véritable entretien. Pensez-y plutôt comme un puzzle - ou vous pouvez sauter et passer à la tâche suivante.

Contexte : nous avons donc fait du bon travail avec deux problèmes précédents. Selon les termes de la nouvelle tâche, nous avons maintenant un tableau des utilisateurs perdus user_churns. Si l'utilisateur était actif au cours du dernier mois, mais non actif dans ce cas, il est alors inscrit dans le tableau de ce mois. Voici à quoi ça ressemble user_churns:

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

Tâche : vous voulez maintenant effectuer une analyse de cohorte, c'est-à-dire une analyse de la totalité des utilisateurs actifs qui ont été réactivés dans le passé . Créez une table avec ces utilisateurs. Vous pouvez utiliser des tableaux user_churnset créer une cohorte logins. Dans Postgres, l'horodatage actuel est accessible via current_timestamp.

DĂ©cision:

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

No. 4. Augmentation du total


Remerciements: cette tâche a été adaptée de l'article de blog SiSense , Cash Flow Modeling in SQL .

Contexte: supposons que nous ayons un tableau transactionssous cette forme:

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

Où cash_flowest le revenu moins les coûts de chaque jour.

Objectif: rédiger une demande pour obtenir un total cumulé des flux de trésorerie chaque jour de telle sorte qu'au final, vous obtenez un tableau sous cette forme:

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

DĂ©cision:

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

Une solution alternative utilisant une fonction fenĂŞtre (plus efficace!):

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

No. 5. Moyenne mobile


Remerciements: cette tâche est adaptée de l'article de blog SiSense , Moving Averages in MySQL and SQL Server .

Remarque: la moyenne mobile peut être calculée de différentes manières. Ici, nous utilisons la moyenne précédente. Ainsi, la métrique du septième jour du mois sera la moyenne des six jours précédents et de lui-même.

Contexte : supposons que nous ayons un tableau signupssous cette forme:

| date | inscriptions |
| ------------ | ---------- |
| 01-01-2018 | 10 |
| 2018-01-02 | 20 |
| 2018-01-03 | 50 |
| ... | ... |
| 01/10/2018 | 35 |

Tâche : rédiger une demande pour obtenir une moyenne mobile sur 7 jours des inscriptions quotidiennes.

DĂ©cision:

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

No. 6. Plusieurs conditions de connexion


Remerciements: cette tâche est adaptée de l'article de blog SiSense, «Analyse de votre messagerie à l'aide de SQL» .

Contexte: disons que notre tableau emailscontient des emails envoyés depuis l'adresse zach@g.comet reçus dessus:

| id | sujet | de | Ă  | horodatage |
| ---- | ---------- | -------------- | -------------- | --- ------------------ |
| 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 | Running | 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 |
| .. | .. | .. | .. | .. |

Tâche: rédigez une demande pour obtenir le temps de réponse pour chaque lettre ( id) envoyée à zach@g.com. N'incluez pas de lettres à d'autres adresses. Supposons que chaque fil ait un thème unique. Gardez à l'esprit que le fil peut avoir plusieurs lettres aller-retour entre zach@g.comet d'autres destinataires.

DĂ©cision:

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 

Tâches pour les fonctions de fenêtre


Non. 1. Trouvez l'identifiant avec la valeur maximale


Contexte: Supposons que nous ayons un tableau salariesavec des données sur les départements et les salaires des employés dans le format suivant:

  depname | empno | salaire |     
----------- + ------- + -------- +
 développer | 11 | 5200 |
 développer | 7 | 4200 |
 développer | 9 | 4500 |
 développer | 8 | 6000 |
 développer | 10 | 5200 |
 personnel | 5 | 3500 |
 personnel | 2 | 3900 |
 ventes | 3 | 4800 |
 ventes | 1 | 5000 |
 ventes | 4 | 4800 |

Tâche : rédiger une demande pour obtenir empnole salaire le plus élevé. Assurez-vous que votre solution gère les cas d'égalité de salaire!

DĂ©cision:

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

Solution alternative utilisant RANK():

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

No. 2. Valeur moyenne et classement avec une fonction de fenĂŞtre (plusieurs parties)


Partie 1


Contexte : supposons que nous ayons une table salariesdans ce format:

  depname | empno | salaire |     
----------- + ------- + -------- +
 développer | 11 | 5200 |
 développer | 7 | 4200 |
 développer | 9 | 4500 |
 développer | 8 | 6000 |
 développer | 10 | 5200 |
 personnel | 5 | 3500 |
 personnel | 2 | 3900 |
 ventes | 3 | 4800 |
 ventes | 1 | 5000 |
 ventes | 4 | 4800 |

Tâche: écrire une requête qui renvoie le même tableau, mais avec une nouvelle colonne qui montre le salaire moyen pour le département. Nous nous attendrions à une table comme celle-ci:

  depname | empno | salaire | avg_salary |     
----------- + ------- + -------- + ------------ +
 développer | 11 | 5200 | 5020 |
 développer | 7 | 4200 | 5020 |
 développer | 9 | 4500 | 5020 |
 développer | 8 | 6000 | 5020 |
 développer | 10 | 5200 | 5020 |
 personnel | 5 | 3500 | 3700 |
 personnel | 2 | 3900 | 3700 |
 ventes | 3 | 4800 | 4867 |
 ventes | 1 | 5000 | 4867 |
 ventes | 4 | 4800 | 4867 |

DĂ©cision:

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

Partie 2


Tâche: écrire une requête qui ajoute une colonne avec la position de chaque employé dans la feuille de temps en fonction de son salaire dans son département, où l'employé avec le salaire le plus élevé obtient la position 1. Nous nous attendrions à une table sous cette forme:

  depname | empno | salaire | salaire_rank |     
----------- + ------- + -------- + ------------- +
 développer | 11 | 5200 | 2 |
 développer | 7 | 4200 | 5 |
 développer | 9 | 4500 | 4 |
 développer | 8 | 6000 | 1 |
 développer | 10 | 5200 | 2 |
 personnel | 5 | 3500 | 2 |
 personnel | 2 | 3900 | 1 |
 ventes | 3 | 4800 | 2 |
 ventes | 1 | 5000 | 1 |
 ventes | 4 | 4800 | 2 |

DĂ©cision:

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

Autres tâches de difficulté moyenne et élevée


N ° 1. Histogrammes


Contexte: Disons que nous avons un tableau sessionsoù chaque ligne représente une session de streaming vidéo d'une durée en secondes:

| session_id | durée_secondes |
| ------------ | ---------------- |
| 1 | 23 |
| 2 | 453 |
| 3 | 27 |
| .. | .. |

Tâche: écrivez une requête pour calculer le nombre de sessions qui tombent à des intervalles de cinq secondes, c'est-à-dire pour le fragment ci-dessus, le résultat sera quelque chose comme ceci:

| seau | compter |
| --------- | ------- |
| 20-25 | 2 |
| 450-455 | 1 |

Le score maximum compte pour les étiquettes de ligne appropriées ("5-10", etc.)

Solution:

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 

No. 2. Connexion croisée (plusieurs parties)


Partie 1


Contexte: disons que nous avons un tableau state_streamsoù le nom de l'État et le nombre total d'heures de streaming depuis l'hébergement vidéo sont indiqués sur chaque ligne:

| Ă©tat | total_streams |
| ------- | --------------- |
| NC | 34569 |
| SC | 33999 |
| CA | 98324 |
| MA | 19345 |
| .. | .. |

(En fait, les tables agrégées de ce type ont généralement une colonne de date, mais nous l'exclurons pour cette tâche)

Tâche: écrire une requête pour obtenir des paires d'états avec un nombre total de threads dans un millier les uns des autres. Pour l'extrait ci-dessus, nous aimerions voir quelque chose comme:

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

DĂ©cision:

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 

Pour information, les jointures croisées peuvent également être écrites sans spécifier explicitement les jointures:

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 

Partie 2


Remarque: il s'agit d'une question bonus plutôt que d'un modèle SQL vraiment important. Vous pouvez le sauter!

Tâche: comment puis-je modifier SQL d'une solution précédente pour supprimer les doublons? Par exemple, l'exemple de la même table, à vapeur NCet SCil n'y avait qu'une seule fois, pas deux.

DĂ©cision:

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 

No. 3. Calculs avancés


Remerciements: Cette tâche a été adaptée d'une discussion sur une question que j'ai posée sur StackOverflow (mon surnom est zthomas.nc).

Remarque: il s'agit probablement d'une tâche plus difficile que celle qui vous sera proposée lors d'un véritable entretien. Pensez-y plutôt comme un puzzle - ou vous pouvez le sauter!

Contexte: supposons que nous ayons une table de tablece type, où userdifférentes valeurs d'une classe peuvent correspondre au même utilisateur class:

| utilisateur | classe |
| ------ | ------- |
| 1 | a |
| 1 | b |
| 1 | b |
| 2 | b |
| 3 | a |

Problème: supposons qu'il n'y a que deux valeurs possibles pour une classe. Écrivez une requête pour calculer le nombre d'utilisateurs dans chaque classe. Dans ce cas, les utilisateurs avec les étiquettes aet bdoivent se référer à la classe b.

Pour notre échantillon, nous obtenons le résultat suivant:

| classe | compter |
| ------- | ------- |
| a | 1 |
| b | 2 |

DĂ©cision:

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

Une solution alternative utilise des instructions SELECTdans les opérateurs SELECTet 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