So wählen Sie nur die ersten Zeilen für jeden eindeutigen Wert einer Spalte aus


96

Angenommen, ich habe eine Tabelle mit Kundenadressen:

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

In der Tabelle kann ein Kunde wie John Smith mehrere Adressen haben. Ich benötige die Auswahlabfrage für diese Tabelle, um nur die erste gefundene Zeile zurückzugeben, in der Duplikate in 'CName' vorhanden sind. Für diese Tabelle sollten alle Zeilen außer der dritten zurückgegeben werden (oder die erste - jede dieser beiden Adressen ist in Ordnung, aber nur eine kann zurückgegeben werden). Gibt es ein Schlüsselwort, das ich der SELECT-Abfrage hinzufügen kann, um zu filtern, ob der Server den Spaltenwert bereits zuvor gesehen hat?

Antworten:


125

Eine sehr einfache Antwort, wenn Sie sagen, dass es Ihnen egal ist, welche Adresse verwendet wird.

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

Wenn Sie die erste Spalte beispielsweise nach einer "eingefügten" Spalte wünschen, handelt es sich um eine andere Abfrage

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

Es ist jedoch möglicherweise nicht beabsichtigt, diese Option bei der Auswahl von 10 Spalten zu verwenden. Es scheint auch, dass es keine Spalte vom Bittyp akzeptieren kann.
Nuit9

1
@ nuit9: Natürlich funktioniert es nicht mit Bit und 10 Spalten. Keine dieser Tatsachen steht in Ihrer Frage. Sie würden die 2. Technik oder die Technik von Ben Thul verwenden. Ich antwortete auf Ihre speziellen Fragen mit Hinweisen zur allgemeineren Lösung.
Gbn

Der erste Teil funktioniert mit mehreren Spalten, jedoch nicht mit Bit-Spalten. Ich habe dies jedoch in MS SQL Server 2016 getestet.
Netfed

24

In SQL 2k5 + können Sie Folgendes tun:

;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

5
Bitte erklären Sie, was Rang, Partition und [r] tun
Roberto

10

Sie können verwenden row_number(), um die Zeilennummer der Zeile abzurufen. Es verwendet den overBefehl - die partition byKlausel gibt an, wann die Nummerierung neu gestartet werden soll, und order bywählt aus, auf welcher Reihenfolge die Zeilennummer angeordnet werden soll. Selbst wenn Sie order byam Ende Ihrer Abfrage ein hinzufügen , wird die Reihenfolge im overBefehl bei der Nummerierung beibehalten .

select *
from mytable
where row_number() over(partition by Name order by AddressLine) = 1

6
In postgresql sind Fensterfunktionen in der WHERE-Klausel nicht erlaubt
ekanna

3
Dies ist für MS-SQL nicht zulässig.
Mixxiphoid

1
ROW_NUMBER()funktioniert nicht in WhereKlausel in Teradata
Pirate X

6

Sie können die row_numer() over(partition by ...)Syntax folgendermaßen verwenden:

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

Dies führt dazu, dass eine Spalte mit dem Namen erstellt wird. Hierbei handelt es sich rowum einen Zähler, der jedes Mal erhöht wird, wenn er dasselbe sieht CName, und diese Vorkommen nach indiziert AddressLine. Durch Auferlegen where row = 1kann man alphabetisch auswählen, CNamewessen AddressLinezuerst kommt. Wenn das order bywar desc, dann wäre es der Pick , CNamedas AddressLinekommt zuletzt in alphabetischer Reihenfolge.


1

Dadurch erhalten Sie eine Zeile jeder doppelten Zeile. Es gibt Ihnen auch die Spalten vom Bittyp und funktioniert zumindest in 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

Wenn Sie stattdessen alle Duplikate finden möchten, ändern Sie einfach rn = 1 in rn> 1. Ich hoffe, dies hilft

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.