SQL / mysql - เลือกที่แตกต่างกัน / UNIQUE แต่กลับคอลัมน์ทั้งหมด?


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

ฉันพยายามที่จะบรรลุคำสั่ง sql ต่อไปนี้ แต่ฉันต้องการให้มันส่งคืนคอลัมน์ทั้งหมดเป็นไปได้หรือไม่ สิ่งที่ต้องการ:

SELECT DISTINCT field1, * from table

12
ทำไมถึงไม่SELECT DISTINCT * FROM tableได้ผลสำหรับคุณ
ypercubeᵀᴹ

19
หากตารางของคุณมี PK ทุกแถวควรเป็นไปdistinctตามคำจำกัดความ หากคุณพยายามที่จะเลือกDISTINCT field1แต่อย่างใดกลับคอลัมน์อื่น ๆ ทั้งหมดสิ่งที่ควรเกิดขึ้นสำหรับคอลัมน์เหล่านั้นที่มีมากกว่าหนึ่งค่าสำหรับfield1ค่าเฉพาะหรือไม่ คุณจะต้องใช้GROUP BYและรวมการเรียงลำดับบางอย่างในคอลัมน์อื่น ๆ เช่น
Martin Smith

1
หากคุณต้องการแถวซ้ำและไม่เพียง แต่แถวที่แตกต่างกันให้ลบคำสำคัญที่แตกต่างกัน
Hyperboreus

2
คุณยกตัวอย่างสิ่งที่คุณคาดหวังว่าผลลัพธ์จะเป็นอย่างไร จนถึงตอนนี้ฉันไม่สามารถเข้าใจคำค้นหาที่คุณต้องการได้
เรียกซ้ำ

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

คำตอบ:


407

คุณกำลังมองหากลุ่มโดย:

select *
from table
group by field1

ซึ่งบางครั้งสามารถเขียนด้วยคำสั่งที่แตกต่าง:

select distinct on field1 *
from table

บนแพลตฟอร์มส่วนใหญ่อย่างไรก็ตามสิ่งใด ๆ ข้างต้นจะไม่ทำงานเนื่องจากพฤติกรรมในคอลัมน์อื่น ๆ นั้นไม่ได้รับการระบุ (ผลงานชิ้นแรกใน MySQL ถ้านั่นคือสิ่งที่คุณใช้)

คุณสามารถดึงเขตข้อมูลที่แตกต่างกันและติดเพื่อเลือกแถวใดก็ได้ในแต่ละครั้ง

ในบางแพลตฟอร์ม (เช่น PostgreSQL, Oracle, T-SQL) สามารถทำได้โดยตรงโดยใช้ฟังก์ชั่นหน้าต่าง:

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

ในอื่น ๆ (MySQL, SQLite) คุณจะต้องเขียนแบบสอบถามย่อยที่จะทำให้คุณเข้าร่วมตารางทั้งหมดด้วยตัวเอง ( ตัวอย่าง ) ดังนั้นจึงไม่แนะนำ


10
The ranking function "row_number" must have an ORDER BY clauseแบบสอบถามจะไม่แยกสำหรับฉันและให้ข้อผิดพลาด: เราจำเป็นต้องเพิ่มคำสั่งตามข้อหลังพาร์ทิชันโดย field1 ดังนั้นคำถามที่ถูกต้องจะเป็น 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
ขอบคุณ! ฉันอยู่ในปัญหาเดียวกันและวิธีแก้ไขคือ GROUP BY
Joaquin Iurchuk

2
นอกจากนี้ใน Oracle (Oracle SQL Developer) คุณไม่สามารถระบุselect *, row_number() over (partition by field1 order by field2) as row_number from tableได้ คุณต้องใช้ชื่อตาราง / นามแฝงในแบบสอบถามแบบใช้เลือกข้อมูลselect **table**.*, row_number() over (partition by field1 order by field2) as row_number from table
meta4

1
@jarlh: อาจเป็น ... วันนี้ อย่างที่คุณอาจสังเกตเห็นคำตอบนี้มีอายุเกือบ 7 ปีซึ่งเป็นช่วงเวลาที่ไม่เป็นเช่นนั้นเพราะฉันสามารถจำได้จากหลังเมื่อฉันยังทำงานอยู่ คุณยินดีที่จะติดแท็กใหม่และ / หรือแก้ไขคำตอบหากคุณรู้สึกว่าจำเป็น
เดนิสเดอเบอร์นาดี

2
select distinct on (field1) * from table; ยังทำงานใน PostgreSQL
Chilianu Bogdan

61

จากการใช้คำถามของคุณฉันเข้าใจว่าคุณต้องการเลือกค่าที่แตกต่างสำหรับเขตข้อมูลที่กำหนดและสำหรับแต่ละค่าดังกล่าวเพื่อให้ค่าคอลัมน์อื่น ๆ ทั้งหมดอยู่ในแถวเดียวกัน DBMSs ส่วนใหญ่จะไม่ปล่อยให้เรื่องนี้มีค่าDISTINCTมิได้GROUP BYเพราะผลที่ไม่ได้กำหนด

ลองคิดแบบนี้ดู: หากคุณfield1เกิดขึ้นมากกว่าหนึ่งครั้งfield2จะมีการระบุมูลค่าใดไว้ (เนื่องจากคุณมีค่าเท่ากันfield1ในสองแถว แต่มีค่าแตกต่างกันสองค่าfield2ในสองแถวนั้น)

อย่างไรก็ตามคุณสามารถใช้ฟังก์ชั่นรวม (ชัดเจนสำหรับทุกฟิลด์ที่คุณต้องการให้แสดง) และใช้GROUP BYแทนDISTINCT:

SELECT field1, MAX(field2), COUNT(field3), SUM(field4), .... FROM table GROUP BY field1

4
+1 สำหรับโซลูชันนี้ ดังนั้นเราสามารถทำได้SELECT field1, MIN(field2), MIN(field3), MIN(field4), .... FROM table GROUP BY field1และ field2, 3, 4 ,,, ไม่จำเป็นต้องเป็นจำนวนเต็ม (หรือตัวเลขอื่น ๆ ) พวกเขาสามารถเป็นช่องถ่านได้เช่นกัน
stalk

ทำงานได้ดีจนกระทั่งฉันติดอยู่ที่คอลัมน์บูลีน ค่าคอลัมน์ MIN (ไดนามิก) ถูกปรับเปลี่ยนเป็นเท็จแม้ว่าจะเป็นจริง .. ฟังก์ชันการรวมอื่น ๆ ใด ๆ ที่สามารถระบุบูลีน - signonsridhar 6 นาทีที่ผ่านมา Sum (ไดนามิก) เปลี่ยน false เป็น 1
signonsridhar

1
คำแนะนำที่ดีนำฉันไปสู่โซลูชันซึ่งฉันคิดว่าเป็นสากลมากขึ้น - ลองดูสิ!
Garrett Simpson

@signonsridhar ส่งบูลีนของคุณไปเป็น int และใช้ผลรวม; เช่นsum(cast(COL as int)) > 0
Drew

26

ถ้าฉันเข้าใจปัญหาของคุณอย่างถูกต้องมันก็คล้ายกับที่ฉันเพิ่งเจอ คุณต้องการที่จะสามารถ จำกัด การใช้งานของ DISTINCT ในฟิลด์ที่ระบุแทนที่จะใช้กับข้อมูลทั้งหมด

หากคุณใช้ GROUP BY โดยไม่มีฟังก์ชั่นรวมซึ่งฟิลด์ใดที่คุณ GROUP BY จะถูกยื่น DISTINCT ของคุณ

หากคุณทำแบบสอบถามของคุณ:

SELECT * from table GROUP BY field1;

มันจะแสดงผลลัพธ์ทั้งหมดของคุณขึ้นอยู่กับอินสแตนซ์เดียวของ field1

ตัวอย่างเช่นหากคุณมีตารางที่มีชื่อที่อยู่และเมือง บุคคลเดียวมีหลายที่อยู่ที่บันทึกไว้ แต่คุณเพียงต้องการที่อยู่เดียวสำหรับบุคคลคุณสามารถสอบถามดังนี้:

SELECT * FROM persons GROUP BY name;

ผลลัพธ์จะเป็นเพียงอินสแตนซ์เดียวของชื่อนั้นที่จะปรากฏพร้อมที่อยู่และอีกอันจะถูกตัดออกจากตารางผลลัพธ์ ข้อควรระวัง: หากไฟล์ของคุณมีค่าปรมาณูเช่นชื่อ, นามสกุลคุณต้องการจัดกลุ่มตามทั้งสองอย่าง

SELECT * FROM persons GROUP BY lastName, firstName;

เพราะถ้าคนสองคนมีนามสกุลเหมือนกันและคุณจัดกลุ่มตามนามสกุลคนหนึ่งในนั้นจะถูกตัดออกจากผลลัพธ์ คุณต้องคำนึงถึงสิ่งเหล่านั้นด้วย หวังว่านี่จะช่วยได้


ดังที่กล่าวไว้ในคำตอบที่ได้รับการยอมรับจะใช้งานได้กับ SQL ส่วนใหญ่ - เฉพาะสำหรับ 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

ทำไมถึงมีC aliasเมื่อมันสามารถทำงานได้โดยปราศจากมัน? ในบรรทัดFROM dbo.TABLE AS C
Talha

2
ฉันเชื่อว่านี่เป็นเพราะการใช้ RedGate SQLPrompt ของฉัน วิธีที่ฉันตั้งค่าไว้มันเพิ่มนามแฝงเสมอแม้ว่าจะไม่จำเป็นก็ตาม มันอยู่ที่นั่น "ในกรณี"
พายุ

สิ่งนี้ดูมีแนวโน้มสำหรับฉัน แต่มันก็ยังนำกลับมาทุกแถวไม่ใช่สนามที่ชัดเจน 1 :(
Michael Fever

13

นั่นเป็นคำถามที่ดีจริงๆ ฉันได้อ่านคำตอบที่มีประโยชน์แล้วที่นี่ แต่ฉันอาจเพิ่มคำอธิบายที่แม่นยำยิ่งขึ้นได้

การลดจำนวนผลการสืบค้นด้วยคำสั่ง GROUP BY นั้นง่ายตราบใดที่คุณไม่ต้องค้นหาข้อมูลเพิ่มเติม สมมติว่าคุณมี 'ตำแหน่ง' ของตารางต่อไปนี้

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

ตอนนี้แบบสอบถาม

SELECT country FROM locations
GROUP BY country

จะส่งผลให้:

--country--
 France
 Poland
 Italy

อย่างไรก็ตามแบบสอบถามดังต่อไปนี้

SELECT country, city FROM locations
GROUP BY country

... แสดงข้อผิดพลาดใน MS SQL เพราะคอมพิวเตอร์ของคุณจะรู้ได้อย่างไรว่าในสามเมืองของฝรั่งเศส "Lyon", "Paris" หรือ "Marseille" ที่คุณต้องการอ่านในช่องทางด้านขวาของ "France"

ในการแก้ไขแบบสอบถามที่สองคุณต้องเพิ่มข้อมูลนี้ วิธีหนึ่งในการทำเช่นนี้คือการใช้ฟังก์ชั่น MAX () หรือ MIN () โดยเลือกค่าที่มากที่สุดหรือน้อยที่สุดในบรรดาผู้สมัครทั้งหมด MAX () และ MIN () ไม่เพียงใช้กับค่าตัวเลขเท่านั้น แต่ยังเปรียบเทียบลำดับตัวอักษรของค่าสตริง

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

จะส่งผลให้:

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

หรือ:

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

จะส่งผลให้:

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

ฟังก์ชั่นเหล่านี้เป็นทางออกที่ดีตราบใดที่คุณไม่ต้องกังวลกับการเลือกค่าของคุณจากทั้งสองด้านของลำดับตัวอักษร (หรือตัวเลข) แต่ถ้าไม่ใช่ในกรณีนี้ล่ะ? ให้เราสมมติว่าคุณต้องการค่าที่มีลักษณะบางอย่างเช่นเริ่มต้นด้วยตัวอักษร 'M' ตอนนี้ทุกอย่างซับซ้อนขึ้น

ทางออกเดียวที่ฉันสามารถค้นหาได้คือใส่แบบสอบถามทั้งหมดของคุณลงในแบบสอบถามย่อยและสร้างคอลัมน์เพิ่มเติมด้วยตนเอง:

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

จะส่งผลให้:

--country-- --city--
 France      Marseille
 Poland      NULL
 Italy       Milano

5

Great question @aryaxt - คุณสามารถบอกได้ว่ามันเป็นคำถามที่ยอดเยี่ยมเพราะคุณถามมันเมื่อ 5 ปีที่แล้วและฉันก็สะดุดเมื่อวันนี้พยายามค้นหาคำตอบ!

ฉันพยายามแก้ไขคำตอบที่ยอมรับเพื่อรวมสิ่งนี้ แต่ในกรณีที่การแก้ไขของฉันไม่ได้ทำใน:

หากตารางของคุณมีขนาดไม่ใหญ่นักและสมมติว่าคีย์หลักของคุณเป็นจำนวนเต็มเพิ่มอัตโนมัติคุณสามารถทำสิ่งนี้:

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


3

คุณสามารถทำได้ด้วยWITHประโยค

ตัวอย่างเช่น:

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

นอกจากนี้ยังช่วยให้คุณสามารถเลือกเฉพาะแถวที่เลือกในWITHคิวรีคำสั่ง


2

สำหรับ SQL Server คุณสามารถใช้ฟังก์ชัน dense_rank และหน้าต่างเพิ่มเติมเพื่อรับแถวและคอลัมน์ทั้งหมดพร้อมค่าซ้ำซ้อนในคอลัมน์ที่ระบุ นี่คือตัวอย่าง ...

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

สิ่งนี้กำลังนับจำนวนแถวสำหรับการรวมกันของ col1, col2 และ col3


ซับซ้อนเกินไปและเฉพาะเจาะจงให้เป็นหนึ่งในการดำเนินงานของ SQL
การ์เร็ตซิมป์สัน

1
select min(table.id), table.column1
from table 
group by table.column1

สิ่งนี้ใช้ได้สำหรับฉัน !! มันน่าสังเกตว่าถ้าคุณใช้ fetch_array () คุณจะต้องโทรหาแต่ละแถวผ่านป้ายกำกับดัชนีแทนที่จะเรียกชื่อแถวโดยปริยาย มีตัวละครไม่พอที่จะเขียนตัวอย่างที่ฉันมี: X ขอโทษ !!
Brandon Printiss

0
SELECT *
FROM tblname
GROUP BY duplicate_values
ORDER BY ex.VISITED_ON DESC
LIMIT 0 , 30

ในORDER BYฉันมีเพียงแค่ใส่ตัวอย่างที่นี่คุณยังสามารถเพิ่มเขตข้อมูล ID ในนี้


ดังที่ได้กล่าวไว้ในคำตอบที่ได้รับการยอมรับจะใช้งานได้กับ SQL ส่วนใหญ่ - สำหรับ MYSQL เท่านั้น
Garrett Simpson

0

พบสิ่งนี้ที่อื่นที่นี่ แต่นี่เป็นวิธีง่ายๆที่ใช้งานได้:

 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

ใช้งานได้กับ MSSQL
Michael Fever

-1

เพิ่มฟิลด์ GROUP BY to ที่คุณต้องการตรวจสอบข้อความค้นหาที่ซ้ำกันของคุณซึ่งอาจมีลักษณะดังนี้

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

field1 จะถูกตรวจสอบเพื่อแยกระเบียนที่ซ้ำกัน

หรือคุณอาจสอบถามเช่น

SELECT *  FROM table GROUP BY field1

ระเบียนที่ซ้ำกันของ field1 จะถูกแยกออกจาก SELECT


1
กลุ่มตามข้อจะต้องตรงกับเขตข้อมูลที่เลือก มิฉะนั้นจะทำให้เกิดข้อผิดพลาดเช่นfiled2 must appear in the GROUP BY clause or be used in an aggregate function
Viuu -a

-2

เพียงรวมฟิลด์ทั้งหมดของคุณในส่วน GROUP BY clause


3
เพื่อให้เป็นคำตอบที่ดีคุณควรรวมรายละเอียดเพิ่มเติมเล็กน้อยเกี่ยวกับสิ่งที่คุณหมายถึง
Robbert

-2

สามารถทำได้โดยใช้แบบสอบถามภายใน

$query = "SELECT * 
            FROM (SELECT field
                FROM table
                ORDER BY id DESC) as rows               
            GROUP BY field";

2
นี้ไม่ตอบคำถาม OP พยายามที่จะได้รับข้อมูลทั้งหมดของตาราง แต่ลบแถวที่มีการซ้ำซ้อนของเขตข้อมูลเดียว
Garrett Simpson

-3
SELECT * from table where field in (SELECT distinct field from table)

7
นั่นจะไม่ทำงาน คุณได้เลือกคอลัมน์ที่แตกต่างกันในแบบสอบถามย่อย แต่ส่วนคำสั่งย่อยได้รับคอลัมน์ทั้งหมดที่มีค่านั้น ดังนั้นการสืบค้นจึงทำได้ดีเท่ากับการเขียน 'select * from table' ยกเว้นว่าคอลัมน์ 'field' นั้นเป็นคอลัมน์ที่ไม่ซ้ำกันซึ่งในกรณีนี้ไม่จำเป็นต้องใช้ความแตกต่างของคอลัมน์นั้นเลย
Ankur-m

-3

เลือก DISTINCT FIELD1, FIELD2, FIELD3 จาก Table1 ทำงานถ้าค่าของทั้งสามคอลัมน์นั้นไม่ซ้ำกันในตาราง

ตัวอย่างเช่นหากคุณมีค่าเหมือนกันหลายค่าสำหรับชื่อ แต่นามสกุลและข้อมูลอื่น ๆ ในคอลัมน์ที่เลือกจะแตกต่างกันระเบียนจะรวมอยู่ในชุดผลลัพธ์


2
สิ่งนี้ไม่ตอบคำถาม OP พยายามหาข้อมูลทั้งหมดของตาราง แต่ลบแถวที่มีรายการซ้ำของเขตข้อมูลเดียว
Garrett Simpson

-3

ฉันอยากจะแนะนำให้ใช้

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

วิธีนี้ถ้าคุณมีค่าเดียวกันใน field1 ข้ามหลายแถวระเบียนทั้งหมดจะถูกส่งกลับ


1
SELECT * FROM table;มันไม่ได้แตกต่างกันด้วย ยิ่งมันช้า
ชินคิม

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