web-dev-qa-db-fra.com

MySQL - Sélectionnez dans une liste de numéros ceux sans contrepartie dans le champ id d'une table

J'ai une liste de chiffres, disons {2,4,5,6,7} J'ai une table, foos, avec foos.ID, y compris disons {1,2,3,4,8,9}

J'aimerais prendre ma liste de numéros et trouver ceux sans homologue dans le champ ID de ma table.

Une façon d'y parvenir serait de créer un tableau de barres, chargé avec {2,4,5,6,7} dans le champ ID. Ensuite, je ferais

 SÉLECTIONNER les barres. * À PARTIR DES BARRES GAUCHE JOINDRE foos SUR bars.ID = foos.ID OERE foos.ID IS NULL 

Cependant, je voudrais accomplir cette table sans temp.

Quelqu'un a-t-il une opinion sur la façon dont cela pourrait se produire?

36
SocialCensus

C'est un problème assez courant: générer une relation à la volée sans créer de table. Les solutions SQL pour ce problème sont assez gênantes. Un exemple utilisant une table dérivée:

SELECT n.id
FROM
  (SELECT 2 AS id 
   UNION SELECT 3 
   UNION SELECT 4 
   UNION SELECT 5 
   UNION SELECT 6 
   UNION SELECT 7) AS n
  LEFT OUTER JOIN foos USING (id)
WHERE foos.id IS NULL;

Mais cela n'évolue pas très bien, car vous pouvez avoir plusieurs valeurs au lieu de six. Il peut devenir fastidieux de construire une longue liste avec un UNION nécessaire par valeur.

Une autre solution consiste à conserver une table à usage général de dix chiffres et à l'utiliser à plusieurs reprises à des fins multiples.

CREATE TABLE num (i int);
INSERT INTO num (i) VALUES (0), (1), (2), (3), (4), (5), (6), (7), (8), (9);

SELECT n.id
FROM 
  (SELECT n1.i + n10.i*10 AS id
   FROM num AS n1 CROSS JOIN num AS n10
   WHERE n1.i + n10.i*10 IN (2, 3, 4, 5, 6, 7)) AS n
  LEFT OUTER JOIN foos USING (id)
WHERE foos.id IS NULL;

Je montre la requête interne générant des valeurs de 0 à 99 même si ce n'est pas nécessaire dans ce cas. Mais vous pourriez avoir des valeurs supérieures à 10 dans votre liste. Le fait est qu'avec une table num, vous pouvez générer de grands nombres sans avoir à recourir à de très longues chaînes avec une UNION par valeur. En outre, vous pouvez spécifier la liste des valeurs souhaitées en un seul endroit, ce qui est plus pratique et plus lisible.

33
Bill Karwin

Je ne trouve pas de solution à votre problème précis qui n'utilise pas de table temporaire, mais une autre façon de faire votre requête en utilisant une sous-sélection au lieu d'une jointure est:

SELECT bars.* FROM bars WHERE bars.ID NOT IN (SELECT ID FROM foos)

Comme les autres affiches que j'ai écrites à l'origine:

SELECT * FROM foos WHERE foos.ID NOT IN (2, 4, 5, 6, 7)

mais j'ai réalisé que cela produisait le contraire de ce que vous vouliez.

20
Alnitak

Si vous utilisez PHP, vous pouvez faire cela sans créer de tables temporaires.

SELECT ID FROM foos WHERE foos.ID IN (2, 4, 5, 6, 7)

Vous pouvez utiliser la fonction array_diff () de PHP pour convertir cela en résultat souhaité. Si votre liste (2,4,5,6,7) est dans un tableau appelé $ list et que le résultat de la requête ci-dessus est dans un tableau $ result, alors

$no_counterparts = array_diff($list, $result);

... renverra tous les numéros de votre liste sans contrepartie dans votre table de base de données. Bien que cette solution n'effectue pas l'intégralité de l'opération dans la requête, le post-traitement que vous devez effectuer en PHP est minimal pour obtenir ce que vous voulez, et il peut être utile d'éviter d'avoir à créer une table temporaire.

6
Alex Matulich

J'avais un problème similaire. J'avais une plage où la clé primaire auto-incrémentée avait des valeurs manquantes, donc j'ai d'abord trouvé combien il y en avait: select count(*) from node where nid > 1962. En comparant ce nombre à la valeur la plus élevée, j'ai obtenu le nombre manquant. Ensuite, j'ai exécuté cette requête: select n2.nid from node n1 right join node n2 on n1.nid = (n2.nid - 1) where n1.nid is null and n2.nid > 1962 Ceci trouvera le nombre d'enregistrements manquants non consécutifs. Il n'en affichera pas de consécutives, et je ne suis pas tout à fait certain de la façon de procéder, au-delà de la modification de la clause ON pour permettre une plus grande latitude (ce qui augmenterait considérablement la table JOIN). En tout cas, cela m'a donné cinq résultats sur les sept manquants, et les deux autres étaient garantis à côté d'au moins l'un des cinq. Si vous avez un plus grand nombre manquant, vous aurez probablement besoin d'un autre moyen de trouver le manquant restant.

1
Daniel

La solution d'Alnitak (et la vôtre) devrait fonctionner, et je ne peux rien penser d'autre qui ne puisse fonctionner qu'en langage SQL.

Mais voici la question - comment passez-vous la liste des valeurs? N'est-il pas préférable de gérer cela dans le code appelant - c'est-à-dire de demander les ID et de les comparer dans le code colling, qui peut être dans un langage mieux adapté à ce type de manipulations.

0
Sunny Milenov