วิธีเลือกเฉพาะแถวแรกสำหรับแต่ละค่าที่ไม่ซ้ำกันของคอลัมน์


100

สมมติว่าฉันมีตารางที่อยู่ลูกค้า:

CName           |   AddressLine
-------------------------------
John Smith      | 123 Nowheresville
Jane Doe        | 456 Evergreen Terrace
John Smith      | 999 Somewhereelse
Joe Bloggs      | 1 Second Ave

ในตารางลูกค้ารายหนึ่งเช่น John Smith สามารถมีที่อยู่ได้หลายรายการ ฉันต้องการคำค้นหาที่เลือกสำหรับตารางนี้เพื่อส่งคืนเฉพาะแถวแรกที่พบซึ่งมีรายการที่ซ้ำกันใน 'CName' สำหรับตารางนี้ควรส่งคืนแถวทั้งหมดยกเว้นแถวที่ 3 (หรือ 1 - ที่อยู่ใดก็ได้ในสองรายการนั้นใช้ได้ แต่สามารถส่งคืนได้เพียงรายการเดียว) มีคำหลักที่ฉันสามารถเพิ่มลงในแบบสอบถาม SELECT เพื่อกรองตามว่าเซิร์ฟเวอร์เคยเห็นค่าคอลัมน์มาก่อนหรือไม่

คำตอบ:


128

คำตอบง่ายๆหากคุณบอกว่าคุณไม่สนใจว่าจะใช้ที่อยู่ใด

SELECT
    CName, MIN(AddressLine)
FROM
    MyTable
GROUP BY
    CName

หากคุณต้องการสิ่งแรกตามให้พูดว่าคอลัมน์ "แทรก" แล้วเป็นข้อความค้นหาอื่น

SELECT
    M.CName, M.AddressLine,
FROM
    (
    SELECT
        CName, MIN(Inserted) AS First
    FROM
        MyTable
    GROUP BY
        CName
    ) foo
    JOIN
    MyTable M ON foo.CName = M.CName AND foo.First = M.Inserted

แม้ว่าอาจไม่ได้ตั้งใจให้ใช้ในลักษณะนี้เมื่อเลือก 10 คอลัมน์ ดูเหมือนว่าจะไม่สามารถยอมรับคอลัมน์ประเภทบิตได้
nuit9

1
@ nuit9: แน่นอนว่ามันใช้ไม่ได้กับบิตและ 10 คอลัมน์ ข้อเท็จจริงทั้งสองนี้ไม่อยู่ในคำถามของคุณ คุณจะใช้เทคนิคที่ 2 หรือเทคนิคของ Ben Thul ฉันตอบสิ่งที่คุณถามโดยเฉพาะพร้อมคำแนะนำเกี่ยวกับวิธีแก้ปัญหาโดยทั่วไป
gbn

ส่วนแรก DO ทำงานกับหลายคอลัมน์แม้ว่าจะไม่ใช้คอลัมน์ประเภทบิต ฉันทดสอบสิ่งนี้ใน MS SQL server 2016 แม้ว่า
netfed

27

ใน SQL 2k5 + คุณสามารถทำสิ่งต่างๆเช่น:

;with cte as (
  select CName, AddressLine,
  rank() over (partition by CName order by AddressLine) as [r]
  from MyTable
)
select CName, AddressLine
from cte
where [r] = 1

7
โปรดอธิบายว่าอันดับพาร์ติชันและ [r] ทำอะไร
Roberto

10

คุณสามารถใช้row_number()เพื่อรับหมายเลขแถวของแถว ใช้overคำสั่ง - partition byประโยคระบุเวลาที่จะเริ่มการทำงานของหมายเลขใหม่และorder byเลือกสิ่งที่จะสั่งให้หมายเลขแถวบน แม้ว่าคุณจะเพิ่มorder byส่วนท้ายของแบบสอบถามของคุณ แต่ก็ยังคงรักษาลำดับไว้ในoverคำสั่งเมื่อกำหนดหมายเลข

select *
from mytable
where row_number() over(partition by Name order by AddressLine) = 1

6
ใน postgresql ไม่อนุญาตให้ใช้ฟังก์ชันหน้าต่างใน WHERE clause
ekanna

4
ไม่อนุญาตให้ใช้ MS-SQL
Mixxiphoid

1
ROW_NUMBER()ไม่ทำงานในWhereประโยคใน Teradata เช่นกัน
Pirate X

6

คุณสามารถใช้row_numer() over(partition by ...)ไวยากรณ์ดังนี้:

select * from
(
select *
, ROW_NUMBER() OVER(PARTITION BY CName ORDER BY AddressLine) AS row
from myTable
) as a
where row = 1

สิ่งนี้ไม่สามารถที่จะสร้างคอลัมน์ที่เรียกว่าrowซึ่งเป็นเคาน์เตอร์ที่เพิ่มขึ้นทุกครั้งที่มันเห็นเดียวกันและดัชนีที่เกิดขึ้นเหล่านั้นด้วยCName AddressLineโดยการกำหนดwhere row = 1สามารถเลือกCNameผู้AddressLineมาก่อนตามตัวอักษร ถ้าorder byเป็นdescเช่นนั้นก็จะเลือกCNameที่AddressLineมาสุดท้ายตามตัวอักษร


1

ซึ่งจะทำให้คุณได้แถวที่ซ้ำกัน 1 แถว นอกจากนี้ยังจะให้คอลัมน์ประเภทบิตแก่คุณและอย่างน้อยก็ทำงานใน MS Sql Server

(select cname, address 
from (
  select cname,address, rn=row_number() over (partition by cname order by cname) 
  from customeraddresses  
) x 
where rn = 1) order by cname

หากคุณต้องการค้นหารายการที่ซ้ำกันทั้งหมดแทนเพียงแค่เปลี่ยน rn = 1 เป็น rn> 1 หวังว่านี่จะช่วยได้

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