SQL / mysql - Pilih yang berbeda / UNIK tetapi mengembalikan semua kolom?

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

Saya mencoba untuk mencapai pernyataan sql berikut tetapi saya ingin mengembalikan semua kolom apakah ini mungkin? Sesuatu seperti:

SELECT DISTINCT field1, * from table
aryaxt
sumber
12
Kenapa SELECT DISTINCT * FROM tabletidak bekerja untukmu?
ypercubeᵀᴹ
19
Jika tabel Anda memiliki PK, semua baris harus distinctdengan definisi. Jika Anda mencoba untuk hanya memilih DISTINCT field1tetapi entah bagaimana mengembalikan semua kolom lain apa yang harus terjadi untuk kolom-kolom yang memiliki lebih dari satu nilai untuk field1nilai tertentu ? Anda perlu menggunakan GROUP BYdan semacam agregasi di kolom lain misalnya.
Martin Smith
1
Jika Anda ingin baris berulang dan bukan hanya baris yang berbeda, hapus kata kunci yang berbeda.
Hyperboreus
2
Bisakah Anda memberikan contoh seperti apa hasil yang Anda harapkan? Sejauh ini, saya tidak dapat memahami permintaan yang Anda inginkan.
Rekursif
3
Berikut adalah jawaban dari pertanyaan serupa yang diajukan, Anda harus terlebih dahulu mendapatkan kolom yang berbeda dengan id mereka dan kemudian bergabung dengan tabel asli. PILIH PERBEDAAN pada satu kolom, kembalikan beberapa kolom lainnya
yadavr

Jawaban:

407

Anda mencari grup dengan:

select *
from table
group by field1

Yang terkadang dapat ditulis dengan pernyataan berbeda:

select distinct on field1 *
from table

Pada sebagian besar platform, tidak satu pun di atas akan berfungsi karena perilaku di kolom lain tidak ditentukan. (Yang pertama berfungsi di MySQL, jika itu yang Anda gunakan.)

Anda bisa mengambil bidang yang berbeda dan tetap memilih satu baris sewenang-wenang setiap kali.

Pada beberapa platform (misalnya PostgreSQL, Oracle, T-SQL) ini dapat dilakukan secara langsung menggunakan fungsi jendela:

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

Pada yang lain (MySQL, SQLite), Anda harus menulis subqueries yang akan membuat Anda bergabung dengan seluruh tabel dengan sendirinya ( contoh ), jadi tidak disarankan.

Denis de Bernardy
sumber
10
Query tidak akan mengurai bagi saya dan memberikan kesalahan: The ranking function "row_number" must have an ORDER BY clause. Kita perlu menambahkan pesanan dengan klausa setelah partisi dengan bidang1. Jadi permintaan yang benar adalah 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
Terima kasih! Saya berada di masalah yang sama dan solusinya adalah GROUP BY
Joaquin Iurchuk
2
Juga di Oracle (Pengembang Oracle SQL) Anda tidak dapat menentukan select *, row_number() over (partition by field1 order by field2) as row_number from table. Anda harus secara eksplisit menggunakan nama tabel / alias dalam kueri select **table**.*, row_number() over (partition by field1 order by field2) as row_number from table
pemilihan
1
@jarlh: Mungkin ... hari ini. Seperti yang mungkin Anda perhatikan, jawaban ini hampir 7 tahun, suatu titik waktu di mana itu tidak terjadi sejauh yang saya ingat dari belakang ketika saya aktif. Anda dapat melakukan retag dan / atau mengedit jawaban jika Anda merasa perlu.
Denis de Bernardy
2
select distinct on (field1) * from table; bekerja juga di PostgreSQL
Chilianu Bogdan
61

Dari ungkapan pertanyaan Anda, saya mengerti bahwa Anda ingin memilih nilai yang berbeda untuk bidang tertentu dan untuk setiap nilai tersebut memiliki semua nilai kolom lainnya di baris yang sama terdaftar. Sebagian besar DBMS tidak akan mengizinkan ini dengan tidak DISTINCTjuga GROUP BY, karena hasilnya tidak ditentukan.

Pikirkan seperti ini: jika Anda field1muncul lebih dari satu kali, nilai berapa yang field2akan dicantumkan (mengingat bahwa Anda memiliki nilai yang sama field1dalam dua baris tetapi dua nilai berbeda field2dalam dua baris itu).

Namun Anda dapat menggunakan fungsi agregat (secara eksplisit untuk setiap bidang yang ingin ditampilkan) dan menggunakan GROUP BYalih-alih DISTINCT:

SELECT field1, MAX(field2), COUNT(field3), SUM(field4), .... FROM table GROUP BY field1
Costi Ciudatu
sumber
4
+1 untuk solusi ini. Jadi bisa kita lakukan SELECT field1, MIN(field2), MIN(field3), MIN(field4), .... FROM table GROUP BY field1, dan field2, 3, 4 ,,, tidak diharuskan menjadi bilangan bulat (atau digit lainnya), mereka juga bisa menjadi ladang char
menguntit
Bekerja dengan baik sampai saya terjebak di kolom boolean. Nilai kolom MIN (Dynamic) dapat dimodifikasi menjadi false walaupun itu benar .. Fungsi agregat lain yang tersedia untuk mengatasi boolean - signonsridhar 6 mnt yang lalu. Jumlah (dinamis) berubah salah menjadi 1
signonsridhar
1
Saran yang bagus, membawa saya ke solusi yang menurut saya lebih universal - lihat!
Garrett Simpson
@signonsridhar melemparkan boolean Anda ke int dan menggunakan jumlah; misalnyasum(cast(COL as int)) > 0
Drew
26

Jika saya memahami masalah Anda dengan benar, itu mirip dengan yang baru saja saya alami. Anda ingin dapat membatasi kegunaan DISTINCT untuk bidang yang ditentukan, daripada menerapkannya ke semua data.

Jika Anda menggunakan GROUP BY tanpa fungsi agregat, bidang mana pun yang Anda GROUP BY akan menjadi DISTINCT Anda.

Jika Anda mengajukan pertanyaan:

SELECT * from table GROUP BY field1;

Ini akan menampilkan semua hasil Anda berdasarkan satu instance dari field1.

Misalnya, jika Anda memiliki tabel dengan nama, alamat, dan kota. Satu orang memiliki beberapa alamat yang direkam, tetapi Anda hanya ingin satu alamat untuk orang tersebut, Anda dapat meminta sebagai berikut:

SELECT * FROM persons GROUP BY name;

Hasilnya adalah bahwa hanya satu instance dari nama itu akan muncul dengan alamatnya, dan yang lainnya akan dihilangkan dari tabel yang dihasilkan. Perhatian: jika arsip Anda memiliki nilai atom seperti firstName, lastName Anda ingin dikelompokkan berdasarkan keduanya.

SELECT * FROM persons GROUP BY lastName, firstName;

karena jika dua orang memiliki nama belakang yang sama dan Anda hanya mengelompokkan berdasarkan nama belakang, salah satu dari mereka akan dihilangkan dari hasilnya. Anda perlu mempertimbangkan hal-hal itu. Semoga ini membantu.

rocklandcitizen
sumber
Seperti disebutkan dalam jawaban yang diterima, akan bekerja untuk sebagian besar inkarnasi SQL - hanya untuk MYSQL
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
Badai
sumber
Kenapa ada C aliassaat itu bisa bekerja tanpanya? sejalanFROM dbo.TABLE AS C
Talha
2
Saya percaya ini karena saya menggunakan RedGate SQLPrompt. Cara saya mengonfigurasinya, selalu menambahkan alias - bahkan jika tidak perlu. Itu ada "berjaga-jaga"
Stormy
Ini tampak menjanjikan bagi saya tetapi masih mengembalikan semua baris, bukan bidang yang berbeda1. :(
Michael Fever
13

Itu pertanyaan yang sangat bagus. Saya sudah membaca beberapa jawaban yang bermanfaat di sini, tetapi mungkin saya dapat menambahkan penjelasan yang lebih tepat.

Mengurangi jumlah hasil kueri dengan pernyataan GROUP BY mudah selama Anda tidak meminta informasi tambahan. Mari kita asumsikan Anda mendapat 'lokasi' tabel berikut.

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

Sekarang kueri

SELECT country FROM locations
GROUP BY country

akan menghasilkan:

--country--
 France
 Poland
 Italy

Namun, pertanyaan berikut

SELECT country, city FROM locations
GROUP BY country

... melempar kesalahan dalam MS SQL, karena bagaimana komputer Anda bisa tahu yang mana dari tiga kota Prancis "Lyon", "Paris" atau "Marseille" yang ingin Anda baca di lapangan di sebelah kanan "Prancis"?

Untuk memperbaiki kueri kedua, Anda harus menambahkan informasi ini. Salah satu cara untuk melakukan ini adalah dengan menggunakan fungsi MAX () atau MIN (), memilih nilai terbesar atau terkecil di antara semua kandidat. MAX () dan MIN () tidak hanya berlaku untuk nilai numerik, tetapi juga membandingkan urutan abjad dari nilai string.

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

akan menghasilkan:

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

atau:

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

akan menghasilkan:

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

Fungsi-fungsi ini adalah solusi yang baik selama Anda baik-baik saja dengan memilih nilai Anda dari kedua ujung urutan abjad (atau numerik). Tetapi bagaimana jika ini tidak terjadi? Mari kita asumsikan bahwa Anda memerlukan nilai dengan karakteristik tertentu, misalnya dimulai dengan huruf 'M'. Sekarang segalanya menjadi rumit.

Satu-satunya solusi yang bisa saya temukan sejauh ini adalah dengan memasukkan seluruh permintaan Anda ke dalam subquery, dan membangun kolom tambahan di luarnya dengan tangan:

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

akan menghasilkan:

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

Pertanyaan bagus @aryaxt - Anda dapat mengatakan bahwa itu adalah pertanyaan yang hebat karena Anda menanyakannya 5 tahun yang lalu dan saya menemukannya hari ini mencoba menemukan jawabannya!

Saya baru saja mencoba mengedit jawaban yang diterima untuk memasukkan ini, tetapi kalau-kalau edit saya tidak membuatnya:

Jika meja Anda tidak sebesar itu, dan dengan asumsi kunci utama Anda adalah bilangan bulat yang bertambah secara otomatis, Anda bisa melakukan sesuatu seperti ini:

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
sumber
5

Mencoba

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

Anda bisa melakukannya dengan WITHklausa.

Sebagai contoh:

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

Ini juga memungkinkan Anda untuk memilih hanya baris yang dipilih dalam WITHpermintaan klausa.

pengguna2225399
sumber
2

Untuk SQL Server, Anda dapat menggunakan fungsi dense_rank dan jendela tambahan untuk mendapatkan semua baris DAN kolom dengan nilai duplikat pada kolom yang ditentukan. Berikut ini sebuah contoh ...

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

Ini mengambil jumlah baris untuk setiap kombinasi col1, col2, dan col3 yang berbeda.

dotjoe
sumber
terlalu rumit dan spesifik untuk satu implementasi SQL
Garrett Simpson
1
select min(table.id), table.column1
from table 
group by table.column1
KadoJ
sumber
Ini berhasil bagi saya !! Perlu dicatat, jika Anda menggunakan fetch_array () maka Anda perlu memanggil setiap baris melalui label indeks daripada secara implisit memanggil nama baris. Tidak ada cukup karakter di sini untuk saya tuliskan contoh yang saya miliki: X maaf !!
Brandon Printiss
0
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30

di ORDER BYsaya baru saja memberikan contoh di sini, Anda juga dapat menambahkan bidang ID di ini

SagarPPanchal
sumber
Seperti disebutkan dalam jawaban yang diterima, akan berfungsi untuk sebagian besar inkarnasi SQL - hanya untuk MYSQL
Garrett Simpson
0

Menemukan ini di tempat lain di sini tetapi ini adalah solusi sederhana yang berfungsi:

 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
sumber
Bekerja untuk MSSQL
Michael Fever
-1

Tambahkan GROUP BY ke bidang yang ingin Anda periksa duplikatnya

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

field1 akan diperiksa untuk mengecualikan catatan duplikat

atau Anda dapat meminta like

SELECT *  FROM table GROUP BY field1

rekaman duplikat field1 dikecualikan dari SELECT

iCodeCrew
sumber
1
Klausa GROUP BY harus cocok dengan bidang yang dipilih. selain itu ia akan melempar kesalahan sepertifiled2 must appear in the GROUP BY clause or be used in an aggregate function
Viuu-a
-2

Cukup sertakan semua bidang Anda dalam klausa GROUP BY.

wayneh
sumber
3
Untuk membuat ini jawaban yang baik, Anda harus memasukkan sedikit lebih detail tentang apa yang Anda maksudkan.
Robbert
-2

Itu bisa dilakukan dengan permintaan dalam

$query = "SELECT * 
            FROM (SELECT field
                FROM table
                ORDER BY id DESC) as rows               
            GROUP BY field";
Zaheer Babar
sumber
2
Ini tidak menjawab pertanyaan, OP sedang mencoba untuk mendapatkan semua data dari tabel tetapi menghapus baris yang berisi duplikat dari satu bidang
Garrett Simpson
-3
SELECT * from table where field in (SELECT distinct field from table)
Andrew
sumber
7
Itu tidak akan berhasil. Anda telah memilih kolom yang berbeda di subquery tetapi klausa di mana mendapatkan semua kolom dengan nilai itu. Jadi kueri sama baiknya dengan menulis 'pilih * dari tabel' kecuali jika kolom 'bidang' adalah kolom unik yang dalam hal ini perbedaan pada kolom itu tidak diperlukan sama sekali.
Ankur-m
-3

SELECT DISTINCT FIELD1, FIELD2, FIELD3 FROM TABLE1 berfungsi jika nilai ketiga kolom unik dalam tabel.

Jika, misalnya, Anda memiliki beberapa nilai identik untuk nama depan, tetapi nama belakang dan informasi lain di kolom yang dipilih berbeda, catatan akan dimasukkan dalam hasil yang ditetapkan.

Doris Gammenthaler
sumber
2
Ini tidak menjawab pertanyaan, OP sedang mencoba untuk mendapatkan semua data dari tabel tetapi menghapus baris yang berisi duplikat dari satu bidang
Garrett Simpson
-3

Saya sarankan menggunakan

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

dengan cara ini jika Anda memiliki nilai yang sama di field1 di beberapa baris, semua catatan akan dikembalikan.

Ioannis K
sumber
1
Tidak berbeda dengan SELECT * FROM table;. Bahkan lebih lambat.
Shin Kim
Tolong, coba jawaban Anda terlebih dahulu.
Sherif