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.
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
)
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
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.
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.
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)