173 votes

DISTINCT pour une seule colonne

Disons que j'ai la requête suivante.

SELECT ID, Email, ProductName, ProductModel FROM Products

Comment puis-je le modifier pour qu'il ne renvoie pas d'e-mails en double ?

En d'autres termes, lorsque plusieurs lignes contiennent le même courriel, je veux que les résultats ne comprennent qu'une seule de ces lignes (de préférence la dernière). Les doublons dans les autres colonnes doivent être autorisés.

Des clauses comme DISTINCT y GROUP BY semblent travailler sur des rangs entiers. Je ne sais donc pas comment aborder la question.

3 votes

Ok, vous devez utiliser la PARTITION ou utiliser deux instructions de sélection ?

0 votes

Et qu'est-ce qui devrait être affiché s'il y a disons 2 lignes avec le même Email mais un nom de produit différent ? Le site (de préférence le dernier) n'est pas clair. Le dernier par quelle commande ?

0 votes

@ypercube Comme indiqué dans la question, de préférence la dernière. Cependant, ce n'est pas vraiment critique pour moi. Je veux juste l'un d'entre eux.

205voto

Chandu Points 40028

Si vous utilisez SQL Server 2005 ou supérieur, utilisez ceci :

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
              ) a
WHERE rn = 1

EDIT : Exemple utilisant une clause where :

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
                   WHERE ProductModel = 2
                     AND ProductName LIKE 'CYBER%'

              ) a
WHERE rn = 1

4 votes

Je dois enquêter sur cette clause PARTITION, je ne l'ai jamais vue en action auparavant. Merci pour l'exemple

0 votes

@Cybernate Une complication : Mon coeur SELECT a besoin d'un WHERE condition. Je pense que les numéros de rangée seront attribués à toutes les rangées du tableau. Cette syntaxe me dépasse un peu. Y a-t-il une chance qu'une mise à jour garantisse une ligne avec un courriel particulier qui répond à la condition WHERE condition ?

1 votes

Vous pouvez ajouter une clause "where" au sql interne. Je vais mettre à jour le post une fois que je peux accéder à mon ordinateur portable.

11voto

pero Points 7952

Cela suppose que le serveur SQL 2005+ et votre définition de "dernier" est le PK maximum pour un courriel donné.

WITH CTE AS
(
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel, 
       ROW_NUMBER() OVER (PARTITION BY Email ORDER BY ID DESC) AS RowNumber 
FROM   Products
)
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel
FROM CTE 
WHERE RowNumber = 1

7voto

jon3laze Points 1676

Lorsque vous utilisez DISTINCT pensez-y comme une ligne distincte, et non comme une colonne. Elle ne renverra que les lignes dont les colonnes ne correspondent pas exactement à la même chose.

SELECT DISTINCT ID, Email, ProductName, ProductModel
FROM Products

----------------------
1 | something@something.com | ProductName1 | ProductModel1
2 | something@something.com | ProductName1 | ProductModel1

La requête renverrait les deux lignes parce que l'option ID La colonne est différente. Je suppose que le ID est une colonne IDENTITY qui s'incrémente, si vous voulez retourner la dernière colonne, je recommande quelque chose comme ceci :

SELECT DISTINCT TOP 1 ID, Email, ProductName, ProductModel
FROM Products
ORDER BY ID DESC

Le site TOP 1 ne renverra que le premier enregistrement, en l'ordonnant par l'indice ID descendant, il retournera les résultats avec la dernière ligne en premier. Vous obtiendrez ainsi le dernier enregistrement.

3 votes

Comme indiqué dans la question, je vois que DISTINCT fonctionne sur la ligne entière. Je veux faire comme vous le suggérez ci-dessus, mais pour chaque fois que l'email est dupliqué dans les résultats (pas seulement une fois).

0 votes

Dans ce cas, je vous recommande de suivre la réponse de @Cybernate. Elle devrait faire exactement ce dont vous avez besoin.

1voto

jRam90 Points 62

Pour Access, vous pouvez utiliser la requête SQL Select que je présente ici :

Par exemple, vous avez ce tableau :

CLIENTE|| NOMBRES || MAIL

888 || T800 ARNOLD || t800.arnold@cyberdyne.com

123 || JOHN CONNOR || s.connor@skynet.com

125 || SARAH CONNOR ||s.connor@skynet.com

Et vous devez sélectionner uniquement des mails distincts. Vous pouvez le faire avec ceci :

SQL SELECT :

SELECT MAX(p.CLIENTE) AS ID_CLIENTE
, (SELECT TOP 1 x.NOMBRES 
    FROM Rep_Pre_Ene_MUESTRA AS x 
    WHERE x.MAIL=p.MAIL 
     AND x.CLIENTE=(SELECT MAX(l.CLIENTE) FROM Rep_Pre_Ene_MUESTRA AS l WHERE x.MAIL=l.MAIL)) AS NOMBRE, 
p.MAIL
FROM Rep_Pre_Ene_MUESTRA AS p
GROUP BY p.MAIL;

Vous pouvez l'utiliser pour sélectionner l'ID maximum, le nom correspondant à cet ID maximum, vous pouvez ajouter tout autre attribut de cette façon. Ensuite, à la fin, vous mettez la colonne distincte pour filtrer et vous ne la regroupez qu'avec cette dernière colonne distincte.

Cela vous apportera l'ID maximum avec les données correspondantes, vous pouvez utiliser min ou toute autre fonction et vous répliquez cette fonction aux sous-requêtes.

Cette sélection va revenir :

CLIENTE|| NOMBRES || MAIL

888 || T800 ARNOLD || t800.arnold@cyberdyne.com

125 || SARAH CONNOR ||s.connor@skynet.com

N'oubliez pas d'indexer les colonnes que vous sélectionnez et la colonne distincte doit contenir des données non numériques, toutes en majuscules ou en minuscules, sinon cela ne fonctionnera pas. Cela fonctionnera également avec un seul courrier recommandé. Bon codage ! !!

0voto

JohnFx Points 23761

La raison DISTINCT y GROUP BY travailler sur des lignes entières est que votre requête retourne des lignes entières.

Pour vous aider à comprendre : Essayez d'écrire à la main ce que la requête doit retourner et vous verrez qu'il est ambigu de savoir ce qu'il faut mettre dans les colonnes non dupliquées.

Si vous vous moquez littéralement de ce que contiennent les autres colonnes, ne les renvoyez pas. Renvoyer une ligne aléatoire pour chaque adresse électronique me semble un peu inutile.

0 votes

@JohnFix Je veux retourner des lignes entières. Je ne veux pas que des lignes soient retournées lorsque les résultats comprennent déjà une ligne avec la même valeur dans la colonne Email.

0 votes

Alors comment doit-il décider lequel renvoyer ? Voulez-vous vraiment une requête qui renvoie une ligne arbitraire pour chaque e-mail ? Il semble vraiment que vous deviez repenser le problème que vous essayez de résoudre. Presque chaque fois que cette question m'a été posée (et elle revient souvent), il s'est avéré que le développeur n'avait pas réfléchi aux conséquences de ce comportement dans l'application.

6 votes

J'ai vraiment du mal à suivre votre logique. Comme indiqué dans la question, je préférerais la dernière (triée par ID). Oui, s'il sélectionnait une ligne aléatoire, ce serait bien. Et, oui, j'y ai pensé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X