Comment sélectionner uniquement les premières lignes pour chaque valeur unique d'une colonne

96

Disons que j'ai un tableau des adresses clients:

CName           |   AddressLine
-------------------------------
John Smith      | 123 Nowheresville
Jane Doe        | 456 Evergreen Terrace
John Smith      | 999 Somewhereelse
Joe Bloggs      | 1 Second Ave

Dans le tableau, un client comme John Smith peut avoir plusieurs adresses. J'ai besoin de la requête de sélection pour cette table pour renvoyer uniquement la première ligne trouvée où il y a des doublons dans «CName». Pour cette table, il doit renvoyer toutes les lignes sauf la 3e (ou la 1ère - n'importe laquelle de ces deux adresses est correcte mais une seule peut être renvoyée). Existe-t-il un mot-clé que je peux ajouter à la requête SELECT pour filtrer en fonction du fait que le serveur a déjà vu la valeur de la colonne auparavant?

nuit9
la source

Réponses:

125

Une réponse très simple si vous dites que vous ne vous souciez pas de l'adresse utilisée.

SELECT
    CName, MIN(AddressLine)
FROM
    MyTable
GROUP BY
    CName

Si vous voulez le premier selon, par exemple, une colonne "insérée", alors c'est une requête différente

SELECT
    M.CName, M.AddressLine,
FROM
    (
    SELECT
        CName, MIN(Inserted) AS First
    FROM
        MyTable
    GROUP BY
        CName
    ) foo
    JOIN
    MyTable M ON foo.CName = M.CName AND foo.First = M.Inserted
gbn
la source
Bien qu'il ne soit pas destiné à être utilisé de cette façon lors de la sélection de 10 colonnes. Il semble également qu'il ne puisse pas accepter une colonne de type bit.
nuit9
1
@ nuit9: bien sûr cela ne fonctionnera pas avec bit et 10 colonnes. Aucun de ces faits n'est dans votre question. Vous utiliseriez la 2ème technique ou la technique de Ben Thul. J'ai répondu à ce que vous avez demandé spécifiquement, avec des conseils sur la façon de résoudre plus généralement.
gbn
La première partie fonctionne avec plusieurs colonnes, mais pas avec des colonnes de type bit. J'ai cependant testé cela dans MS SQL Server 2016.
netfed le
24

Dans SQL 2k5 +, vous pouvez faire quelque chose comme:

;with cte as (
  select CName, AddressLine,
  rank() over (partition by CName order by AddressLine) as [r]
  from MyTable
)
select CName, AddressLine
from cte
where [r] = 1
Ben Thul
la source
5
Veuillez expliquer ce que font le classement, la partition et [r]
Roberto
10

Vous pouvez utiliser row_number()pour obtenir le numéro de ligne de la ligne. Il utilise la overcommande - la partition byclause spécifie quand redémarrer la numérotation et order bysélectionne sur quoi ordonner le numéro de ligne. Même si vous avez ajouté un order byà la fin de votre requête, cela conserverait l'ordre dans la overcommande lors de la numérotation.

select *
from mytable
where row_number() over(partition by Name order by AddressLine) = 1
Franc
la source
6
Dans postgresql, les fonctions de fenêtre ne sont pas autorisées dans la clause WHERE
ekanna
3
Ce n'est pas non plus autorisé pour MS-SQL.
Mixxiphoid
1
ROW_NUMBER()ne fonctionne pas non plus en Whereclause dans Teradata
Pirate X
6

Vous pouvez utiliser la row_numer() over(partition by ...)syntaxe comme ceci:

select * from
(
select *
, ROW_NUMBER() OVER(PARTITION BY CName ORDER BY AddressLine) AS row
from myTable
) as a
where row = 1

Cela crée une colonne appelée row, qui est un compteur qui s'incrémente à chaque fois qu'il voit la même chose CNameet indexe ces occurrences par AddressLine. En imposant where row = 1, on peut sélectionner celui CNamequi AddressLinevient en premier par ordre alphabétique. Si l' order byétait desc, alors il choisirait le CNamedont AddressLinevient en dernier par ordre alphabétique.

FatihAkici
la source
1

Cela vous donnera une ligne de chaque ligne en double. Il vous donnera également les colonnes de type bit, et cela fonctionne au moins dans MS Sql Server.

(select cname, address 
from (
  select cname,address, rn=row_number() over (partition by cname order by cname) 
  from customeraddresses  
) x 
where rn = 1) order by cname

Si vous voulez trouver tous les doublons à la place, changez simplement le rn = 1 en rn> 1. J'espère que cela vous aidera

netfed
la source