SQL / mysql - Wählen Sie different / UNIQUE aus, geben Sie jedoch alle Spalten zurück?

373
SELECT DISTINCT field1, field2, field3, ......   FROM table

Ich versuche, die folgende SQL-Anweisung auszuführen, möchte aber, dass alle Spalten zurückgegeben werden. Ist dies möglich? Etwas wie:

SELECT DISTINCT field1, * from table
aryaxt
quelle
12
Warum funktioniert SELECT DISTINCT * FROM tabledas bei dir nicht?
Ypercubeᵀᴹ
19
Wenn Ihre Tabelle eine PK hat, sollten alle Zeilen distinctper Definition sein. Wenn Sie versuchen, nur DISTINCT field1alle anderen Spalten auszuwählen, aber irgendwie zurückzugeben, was sollte für die Spalten geschehen, die mehr als einen Wert für einen bestimmten field1Wert haben? Sie müssten beispielsweise eine GROUP BYArt Aggregation für die anderen Spalten verwenden.
Martin Smith
1
Wenn Sie wiederholte Zeilen und nicht nur unterschiedliche Zeilen möchten, entfernen Sie das eindeutige Schlüsselwort.
Hyperboreus
2
Können Sie ein Beispiel geben, wie die Ergebnisse aussehen sollen? Bisher kann ich Ihre gewünschte Anfrage nicht verstehen.
rekursiv
3
Hier ist die Antwort auf eine ähnliche Frage: Sie müssen zuerst die eindeutige Spalte mit ihren IDs abrufen und sie dann mit der Originaltabelle verbinden. SELECT DISTINCT für eine Spalte, geben Sie mehrere andere Spalten zurück
yadavr

Antworten:

407

Sie suchen eine Gruppe von:

select *
from table
group by field1

Was gelegentlich mit einer eindeutigen on-Aussage geschrieben werden kann:

select distinct on field1 *
from table

Auf den meisten Plattformen funktioniert jedoch keines der oben genannten Verfahren, da das Verhalten in den anderen Spalten nicht angegeben ist. (Das erste funktioniert in MySQL, wenn Sie es verwenden.)

Sie können die verschiedenen Felder abrufen und jedes Mal eine einzelne Zeile auswählen.

Auf einigen Plattformen (z. B. PostgreSQL, Oracle, T-SQL) kann dies direkt mithilfe von Fensterfunktionen erfolgen:

select *
from (
   select *,
          row_number() over (partition by field1 order by field2) as row_number
   from table
   ) as rows
where row_number = 1

Bei anderen (MySQL, SQLite) müssen Sie Unterabfragen schreiben, mit denen Sie die gesamte Tabelle mit sich selbst verbinden können ( Beispiel ). Dies wird daher nicht empfohlen.

Denis de Bernardy
quelle
10
Die Abfrage wird für mich nicht analysiert und gibt einen Fehler aus : The ranking function "row_number" must have an ORDER BY clause. Wir müssen order by-Klausel nach Partition nach field1 hinzufügen. Die richtige Abfrage lautet also select * from ( select *, row_number() over (partition by field1 order by orderbyFieldName) as row_number from table ) as rows where row_number = 1
Ankur-m
1
Vielen Dank! Ich war im gleichen Problem und die Lösung war die GROUP BY
Joaquin Iurchuk
2
Auch in Oracle (Oracle SQL Developer) können Sie nicht angeben select *, row_number() over (partition by field1 order by field2) as row_number from table. Sie müssen explizit Tabellenname / Alias ​​in der ausgewählten Abfrage verwendenselect **table**.*, row_number() over (partition by field1 order by field2) as row_number from table
meta4
1
@ Jarlh: Könnte ... heute sein. Wie Sie vielleicht bemerken, ist diese Antwort fast 7 Jahre alt, ein Zeitpunkt, an dem dies nicht der Fall war, soweit ich mich von hinten erinnern kann, als ich aktiv war. Sie können die Antwort gerne erneut markieren und / oder bearbeiten, wenn Sie dies für erforderlich halten.
Denis de Bernardy
2
select distinct on (field1) * from table;; funktioniert auch in PostgreSQL
Chilianu Bogdan
61

Aus der Formulierung Ihrer Frage geht hervor, dass Sie die unterschiedlichen Werte für ein bestimmtes Feld auswählen möchten und für jeden dieser Werte alle anderen Spaltenwerte in derselben Zeile aufgelistet werden sollen. Die meisten DBMS erlauben dies weder mit DISTINCTnoch GROUP BY, da das Ergebnis nicht bestimmt wird.

Stellen Sie sich das so vor: Wenn Ihr field1mehr als einmal vorkommt, wird der Wert von field2aufgelistet (vorausgesetzt, Sie haben den gleichen Wert für field1in zwei Zeilen, aber zwei unterschiedliche Werte für field2in diesen beiden Zeilen).

Sie können jedoch Aggregatfunktionen verwenden (explizit für jedes Feld, das angezeigt werden soll) und GROUP BYstatt DISTINCT:

SELECT field1, MAX(field2), COUNT(field3), SUM(field4), .... FROM table GROUP BY field1
Costi Ciudatu
quelle
4
+1 für diese Lösung. So was wir tun können SELECT field1, MIN(field2), MIN(field3), MIN(field4), .... FROM table GROUP BY field1, und field2, 3, 4 ,,, nicht als ganze Zahlen (oder andere Stellen) erforderlich, können sie Textfelder als auch sein
Stiel
Hat gut funktioniert, bis ich an einer booleschen Säule feststeckte. MIN (Dynamic) -Spaltenwerte werden in false geändert, auch wenn dies der Fall ist. Alle anderen Aggregatfunktionen, die vor 6 Minuten für die Adressierung von boolean - signonsridhar verfügbar waren. Summe (dynamisch) geändert falsch zu 1
signonsridhar
1
Ein großartiger Vorschlag führte mich zu meiner Lösung, die ich für universeller halte - werfen Sie einen Blick darauf!
Garrett Simpson
@signonsridhar wandelt Ihren Booleschen Wert in ein int um und verwendet sum; zBsum(cast(COL as int)) > 0
Drew
26

Wenn ich Ihr Problem richtig verstanden habe, ähnelt es einem, das ich gerade hatte. Sie möchten die Verwendbarkeit von DISTINCT auf ein bestimmtes Feld beschränken können, anstatt es auf alle Daten anzuwenden.

Wenn Sie GROUP BY ohne Aggregatfunktion verwenden, ist jedes Feld, in dem Sie GROUP BY verwenden, Ihre DISTINCT-Datei.

Wenn Sie Ihre Anfrage stellen:

SELECT * from table GROUP BY field1;

Es werden alle Ihre Ergebnisse basierend auf einer einzelnen Instanz von Feld1 angezeigt.

Zum Beispiel, wenn Sie eine Tabelle mit Name, Adresse und Stadt haben. Eine einzelne Person hat mehrere Adressen aufgezeichnet, aber Sie möchten nur eine einzige Adresse für die Person, die Sie wie folgt abfragen können:

SELECT * FROM persons GROUP BY name;

Das Ergebnis ist, dass nur eine Instanz dieses Namens mit ihrer Adresse angezeigt wird und die andere in der resultierenden Tabelle weggelassen wird. Achtung: Wenn Ihre Dateien atomare Werte wie Vorname und Nachname haben, die Sie nach beiden gruppieren möchten.

SELECT * FROM persons GROUP BY lastName, firstName;

Wenn zwei Personen denselben Nachnamen haben und Sie nur nach Nachnamen gruppieren, wird eine dieser Personen in den Ergebnissen nicht berücksichtigt. Sie müssen diese Dinge berücksichtigen. Hoffe das hilft.

Rocklandcitizen
quelle
Wie in der akzeptierten Antwort erwähnt, würde für die meisten Inkarnationen von SQL funktionieren
Garrett Simpson
15
SELECT  c2.field1 ,
        field2
FROM    (SELECT DISTINCT
                field1
         FROM   dbo.TABLE AS C
        ) AS c1
        JOIN dbo.TABLE AS c2 ON c1.field1 = c2.field1
Stürmisch
quelle
Warum gibt es, C aliaswenn es ohne es funktionieren kann? in der SchlangeFROM dbo.TABLE AS C
Talha
2
Ich glaube, das liegt an meiner Verwendung von RedGate SQLPrompt. So wie ich es konfiguriert habe, werden immer Aliase hinzugefügt - auch wenn dies nicht erforderlich ist. Es ist da "nur für den Fall"
Stormy
Das sah für mich vielversprechend aus, brachte aber immer noch alle Zeilen zurück, nicht das eindeutige Feld1. :(
Michael Fever
13

Das ist eine wirklich gute Frage. Ich habe hier bereits einige nützliche Antworten gelesen, aber wahrscheinlich kann ich eine genauere Erklärung hinzufügen.

Das Reduzieren der Anzahl der Abfrageergebnisse mit einer GROUP BY-Anweisung ist einfach, solange Sie keine zusätzlichen Informationen abfragen. Nehmen wir an, Sie haben die folgende Tabelle "Standorte".

--country-- --city--
 France      Lyon
 Poland      Krakow
 France      Paris
 France      Marseille
 Italy       Milano

Nun die Abfrage

SELECT country FROM locations
GROUP BY country

wird darin enden, dass:

--country--
 France
 Poland
 Italy

Allerdings die folgende Abfrage

SELECT country, city FROM locations
GROUP BY country

... wirft einen Fehler in MS SQL aus, denn wie kann Ihr Computer wissen, welche der drei französischen Städte "Lyon", "Paris" oder "Marseille" Sie im Feld rechts von "Frankreich" lesen möchten?

Um die zweite Abfrage zu korrigieren, müssen Sie diese Informationen hinzufügen. Eine Möglichkeit, dies zu tun, besteht darin, die Funktionen MAX () oder MIN () zu verwenden und den größten oder kleinsten Wert unter allen Kandidaten auszuwählen. MAX () und MIN () gelten nicht nur für numerische Werte, sondern vergleichen auch die alphabetische Reihenfolge der Zeichenfolgenwerte.

SELECT country, MAX(city) FROM locations
GROUP BY country

wird darin enden, dass:

--country-- --city--
 France      Paris
 Poland      Krakow
 Italy       Milano

oder:

SELECT country, MIN(city) FROM locations
GROUP BY country

wird darin enden, dass:

--country-- --city--
 France      Lyon
 Poland      Krakow
 Italy       Milano

Diese Funktionen sind eine gute Lösung, solange Sie Ihren Wert an beiden Enden der alphabetischen (oder numerischen) Reihenfolge auswählen können. Aber was ist, wenn dies nicht der Fall ist? Nehmen wir an, Sie benötigen einen Wert mit einem bestimmten Merkmal, z. B. beginnend mit dem Buchstaben 'M'. Jetzt wird es kompliziert.

Die einzige Lösung, die ich bisher finden konnte, besteht darin, Ihre gesamte Abfrage in eine Unterabfrage zu stellen und die zusätzliche Spalte außerhalb von Hand zu erstellen:

SELECT
     countrylist.*,
     (SELECT TOP 1 city
     FROM locations
     WHERE
          country = countrylist.country
          AND city like 'M%'
     )
FROM
(SELECT country FROM locations
GROUP BY country) countrylist

wird darin enden, dass:

--country-- --city--
 France      Marseille
 Poland      NULL
 Italy       Milano
Ulf Sanne
quelle
5

Tolle Frage @aryaxt - Sie können sagen, dass es eine großartige Frage war, weil Sie sie vor 5 Jahren gestellt haben und ich heute darauf gestoßen bin, um die Antwort zu finden!

Ich habe nur versucht, die akzeptierte Antwort zu bearbeiten, um dies einzuschließen, aber falls meine Bearbeitung es nicht schafft in:

Wenn Ihre Tabelle nicht so groß wäre und Ihr Primärschlüssel eine automatisch inkrementierende Ganzzahl wäre, könnten Sie Folgendes tun:

SELECT 
  table.*
FROM table
--be able to take out dupes later
LEFT JOIN (
  SELECT field, MAX(id) as id
  FROM table
  GROUP BY field
) as noDupes on noDupes.id = table.id
WHERE
  //this will result in only the last instance being seen
  noDupes.id is not NULL
Garrett Simpson
quelle
5

Versuchen

SELECT table.* FROM table 
WHERE otherField = 'otherValue'
GROUP BY table.fieldWantedToBeDistinct
limit x
Pedro Ramos
quelle
3

Sie können dies mit einer WITHKlausel tun .

Zum Beispiel:

WITH c AS (SELECT DISTINCT a, b, c FROM tableName)
SELECT * FROM tableName r, c WHERE c.rowid=r.rowid AND c.a=r.a AND c.b=r.b AND c.c=r.c

Auf diese Weise können Sie auch nur die in der WITHKlauselabfrage ausgewählten Zeilen auswählen .

user2225399
quelle
2

Für SQL Server können Sie den Funktionen dens_rank und zusätzliche Fensterfunktionen verwenden, um alle Zeilen UND Spalten mit doppelten Werten für bestimmte Spalten abzurufen. Hier ist ein Beispiel...

with t as (
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r1' union all
    select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r2' union all
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r3' union all
    select col1 = 'a', col2 = 'b', col3 = 'c', other = 'r4' union all
    select col1 = 'c', col2 = 'b', col3 = 'a', other = 'r5' union all
    select col1 = 'a', col2 = 'a', col3 = 'a', other = 'r6'
), tdr as (
    select 
        *, 
        total_dr_rows = count(*) over(partition by dr)
    from (
        select 
            *, 
            dr = dense_rank() over(order by col1, col2, col3),
            dr_rn = row_number() over(partition by col1, col2, col3 order by other)
        from 
            t
    ) x
)

select * from tdr where total_dr_rows > 1

Dies erfordert eine Zeilenanzahl für jede unterschiedliche Kombination von col1, col2 und col3.

dotjoe
quelle
zu kompliziert und spezifisch für eine Implementierung von SQL
Garrett Simpson
1
select min(table.id), table.column1
from table 
group by table.column1
KadoJ
quelle
Das hat bei mir funktioniert !! Wenn Sie fetch_array () verwenden, müssen Sie jede Zeile über eine Indexbezeichnung aufrufen, anstatt implizit den Zeilennamen aufzurufen. Es gibt nicht genug Zeichen, um das Beispiel aufzuschreiben, das ich habe: X Entschuldigung !!
Brandon Printiss
0
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30

In diesem ORDER BYBeispiel habe ich gerade ein ID-Feld hinzugefügt

SagarPPanchal
quelle
Wie in der akzeptierten Antwort erwähnt, würde für die meisten Inkarnationen von SQL funktionieren
Garrett Simpson
0

Fand dies an anderer Stelle hier, aber dies ist eine einfache Lösung, die funktioniert:

 WITH cte AS /* Declaring a new table named 'cte' to be a clone of your table */
 (SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY val1 DESC) AS rn
 FROM MyTable /* Selecting only unique values based on the "id" field */
 )
 SELECT * /* Here you can specify several columns to retrieve */
 FROM cte
 WHERE rn = 1
Michael Fever
quelle
Funktioniert für MSSQL
Michael Fever
-1

Fügen Sie GROUP BY zu dem Feld hinzu, in dem Sie nach Duplikaten suchen möchten, nach denen Ihre Abfrage möglicherweise aussieht

SELECT field1, field2, field3, ......   FROM table GROUP BY field1

Feld1 wird aktiviert, um doppelte Datensätze auszuschließen

oder Sie können wie abfragen

SELECT *  FROM table GROUP BY field1

Doppelte Datensätze von Feld1 werden von SELECT ausgeschlossen

iCodeCrew
quelle
1
Die GROUP BY-Klausel muss mit ausgewählten Feldern übereinstimmen. sonst wird es Fehler wiefiled2 must appear in the GROUP BY clause or be used in an aggregate function
Viuu -a
-2

Fügen Sie einfach alle Ihre Felder in die GROUP BY-Klausel ein.

Wayneh
quelle
3
Um dies zu einer guten Antwort zu machen, sollten Sie etwas detaillierter angeben, was Sie meinen.
Robbert
-2

Dies kann durch innere Abfrage erfolgen

$query = "SELECT * 
            FROM (SELECT field
                FROM table
                ORDER BY id DESC) as rows               
            GROUP BY field";
Zaheer Babar
quelle
2
Dies beantwortet nicht die Frage, das OP hat versucht, alle Daten der Tabelle
Garrett Simpson
-3
SELECT * from table where field in (SELECT distinct field from table)
Andrew
quelle
7
Das macht den Job nicht. Sie haben die eindeutige Spalte in der Unterabfrage ausgewählt, aber die where-Klausel ruft alle diese Spalten mit diesem Wert ab. Die Abfrage ist also so gut wie das Schreiben von 'select * from table', es sei denn, die Spalte 'field' ist eine eindeutige Spalte. In diesem Fall ist die Unterscheidung in dieser Spalte überhaupt nicht erforderlich.
Ankur-m
-3

SELECT DISTINCT FIELD1, FIELD2, FIELD3 FROM TABLE1 funktioniert, wenn die Werte aller drei Spalten in der Tabelle eindeutig sind.

Wenn Sie beispielsweise mehrere identische Werte für den Vornamen haben, der Nachname und andere Informationen in den ausgewählten Spalten jedoch unterschiedlich sind, wird der Datensatz in die Ergebnismenge aufgenommen.

Doris Gammenthaler
quelle
2
Dies beantwortet nicht die Frage, das OP hat versucht, alle Daten der Tabelle abzurufen, aber Zeilen mit Duplikaten eines einzelnen Feldes zu entfernen
Garrett Simpson
-3

Ich würde vorschlagen, zu verwenden

SELECT  * from table where field1 in 
(
  select distinct field1 from table
)

Auf diese Weise werden alle Datensätze zurückgegeben, wenn Sie in mehreren Zeilen denselben Wert in Feld1 haben.

Ioannis K.
quelle
1
Es ist nicht anders mit SELECT * FROM table;. Noch mehr Es ist langsam.
Shin Kim
Bitte versuchen Sie zuerst Ihre Antwort.
Sherif