วิธีการเลือกระเบียนเฉพาะโดย SQL


89

เมื่อฉันดำเนินการ "SELECT * FROM table" ฉันได้ผลลัพธ์ดังนี้:

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

อย่างที่คุณเห็นมีระเบียน dup จาก column2 (รายการ 1 ถูก dupped) แล้วฉันจะได้ผลลัพธ์เช่นนี้ได้อย่างไร:

1 item1 data1
2 item2 data3
3 item3 data4

มีเพียงระเบียนเดียวเท่านั้นที่จะถูกส่งคืนจากการทำซ้ำพร้อมกับระเบียนที่ไม่ซ้ำ


รายการ 1 ไม่ซ้ำกันในทางเทคนิค ดังที่แสดงไว้แถวที่ 1 และ 2 เป็นข้อสังเกตเฉพาะ จะเป็นอย่างไรถ้าคุณต้องการเก็บแถว 2 ไม่ใช่แถวที่ 1
Cybernetic

คำตอบ:


107

ด้วยdistinctคำสำคัญที่มีชื่อคอลัมน์เดียวและหลายคอลัมน์คุณจะได้รับระเบียนที่แตกต่างกัน:

SELECT DISTINCT column 1, column 2, ...
FROM table_name;

15
เป็นไปได้ไหมว่าคำตอบนั้นผิดจริง DISTINCT ถูกนำไปใช้กับคอลัมน์ที่เลือกทั้งหมด (อย่างน้อยใน DB2) ซึ่งจะยังคงส่งคืนค่าที่ซ้ำกันในแต่ละคอลัมน์
Konstantin

26

DISTINCTหากคุณจำเป็นต้องซ้ำกันลบแล้วใช้เท่านั้น GROUP BYควรใช้เพื่อใช้ตัวดำเนินการรวมกับแต่ละกลุ่ม

GROUP BY v DISTINCT


11

ขึ้นอยู่กับแถวที่คุณต้องการส่งคืนสำหรับแต่ละรายการที่ไม่ซ้ำกัน ข้อมูลของคุณดูเหมือนจะระบุค่าข้อมูลขั้นต่ำดังนั้นในอินสแตนซ์นี้สำหรับ SQL Server

SELECT item, min(data)
FROM  table
GROUP BY item

11

มี 4 วิธีที่คุณสามารถใช้ได้:

  1. แตกต่าง
  2. GROUP BY
  3. แบบสอบถามย่อย
  4. นิพจน์ตารางทั่วไป (CTE) กับ ROW_NUMBER ()

พิจารณาตัวอย่างต่อไปนี้TABLEพร้อมข้อมูลการทดสอบ:

/** 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

ตัวเลือกที่ 1: เลือก DISTINCT

นี่เป็นวิธีที่ง่ายและตรงไปตรงมาที่สุด แต่ยังเป็นวิธีที่ จำกัด ที่สุด:

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

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

ตัวเลือกที่ 2: GROUP BY

การจัดกลุ่มช่วยให้คุณสามารถเพิ่มข้อมูลรวมเช่นmin(id), max(id), count(*)ฯลฯ :

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|
*/

ตัวเลือกที่ 3: แบบสอบถามย่อย

การใช้เคียวรีย่อยคุณสามารถระบุแถวที่ซ้ำกันเพื่อละเว้นก่อนจากนั้นกรองออกในแบบสอบถามภายนอกด้วยWHERE NOT IN (subquery)โครงสร้าง:

/** 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|
*/

ตัวเลือกที่ 4: นิพจน์ตารางทั่วไปกับ ROW_NUMBER ()

ใน Common Table Expression (CTE) เลือก ROW_NUMBER () แบ่งพาร์ติชันตามคอลัมน์กลุ่มและเรียงลำดับตามลำดับที่ต้องการ จากนั้นเลือกเฉพาะระเบียนที่มี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|
*/

6

เพียงใช้การรวมภายในเนื่องจากการจัดกลุ่มโดยจะไม่ทำงานกับหลายคอลัมน์ที่บอกว่าไม่มีอยู่ในฟังก์ชันการรวม

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;

นั่นคือคำตอบสำหรับคำถามที่แตกต่างออกไปซึ่งอาจเป็นคำถามที่ควรติดแท็กมากที่สุด -n-per-group
a_horse_with_no_name

นี่และคำตอบของ Dave Baker เป็นคำตอบที่ถูกต้องสำหรับคำถาม SO ข้อดีของโซลูชันนี้คืออนุญาตให้เลือกแถวที่มีเฉพาะคอลัมน์ที่แตกต่างกันที่ระบุไว้เท่านั้นและต้องกำหนดรหัส MIN (id) AS หนึ่งคอลัมน์เพื่อเลือกคอลัมน์ที่ระบุเพียงคอลัมน์เดียว
giordano

1

ฉันพบว่าถ้าฉันไม่สามารถใช้ DISTINCT ไม่ว่าด้วยเหตุผลใดก็ตาม GROUP BY จะทำงาน


1

ในการรับคอลัมน์ทั้งหมดในผลลัพธ์ของคุณคุณต้องวางบางสิ่งเป็น:

SELECT distinct a, Table.* FROM Table

มันจะวางaเป็นคอลัมน์แรกและส่วนที่เหลือจะเป็นคอลัมน์ทั้งหมดในลำดับเดียวกับคำจำกัดความของคุณ นี่คือคอลัมน์aจะถูกทำซ้ำ


1
คุณแน่ใจเกี่ยวกับเรื่องนี้หรือไม่? ฉันลองสิ่งนี้ใน w3schools และมันกลับมาเหมือนกันกับ SELECT * ยกเว้น a เป็นคอลัมน์แรก
ประหลาด

@Freakishly ใช่และนั่นคือสิ่งที่บอกว่าจะทำในคำตอบของฉัน: /
htafoya

วิธีนี้จะใช้ไม่ได้คุณไม่สามารถเลือก * หลังจากที่แตกต่างกันได้ (คุณจะได้รับข้อผิดพลาด 1064 - ข้อผิดพลาดในไวยากรณ์ SQL ของคุณ)
tim.baker

@Mohsinkhan ดีฉันลืมวางที่คุณต้องเขียนชื่อโต๊ะ อย่างใดเมื่อฉันเขียนสิ่งนี้มันได้ผล แต่ฉันเพิ่งทดสอบตอนนี้และมันก็ไม่ได้ไม่มีชื่อตารางก่อนหน้า *
htafoya

2
สิ่งนี้เหมือนกับselect distinct * from ...
a_horse_with_no_name

-4

เลือก Eff_st จาก (เลือก EFF_ST, ROW_NUMBER () ทับ (PARTITION BY eff_st) XYZ - จาก ABC.CODE_DIM

) โดยที่ XYZ = 1 คำสั่งโดย EFF_ST ดึงข้อมูล 5 แถวแรกเท่านั้น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.