Comment puis-je (Ou puis-je) sélectionner DISTINCT sur plusieurs colonnes?

je dois récupérer toutes les lignes d'une table où 2 colonnes combinées sont toutes différentes. Donc, je veux que toutes les ventes qui n'ont pas d'autres ventes qui s'est passé le même jour pour le même prix. Les ventes qui sont uniques basés sur le jour et le prix seront mis à jour à un statut actif.

Donc, je suis en train de penser:

UPDATE sales
SET status = 'ACTIVE'
WHERE id IN (SELECT DISTINCT (saleprice, saledate), id, count(id)
             FROM sales
             HAVING count = 1)

mais mon cerveau me fait mal d'aller plus loin.

339
demandé sur Erwin Brandstetter 2008-09-10 19:33:10

4 réponses

SELECT DISTINCT a,b,c FROM t

est environ équivalent à:

SELECT a,b,c FROM t GROUP BY a,b,c

c'est une bonne idée de s'habituer au groupe par syntaxe, car il est plus puissant.

pour votre requête, je le ferais comme ceci:

UPDATE sales
SET status='ACTIVE'
WHERE id IN
(
    SELECT id
    FROM sales S
    INNER JOIN
    (
        SELECT saleprice, saledate
        FROM sales
        GROUP BY saleprice, saledate
        HAVING COUNT(*) = 1 
    ) T
    ON S.saleprice=T.saleprice AND s.saledate=T.saledate
 )
379
répondu Joel Coehoorn 2008-09-10 15:55:08

si vous rassemblez les réponses jusqu'à présent, nettoyez et améliorez, vous arriverez à cette requête supérieure:

UPDATE sales
SET    status = 'ACTIVE'
WHERE  (saleprice, saledate) IN (
    SELECT saleprice, saledate
    FROM   sales
    GROUP  BY saleprice, saledate
    HAVING count(*) = 1 
    );

qui est beaucoup plus rapide que l'un ou l'autre d'entre eux. Nuance la performance de la réponse actuellement acceptée par le facteur 10-15 (dans mes tests sur PostgreSQL 8.4 et 9.1).

, Mais c'est encore loin d'être optimale. Utiliser un NOT EXISTS (anti-)semi-jointure pour une meilleure performance. EXISTS est SQL standard, a été autour pour toujours (au moins depuis PostgreSQL 7.2, bien avant que cette question a été posée) et répond parfaitement aux exigences présentées:

UPDATE sales s
SET    status = 'ACTIVE'
WHERE  NOT EXISTS (
   SELECT 1
   FROM   sales s1
   WHERE  s.saleprice = s1.saleprice
   AND    s.saledate  = s1.saledate
   AND    s.id <> s1.id                     -- except for row itself
   );
AND    s.status IS DISTINCT FROM 'ACTIVE';  -- avoid empty updates. see below

SQL Fiddle.

clé Unique pour identifier la rangée

si vous n'avez pas de clé primaire ou unique pour la table ( id dans le exemple), vous pouvez substituer avec la colonne système ctid pour les fins de cette requête (mais pas pour d'autres fins):

   AND    s1.ctid <> s.ctid

Chaque table doit avoir une clé primaire. Ajoutez-en un si vous n'avez pas, encore. Je suggère une colonne serial ou IDENTITY dans Postgres 10+.

Related:

Comment est-ce plus rapide?

le sous-jeu dans le EXISTS (anti -) semi-jointure peut arrêter d'évaluer dès que le premier dupe est trouvé (aucun point à regarder plus loin). Pour une table de base avec peu de duplicata c'est seulement légèrement plus efficace. Avec beaucoup de duplicata cela devient manière plus efficace.

Exclure les mises à jour vides

si certaines ou plusieurs lignes ont déjà status = 'ACTIVE' , votre mise à jour ne changera rien, mais insérera quand même une nouvelle version de ligne au coût total (des exceptions mineures s'appliquent). Normalement, vous ne voulez pas cela. Ajouter une autre condition WHERE comme démontré ci-dessus pour rendre cela encore plus rapide:

si status est défini NOT NULL , vous pouvez simplifier à:

AND status <> 'ACTIVE';

subtil difference in NULL manipulation

cette requête (contrairement à la réponse actuellement acceptée par Joel ) ne traite pas les valeurs nulles comme égales. Ces deux rangées pour (saleprice, saledate) se qualifieraient comme "distinct" (bien qu'ayant l'air identique à l'oeil humain):

(123, NULL)
(123, NULL)

passe aussi dans un indice unique et presque n'importe où ailleurs, puisque les valeurs nulles ne comparent pas égal selon la norme SQL. Voir:

otoh, que, GROUP BY ou DISTINCT ou DISTINCT ON () traiter les valeurs NULLES comme des égaux. Utilisez un style de requête approprié en fonction de ce que vous voulez accomplir. Vous pouvez toujours utiliser ce style de requête plus rapide en utilisant IS NOT DISTINCT FROM au lieu de = pour n'importe quelle ou toutes les comparaisons pour rendre nulle comparer égale. Plus:

si toutes les colonnes comparées sont définies NOT NULL , il n'y a pas de place pour le désaccord.

295
répondu Erwin Brandstetter 2018-01-13 12:24:49

le problème avec votre requête est que lorsque vous utilisez un groupe par clause (ce que vous faites essentiellement en utilisant distinct) vous ne pouvez utiliser que des colonnes que vous groupez par ou Agrégez des fonctions. Vous ne pouvez pas utiliser l'id de colonne parce qu'il y a des valeurs potentiellement différentes. Dans votre cas, il n'y a toujours qu'une seule valeur à cause de la clause D'avoir, mais la plupart des SGBDR ne sont pas assez intelligents pour le reconnaître.

cela devrait fonctionner cependant (et n'a pas besoin d'une jointure):

UPDATE sales
SET status='ACTIVE'
WHERE id IN (
  SELECT MIN(id) FROM sales
  GROUP BY saleprice, saledate
  HAVING COUNT(id) = 1
)

vous pouvez également utiliser MAX ou AVG au lieu de MIN, il est seulement important d'utiliser une fonction qui renvoie la valeur de la colonne s'il n'y a qu'une seule ligne correspondante.

22
répondu Christian Berg 2008-09-10 16:17:13

je veux sélectionner les valeurs distinctes d'une colonne 'GrondOfLucht", mais ils doivent être triés dans l'ordre, dans la colonne "sortering'. Je ne peux pas obtenir les valeurs distinctes d'une seule colonne en utilisant

Select distinct GrondOfLucht,sortering
from CorWijzeVanAanleg
order by sortering

il donnera également la colonne 'sortering' et parce que 'GrondOfLucht' et 'sortering' n'est pas unique, le résultat sera toutes les rangées.

utilisez le groupe pour sélectionner les enregistrements de 'GrondOfLucht' dans l'ordre donné par ' sortering

SELECT        GrondOfLucht
FROM            dbo.CorWijzeVanAanleg
GROUP BY GrondOfLucht, sortering
ORDER BY MIN(sortering)
1
répondu frans eilering 2018-01-13 10:41:47