2
votes

Division relationnelle - imitant «UNIQUEMENT DANS»

J'essaie d'écrire une requête dans Oracle SQL qui prend deux paramètres et trouve dans la table toutes les instances où seulement ou se produisent, quel que soit le nombre d'entre eux dans la recherche. Voici un exemple de ce que je recherche:

SELECT DISTINCT ID, FileType, COUNT(FileType)
FROM Table ta
WHERE (ta.FileType = 'jpg' or ta.FileType = 'png') and
NOT EXISTS
    (SELECT *
     FROM Table tb
     WHERE ta.FileType = tb.FileType and
     (tb.FileType != 'jpg' or tb.FileType != 'png'))
GROUP BY ID, FileType;

Mon objectif est de prendre tous les ID avec UNIQUEMENT les png OU les jpg et d'omettre le reste, donc je veux seulement les ID 2, 3 et 4 pour être renvoyé.

J'ai essayé de rechercher des solutions ici et je n'ai rien trouvé qui semble correspondre à mon cas. (La plus proche étant cette question: SQL sélectionne les lignes avec seulement un une certaine valeur en eux )

J'ai réussi à découvrir que le problème peut être résolu par la division relationnelle, mais je n'en ai pas eu jusqu'ici. Jusqu'à présent, ma requête ressemble à ceci:

| ID  | FileType | COUNT(FileType) |
|-----|----------|-----------------|
| 1   | txt      | 1               |
| 1   | png      | 3               |
| 1   | jpg      | 2               |
====================================
| 2   | txt      | 0               |
| 2   | png      | 6               |
| 2   | jpg      | 0               |
====================================
| 3   | txt      | 0               |
| 3   | png      | 0               |
| 3   | jpg      | 5               |
====================================
| 4   | txt      | 0               |
| 4   | png      | 3               |
| 4   | jpg      | 1               |
====================================
| 5   | txt      | 5               |
| 5   | png      | 0               |
| 5   | jpg      | 3               |

Quand j'essaye ceci, je n'obtiens aucun résultat. Quelqu'un a des idées sur lesquelles je me suis trompé?


0 commentaires

3 Réponses :


1
votes

Vous êtes proche. Voyez simplement combien de types de fichiers distincts vous avez. Voici vos identifiants:

SELECT ID
FROM Table ta
GROUP BY ID
HAVING count(distinct FileType) <= 2 
   and max(ta.FileType) in ('jpg','png') 
   and min(ta.FileType) in ('jpg','png');

Mise à jour: la partie supérieure échouera pour le cas 4.

Cela le fera mais c'est moche:

SELECT ID
FROM Table ta
GROUP BY ID
HAVING count(distinct FileType) = 1 and max(ta.FileType) in ('jpg','png');

C'est moche car vous ne pouvez pas l'étendre pour 3 valeurs.


0 commentaires

2
votes

Vous pouvez utiliser les opérateurs set:

SELECT ID FROM tab WHERE FileType IN ('jpg', 'png')
MINUS
SELECT ID 
FROM (SELECT * FROM tab WHERE FileType IS NOT NULL) 
WHERE FileType NOT IN ('jpg', 'png')

Hypothèse: FileType n'est pas nullable.


Gestion NULL:

SELECT ID FROM tab WHERE FileType IN ('jpg', 'png')
MINUS
SELECT ID FROM tab WHERE FileType NOT IN ('jpg', 'png')


6 commentaires

C'est une bonne et belle requête. Vous pouvez l'améliorer en l'écrivant de cette manière: SELECT ID FROM onglet WHERE FileType IN ('jpg', 'png') moins SELECT ID FROM onglet WHERE FileType NOT IN ('jpg', 'png')


Cette réponse exprime au mieux la demande. SQL pur et puissant.


Merci, c'est la solution la plus claire et la plus simple à comprendre. Je crois que cela fonctionnera.


Mes excuses pour avoir nécrosé si rapidement et pour ne pas m'en être rendu compte plus tôt, mais si le FileType est Nullable, la sagesse conventionnelle me dit que remplacer IN par '= (jpg ou png)' et NOT IN par '! = (Jpg ou png)' serait correct, mais cela ne semble pas être le cas. Des idées dans cette situation?


Les valeurs @Cody Null peuvent être supprimées à l'aide de la vue intégrée.


Merci pour votre aide!



0
votes

Il suffit d'utiliser une table "d'aide":

WITH TypeCounts AS
(
  SELECT ID, FileType, COUNT(*) AS CNT
  FROM Table
  GROUP BY ID, FileType
)
SELECT *
FROM Table
LEFT JOIN TypeCounts txt ON txt.ID = Table.ID AND txt.FileType = 'txt'
LEFT JOIN TypeCounts jpg ON jpg.ID = Table.ID AND jpg.FileType = 'jpg'
LEFT JOIN TypeCounts png ON png.ID = Table.ID AND png.FileType = 'png'
WHERE COALESCE(txt.CNT,0) = 0 AND
    ( COALESCE(jpg.CNT,0) > 0 OR COALESCE(png.CNT,0) > 0)

Ce qui est bien avec cette solution, c'est qu'elle est très claire en ce qui concerne les règles métier et est donc plus facile à maintenir.


0 commentaires