web-dev-qa-db-fra.com

Suppression des doublons d'une requête SQL (pas seulement "utiliser distinct")

C'est probablement simple, voici ma requête:

SELECT DISTINCT U.NAME, P.PIC_ID
FROM USERS U, PICTURES P, POSTINGS P1
WHERE U.EMAIL_ID = P1.EMAIL_ID AND P1.PIC_ID = P.PIC_ID AND P.CAPTION LIKE '%car%';

mais cela supprimera uniquement les doublons où une ligne a à la fois le même u.name et p.pic_id. Je le veux donc s'il y a des doublons des noms, il laisse juste de côté les autres lignes. C'est une requête étrange, mais en général, comment puis-je appliquer le distinct à une seule colonne de la clause SELECT?

13
Dave

Choix arbitraire de conserver le PIC_ID minimum. Évitez également d'utiliser la syntaxe de jointure implicite.

SELECT U.NAME, MIN(P.PIC_ID)
    FROM USERS U
        INNER JOIN POSTINGS P1
            ON U.EMAIL_ID = P1.EMAIL_ID
        INNER JOIN PICTURES P
            ON P1.PIC_ID = P.PIC_ID
    WHERE P.CAPTION LIKE '%car%'
    GROUP BY U.NAME;
18
Joe Stefanelli

Votre question est un peu déroutante; voulez-vous afficher une seule ligne par utilisateur ou voulez-vous afficher une ligne par image mais supprimer les valeurs répétitives dans le champ U.NAME? Je pense que vous voulez le second; sinon, il y a beaucoup de réponses pour la première.

L'affichage des valeurs répétitives est une logique d'affichage, pour laquelle SQL n'a pas vraiment été conçu. Vous pouvez utiliser un curseur dans une boucle pour traiter les résultats ligne par ligne, mais vous perdrez beaucoup de performances. Si vous avez un langage frontal "intelligent" comme un langage .NET ou Java, quelle que soit la construction dans laquelle vous mettez ces données peut être manipulée à moindre coût pour supprimer les valeurs répétitives avant de finalement l'afficher dans l'interface utilisateur.

Si vous utilisez Microsoft SQL Server et que la transformation DOIT être effectuée au niveau de la couche de données, vous pouvez envisager d'utiliser un CTE (Computed Table Expression) pour contenir la requête initiale, puis sélectionnez les valeurs de chaque ligne du CTE selon que les colonnes de la ligne précédente contiennent les mêmes données. Ce sera plus performant que le curseur, mais ce sera un peu désordonné de toute façon. Observer:

USING CTE (Row, Name, PicID)
AS
(
    SELECT ROW_NUMBER() OVER (ORDER BY U.NAME, P.PIC_ID),
       U.NAME, P.PIC_ID
    FROM USERS U
        INNER JOIN POSTINGS P1
            ON U.EMAIL_ID = P1.EMAIL_ID
        INNER JOIN PICTURES P
            ON P1.PIC_ID = P.PIC_ID
    WHERE P.CAPTION LIKE '%car%'
    ORDER BY U.NAME, P.PIC_ID 
)
SELECT
    CASE WHEN current.Name == previous.Name THEN '' ELSE current.Name END,
    current.PicID
FROM CTE current
LEFT OUTER JOIN CTE previous
   ON current.Row = previous.Row + 1
ORDER BY current.Row

L'exemple ci-dessus est spécifique à TSQL; il n'est pas garanti de fonctionner dans n'importe quel autre DBPL comme PL/SQL, mais je pense que la plupart des moteurs SQL au niveau de l'entreprise ont quelque chose de similaire.

2
KeithS

Vous devez indiquer à la requête quelle valeur choisir pour les autres colonnes, MIN ou MAX semblent être des choix appropriés.

 SELECT
   U.NAME, MIN(P.PIC_ID)
 FROM
   USERS U,
   PICTURES P,
   POSTINGS P1
 WHERE
   U.EMAIL_ID = P1.EMAIL_ID AND
   P1.PIC_ID = P.PIC_ID AND
   P.CAPTION LIKE '%car%'
 GROUP BY
   U.NAME;
1
Brandon Horsley

Si je vous comprends bien, vous souhaitez lister pour exclure les doublons sur une seule colonne, la jointure interne à une sous-sélection

select u.* [whatever joined values]
from users u
inner join
(select name from users group by name having count(*)=1) uniquenames
on uniquenames.name = u.name
1
Xhalent

Si je vous comprends bien, vous voulez une liste de toutes les photos avec le même nom (et leurs différents identifiants) de sorte que leur nom apparaisse plus d'une fois dans le tableau. Je pense que cela fera l'affaire:

SELECT U.NAME, P.PIC_ID
FROM USERS U, PICTURES P, POSTINGS P1
WHERE U.EMAIL_ID = P1.EMAIL_ID AND P1.PIC_ID = P.PIC_ID AND U.Name IN (
SELECT U.Name 
FROM USERS U, PICTURES P, POSTINGS P1
WHERE U.EMAIL_ID = P1.EMAIL_ID AND P1.PIC_ID = P.PIC_ID AND P.CAPTION LIKE '%car%';
GROUP BY U.Name HAVING COUNT(U.Name) > 1)

Je ne l'ai pas exécuté, il peut donc y avoir une ou deux erreurs de syntaxe.

0
Chris B. Behrens