1
votes

Sélectionnez la valeur de la colonne mais pas les deux

Il existe une table T0 qui ressemble à ceci et qui utilise MySQL 5.7. Je stocke des identifiants dans id , du texte dans C1 et NULL ou des entiers dans C2 - NULL étant la valeur par défaut. Une entrée dans C1 peut avoir plusieurs valeurs dans C2. Si vous connaissez les tables de fermeture, vous remarquerez que cette table est légèrement différente en ce que les entrées C1 ne correspondent pas aux entrées d'id.

id | C1 | C2
---+----+-----
1  | a  | NULL
2  | b  | NULL
4  | c  | 3
6  | d  | NULL

La valeur de C1 est stockée dans C2. La table est définie de telle sorte que C1 ne puisse pas avoir de valeurs en double dans C2. Donc c, par exemple, ne peut pas avoir deux 3. La table est donc unique de cette façon.

Voudrait qu'une requête renvoie toutes les lignes où la valeur du champ C1 est 3 et si la valeur C1 n'est pas 3, renvoie la ligne où la valeur C1 est NULL.

J'ai essayé le code suivant mais en vain.

SELECT *
FROM T0
WHERE C2 = 3 OR C2 IS NULL

La requête devrait donc pouvoir renvoyer 1, 2, 4 et 6. Le résultat souhaité est:

id | C1 | C2
---+----+-----
1  | a  | NULL
2  | b  | NULL
3  | c  | NULL
4  | c  | 3
5  | c  | 5
6  | d  | NULL


12 commentaires

Quel type de données stockez-vous dans C2?


Il y a une certaine confusion dans votre question. Vouliez-vous dire que vous vouliez trouver des valeurs de C1 pour lesquelles il existe une ligne avec C2 = 3 ou il existe une ligne avec C2 IS NULL? Être clair.


Il semble également que ce type d'approche soit contraire à la manière dont les tables sont censées être utilisées. Peut-être pouvez-vous expliquer votre objectif réel pendant que vous y êtes!


@LightnessRacesinOrbit vient de le peaufiner. En fait, je ne vois pas cela comme antithétique. La valeur NULL est la valeur par défaut. C1 peut avoir plusieurs valeurs dans C2 - plus comme une relation 1 à plusieurs.


Quelle requête puis-je utiliser pour renvoyer les valeurs d'id 1,2,4 et 6 (où C2 vaut 3 ou NULL)? - Qu'en est-il de la ligne avec id = 3?


Je ne comprends pas du tout votre question, mon sens logique dirait que votre résultat devrait / serait les enregistrements où id = 1, 2 et 6 parce que C2 pour le groupe C1 = 2 a à la fois C2 = NULL et C2 = 3. c'est ce que vous demandez avec "Souhaitez qu'une requête renvoie des lignes où la valeur du champ C1 de la table est soit 3, soit NULL, mais pas les deux."


@RaymondNijland Désolé pour ça. J'ai mis à jour la question. J'ai codé pendant des heures si fatigué. J'espère que le rafraîchissement vous aidera.


qu'en est-il des «liens»? que doit-il se passer par exemple si C1 a a deux enregistrements C2 avec NULL ?


@RaymondNijland La table est définie de telle sorte que les liens ne peuvent pas se produire. Déjà testé et déjà porteur de données.


Ok c'est un peu plus clair. Donc, vous voulez renvoyer des lignes avec la valeur C2 3 et des lignes avec la valeur C2 NULL qui n'ont pas de ligne correspondante (via C1) avec la valeur 3. Nous pouvons travailler avec cela (même si cela semble toujours être une disposition de table un peu étrange)


@LightnessRacesinOrbit Exactement! Merci pour les questions. Ils m'ont aidé à mettre à jour ma question.


Aucun problème! Bonne chance.


4 Réponses :


2
votes

Vous pouvez vérifier cette condition en utilisant NOT EXISTS :

SELECT *
FROM t
WHERE (
    C2 = 3
) OR (
    C2 IS NULL AND NOT EXISTS (
        SELECT 1
        FROM t AS x
        WHERE x.C1 = t.C1 AND x.C2 = 3
    )
)


8 commentaires

Je serais tenté de reformer cela en une jointure mais oui


Ça a l'air bien ici: dbfiddle.uk/…


@SalmanA dans le violon ça marche. J'ai examiné le code et cela devrait fonctionner et la plupart des gens n'auraient aucun problème avec lui. Dans ma base de données, il faut 5 ms d'exécution et 5 ms de récupération, ce qui est bien. Mais je n'arrive pas à comprendre pourquoi cela ne fonctionnera pas comme prévu dans ma base de données mais renvoie toutes les lignes sauf l'id # 5. Pourriez-vous avoir une implémentation alternative?


Aucune idée, peut-être avez-vous gâché les alias utilisés dans la sous-requête (x et t), par exemple. WHERE C1 = t.C1 AND t.C2 = 3 est incorrect et il produira 5 lignes au lieu de 4. Je réviserai la requête et spécifierai explicitement les alias comme ils devraient être.


Ça a marché. Merci Salman A pour votre aide et votre sincérité et merci à @LightnessRacesinOrbit de m'avoir indiqué votre réponse. Il s'avère que C1 n'avait pas été indexé donc un 'NOT EXISTS' ne fonctionnait pas comme prévu. La table a plus de 12 000 enregistrements. Juste fait un petit contournement en ajoutant une jointure gauche à une autre table dans la sous-requête que vous avez fournie et je suis de retour sur la bonne voie. Le tableau n'était pas nécessaire à cette fin, mais C1 ne répondait pas au besoin. Merci pour votre temps également. Testera toutes les autres réponses et leur donnera également une réponse.


Cela ne semble pas juste, @John. L'indexation n'affecte pas le résultat, uniquement les performances.


@LightnessRacesinOrbit C'est vrai que l'indexation n'affecte que les performances. Mais c'est dans la plupart des cas. Les configurations complexes comme celle-ci sont autre chose. C'était le peu que je pouvais divulguer sans dévoiler toute la configuration.


Non, c'est dans tous les cas. Votre "configuration" n'est pas assez complexe pour enfreindre les principes fondamentaux. Vous avez fait une erreur dans vos observations.



0
votes

Que diriez-vous d'utiliser une UNION?

SELECT * FROM t0 where C2=3 
UNION 
SELECT * FROM t0 where C2 IS NULL AND C1 NOT IN (SELECT C1 FROM t0 WHERE C2<>3) ORDER BY id;


0 commentaires

0
votes

C'est une approche plus simple et serait plus rapide que de faire une jointure quand il y a beaucoup de lignes dans la table.

SELECT * 
  FROM T0 
 WHERE (C2 = 3) 
    OR (C2 IS NULL AND 
        C1 NOT IN (SELECT C1 FROM T0 WHERE C2 = 3))


0 commentaires

0
votes

Devez-vous afficher l'ID? Sinon, cela devient facile:

select c1, max(c2)
from t0
where c2 = 3 or c2 is null
group by c1
order by c1;


1 commentaires

Oui, les identifiants sont requis car ils seront utilisés comme clés étrangères dans une autre table.