Cara memilih catatan unik dengan SQL

89

Ketika saya melakukan "SELECT * FROM table" saya mendapatkan hasil seperti di bawah ini:

1 item1 data1
2 item1 data2
3 item2 data3
4 item3 data4

Seperti yang Anda lihat, ada catatan dup dari kolom2 (item1 diduplikasi). Jadi bagaimana saya bisa mendapatkan hasil seperti ini:

1 item1 data1
2 item2 data3
3 item3 data4

Hanya satu catatan yang dikembalikan dari duplikat, bersama dengan catatan unik lainnya.

Yinan
sumber
Item 1 tidak diduplikasi secara teknis. Seperti yang ditunjukkan, baris 1 dan 2 adalah pengamatan unik. Bagaimana jika Anda ingin mempertahankan baris 2 dan bukan baris 1?
Cybernetic

Jawaban:

107

Dengan distinctkata kunci dengan nama kolom tunggal dan ganda, Anda mendapatkan catatan yang berbeda:

SELECT DISTINCT column 1, column 2, ...
FROM table_name;
mjallday
sumber
15
Mungkinkah jawabannya salah? DISTINCT diterapkan ke semua kolom yang dipilih (setidaknya pada DB2), yang masih akan mengembalikan nilai duplikat di kolom individual.
Konstantin
26

Jika Anda hanya perlu menghapus duplikat, gunakan DISTINCT. GROUP BYharus digunakan untuk menerapkan operator agregat ke setiap grup

GRUP BERDASARKAN v DISTINCT

rahul
sumber
11

Itu tergantung pada baris mana yang ingin Anda kembalikan untuk setiap item unik. Data Anda tampaknya menunjukkan nilai data minimum jadi dalam contoh ini untuk SQL Server.

SELECT item, min(data)
FROM  table
GROUP BY item
Dave Barker
sumber
11

Ada 4 metode yang dapat Anda gunakan:

  1. BERBEDA
  2. GRUP OLEH
  3. Subquery
  4. Ekspresi Tabel Umum (CTE) dengan ROW_NUMBER ()

Pertimbangkan contoh berikut TABLEdengan data uji:

/** Create test table */
CREATE TEMPORARY TABLE dupes(word text, num int, id int);

/** Add test data with duplicates */
INSERT INTO dupes(word, num, id)
VALUES ('aaa', 100, 1)
      ,('bbb', 200, 2)
      ,('ccc', 300, 3)
      ,('bbb', 400, 4)
      ,('bbb', 200, 5)     -- duplicate
      ,('ccc', 300, 6)     -- duplicate
      ,('ddd', 400, 7)
      ,('bbb', 400, 8)     -- duplicate
      ,('aaa', 100, 9)     -- duplicate
      ,('ccc', 300, 10);   -- duplicate

Opsi 1: PILIH DISTINCT

Ini adalah cara yang paling sederhana dan mudah, tetapi juga cara yang paling terbatas:

SELECT DISTINCT word, num 
FROM    dupes
ORDER BY word, num;

/*
word|num|
----|---|
aaa |100|
bbb |200|
bbb |400|
ccc |300|
ddd |400|
*/

Opsi 2: KELOMPOKKAN OLEH

Pengelompokan memungkinkan Anda untuk menambahkan data dikumpulkan, seperti min(id), max(id), count(*), dll:

SELECT  word, num, min(id), max(id), count(*)
FROM    dupes
GROUP BY word, num
ORDER BY word, num;

/*
word|num|min|max|count|
----|---|---|---|-----|
aaa |100|  1|  9|    2|
bbb |200|  2|  5|    2|
bbb |400|  4|  8|    2|
ccc |300|  3| 10|    3|
ddd |400|  7|  7|    1|
*/

Opsi 3: Subkueri

Dengan menggunakan subkueri, Anda dapat mengidentifikasi baris duplikat untuk diabaikan terlebih dahulu, lalu memfilternya di kueri luar dengan WHERE NOT IN (subquery)konstruksi:

/** Find the higher id values of duplicates, distinct only added for clarity */
    SELECT  distinct d2.id
    FROM    dupes d1
        INNER JOIN dupes d2 ON d2.word=d1.word AND d2.num=d1.num
    WHERE d2.id > d1.id

/*
id|
--|
 5|
 6|
 8|
 9|
10|
*/

/** Use the previous query in a subquery to exclude the dupliates with higher id values */
SELECT  *
FROM    dupes
WHERE   id NOT IN (
    SELECT  d2.id
    FROM    dupes d1
        INNER JOIN dupes d2 ON d2.word=d1.word AND d2.num=d1.num
    WHERE d2.id > d1.id
)
ORDER BY word, num;

/*
word|num|id|
----|---|--|
aaa |100| 1|
bbb |200| 2|
bbb |400| 4|
ccc |300| 3|
ddd |400| 7|
*/

Opsi 4: Ekspresi Tabel Umum dengan ROW_NUMBER ()

Di Common Table Expression (CTE), pilih ROW_NUMBER (), yang dipartisi menurut kolom grup dan diurutkan dalam urutan yang diinginkan. Kemudian PILIH hanya record yang memiliki ROW_NUMBER() = 1:

WITH CTE AS (
    SELECT  *
           ,row_number() OVER(PARTITION BY word, num ORDER BY id) AS row_num
    FROM    dupes
)
SELECT  word, num, id 
FROM    cte
WHERE   row_num = 1
ORDER BY word, num;

/*
word|num|id|
----|---|--|
aaa |100| 1|
bbb |200| 2|
bbb |400| 4|
ccc |300| 3|
ddd |400| 7|
*/
isapir
sumber
6

cukup gunakan gabungan dalam karena kelompok oleh tidak akan berfungsi dengan beberapa kolom yang mengatakan tidak terdapat dalam fungsi agregat.

SELECT a.*
FROM yourtable a
INNER JOIN 
  (SELECT yourcolumn,
    MIN(id) as id
  FROM yourtable 
  GROUP BY yourcolumn
) AS b
  ON a.yourcolumn= b.yourcolumn
  AND a.id = b.id;
Ankit Kashyap
sumber
Itu adalah jawaban untuk pertanyaan yang berbeda, mungkin pertanyaan yang harus diberi tag dengan greatest-n-per-group
a_horse_with_no_name
Ini dan solusi Dave Baker adalah solusi yang tepat untuk pertanyaan SO. Keuntungan dari solusi ini adalah memungkinkan untuk memilih baris dengan hanya beberapa kolom tertentu yang berbeda dan satu kolom MIN (id) AS id harus ditentukan untuk memilih hanya satu dari beberapa kolom yang ditentukan.
giordano
1

Saya menemukan bahwa jika saya tidak dapat menggunakan DISTINCT untuk alasan apa pun, maka GROUP BY akan berfungsi.

John Hamelink
sumber
1

Untuk mendapatkan semua kolom dalam hasil Anda, Anda perlu menempatkan sesuatu sebagai:

SELECT distinct a, Table.* FROM Table

itu akan menempatkan a sebagai kolom pertama dan sisanya akan menjadi SEMUA kolom dalam urutan yang sama seperti definisi Anda. Artinya, kolom a akan diulang.

htafoya.dll
sumber
1
Apa kau yakin tentang ini? Saya mencoba ini di w3schools dan hasilnya sama dengan SELECT *, kecuali kolom pertama adalah
Freakishly
@ Anehnya ya dan itulah yang dikatakannya akan dilakukan dalam jawaban saya: /
htafoya
Ini tidak akan berhasil, Anda tidak dapat memilih * setelah perbedaan seperti itu (Anda akan mendapatkan kesalahan 1064 - Kesalahan dalam sintaks SQL Anda)
tim.baker
@Mohsinkhan baik saya lupa tempat Anda perlu menulis nama tabel. Entah bagaimana ketika saya menulis ini, itu berhasil tetapi saya baru saja mengujinya sekarang dan itu tidak terjadi tanpa nama tabel sebelum *
htafoya
2
Ini persis sama denganselect distinct * from ...
a_horse_with_no_name
-4

Pilih Eff_st dari (pilih EFF_ST, ROW_NUMBER () over (PARTITION BY eff_st) XYZ - dari ABC.CODE_DIM

) di mana XYZ = 1 pesanan dengan EFF_ST mengambil 5 baris pertama saja

Shailendra Singhai
sumber