DISTINCT สำหรับคอลัมน์เดียวเท่านั้น


156

สมมติว่าฉันมีคำถามต่อไปนี้

SELECT ID, Email, ProductName, ProductModel FROM Products

ฉันจะแก้ไขเพื่อที่จะไม่ได้รับอีเมล์ที่ซ้ำกันได้อย่างไร

กล่าวอีกนัยหนึ่งเมื่อหลายแถวมีอีเมลเดียวกันฉันต้องการให้ผลลัพธ์รวมแถวเดียวเท่านั้น (ควรเป็นแถวสุดท้าย) ควรอนุญาตให้ทำซ้ำในคอลัมน์อื่น

ข้อเหมือนDISTINCTและGROUP BYดูเหมือนจะทำงานในแถวทั้งหมด ดังนั้นฉันไม่แน่ใจว่าวิธีการนี้


2
ตกลงคุณต้องใช้พาร์ทิชันหรือใช้สองคำสั่งเลือก?
CarneyCode

และควรจะแสดงอะไรถ้ามี 2 แถวที่มีอีเมลเดียวกัน แต่ชื่อผลิตภัณฑ์ต่างกัน (โดยเฉพาะคนสุดท้าย)ไม่ชัดเจน ครั้งสุดท้ายที่สั่งซื้อ
ypercubeᵀᴹ

@ypercube ตามที่ระบุไว้ในคำถามโดยเฉพาะอย่างยิ่งคนสุดท้าย อย่างไรก็ตามนั่นไม่สำคัญสำหรับฉัน ฉันแค่ต้องการหนึ่งในนั้น
โจนาธานวู้ด

1
คุณสามารถดูคำถามต่อไปนี้: คำถาม 1 , คำถาม 2หรือคำถาม 3
แมเรียน

ทำไมคุณไม่สามารถใช้: เลือก DISTINCT Email, ID, ProductName, ProductModel จากผลิตภัณฑ์?
Rick Henderson

คำตอบ:


186

หากคุณใช้ SQL Server 2005 หรือสูงกว่าใช้สิ่งนี้:

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
              ) a
WHERE rn = 1

แก้ไข: ตัวอย่างการใช้คำสั่ง where

SELECT *
  FROM (
                SELECT  ID, 
                        Email, 
                        ProductName, 
                        ProductModel,
                        ROW_NUMBER() OVER(PARTITION BY Email ORDER BY ID DESC) rn
                    FROM Products
                   WHERE ProductModel = 2
                     AND ProductName LIKE 'CYBER%'

              ) a
WHERE rn = 1

4
ฉันต้องตรวจสอบประโยคส่วนนี้ไม่เคยเห็นมาก่อน ขอบคุณสำหรับตัวอย่าง
LorenVS

@Cybernate One แทรกซ้อน: ฉันSELECTต้องการWHEREเงื่อนไขภายใน ฉันคิดว่าหมายเลขแถวจะถูกกำหนดให้กับทุกแถวในตาราง ไวยากรณ์นี้เกินกว่าฉันเล็กน้อย โอกาสใดของการอัปเดตที่รับประกันได้ว่าจะมีอีเมลหนึ่งฉบับที่ตรงกับWHEREเงื่อนไขหรือไม่
โจนาธานวู้ด

1
คุณสามารถเพิ่มส่วนคำสั่งไปยัง sql ด้านในได้ ฉันจะอัปเดตโพสต์เมื่อฉันสามารถเข้าถึงแล็ปท็อปของฉันได้
Chandu

1
อัปเดตโพสต์ด้วยตัวอย่างโดยใช้ตำแหน่งข้อ
Chandu

1
ฉันได้รับการทำงานอย่างถูกต้องเฉพาะเมื่อไม่มี JOINของในแบบสอบถามของฉัน เร็วที่สุดเท่าที่ฉันมีJOINที่ROW_NUMBERค่าผลตอบแทนที่สูงกว่า "1"
Uwe Keim

10

สิ่งนี้ถือว่า SQL Server 2005+ และคำจำกัดความของคุณของ "last" คือ PK สูงสุดสำหรับอีเมลที่ระบุ

WITH CTE AS
(
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel, 
       ROW_NUMBER() OVER (PARTITION BY Email ORDER BY ID DESC) AS RowNumber 
FROM   Products
)
SELECT ID, 
       Email, 
       ProductName, 
       ProductModel
FROM CTE 
WHERE RowNumber = 1

6

เมื่อคุณDISTINCTคิดว่ามันเป็นแถวที่แตกต่างกันไม่ใช่คอลัมน์ มันจะส่งกลับเฉพาะแถวที่คอลัมน์ไม่ตรงกันเท่านั้น

SELECT DISTINCT ID, Email, ProductName, ProductModel
FROM Products

----------------------
1 | something@something.com | ProductName1 | ProductModel1
2 | something@something.com | ProductName1 | ProductModel1

แบบสอบถามจะส่งคืนทั้งสองแถวเนื่องจากIDคอลัมน์นั้นแตกต่างกัน ฉันสมมติว่าIDคอลัมน์เป็นIDENTITYคอลัมน์ที่เพิ่มขึ้นหากคุณต้องการคืนค่าล่าสุดฉันแนะนำสิ่งนี้:

SELECT DISTINCT TOP 1 ID, Email, ProductName, ProductModel
FROM Products
ORDER BY ID DESC

TOP 1จะกลับมาเพียงครั้งแรกที่บันทึกโดยการสั่งซื้อมันจากIDมากไปหาน้อยจะทำการแสดงผลที่มีแถวสุดท้ายเป็นครั้งแรก สิ่งนี้จะทำให้คุณมีบันทึกล่าสุด


2
ตามที่ระบุในคำถามฉันเห็นว่า DISTINCT ทำงานได้ทั้งแถว ฉันต้องการทำตามที่คุณแนะนำข้างต้น แต่ทุกครั้งที่มีการทำซ้ำอีเมลในผลลัพธ์ (ไม่ใช่เพียงครั้งเดียว)
โจนาธานวู้ด

ในกรณีนี้ฉันอยากจะแนะนำให้ไปกับ @Cybernate คำตอบ นั่นควรทำสิ่งที่คุณต้องการ
jon3laze

4

คุณสามารถทำได้โดยใช้ฟังก์ชั่น GROUP BY

SELECT ID, Email, ProductName, ProductModel FROM Products GROUP BY Email


16
คอลัมน์ 'Products.ID' ไม่ถูกต้องในรายการที่เลือกเนื่องจากมันไม่ได้อยู่ในฟังก์ชั่นรวมหรือประโยคตามกลุ่ม
palota

2
วิธีนี้ใช้ไม่ได้หากไม่ใช้บางอย่างเช่น MAX (ID), MAX (ProductName), MAX (ProductModel) สำหรับคอลัมน์อื่น ๆ
avl_sweden

2
ใน postgres SELECT id, max(email) AS email FROM tbl GROUP by emailคุณจะต้องฟังก์ชั่นรวมในคอลัมน์ที่จะนำมาใช้ในกลุ่มโดยข้อเช่น ใน SQL server คอลัมน์ทั้งหมดในSELECTclause ต้องอยู่ในฟังก์ชันการรวม สิ่งนี้กัดฉันทุกครั้งที่ฉันกลับไป
Bruce Pierson

สิ่งนี้จะไม่ทำงาน มันเป็นทางออกที่ไม่ดี
Dan AS

1

สำหรับการเข้าถึงคุณสามารถใช้แบบสอบถาม SQL เลือกฉันนำเสนอที่นี่:

ตัวอย่างเช่นคุณมีตารางนี้:

cliente || NOMBRES || MAIL

888 || T800 ARNOLD || t800.arnold@cyberdyne.com

123 || JOHN CONNOR || s.connor@skynet.com

125 || SARAH CONNOR ||s.connor@skynet.com

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

เลือก SQL:

SELECT MAX(p.CLIENTE) AS ID_CLIENTE
, (SELECT TOP 1 x.NOMBRES 
    FROM Rep_Pre_Ene_MUESTRA AS x 
    WHERE x.MAIL=p.MAIL 
     AND x.CLIENTE=(SELECT MAX(l.CLIENTE) FROM Rep_Pre_Ene_MUESTRA AS l WHERE x.MAIL=l.MAIL)) AS NOMBRE, 
p.MAIL
FROM Rep_Pre_Ene_MUESTRA AS p
GROUP BY p.MAIL;

คุณสามารถใช้สิ่งนี้เพื่อเลือก ID สูงสุดชื่อผู้ติดต่อใน ID สูงสุดนั้นคุณสามารถเพิ่มแอททริบิวอื่น ๆ ได้ จากนั้นในตอนท้ายคุณวางคอลัมน์ที่แตกต่างเพื่อกรองและคุณจัดกลุ่มเฉพาะคอลัมน์ที่แตกต่างกันล่าสุด

สิ่งนี้จะทำให้คุณได้ ID สูงสุดพร้อมข้อมูลผู้ติดต่อคุณสามารถใช้ min หรือฟังก์ชั่นอื่น ๆ และคุณทำซ้ำฟังก์ชั่นนั้นไปยังแบบสอบถามย่อย

การเลือกนี้จะกลับมา:

cliente || NOMBRES || MAIL

888 || T800 ARNOLD || t800.arnold@cyberdyne.com

125 || SARAH CONNOR ||s.connor@skynet.com

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


0

เหตุผลDISTINCTและการGROUP BYทำงานกับแถวทั้งหมดคือแบบสอบถามของคุณส่งคืนแถวทั้งหมด

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

หากคุณไม่สนใจสิ่งที่อยู่ในคอลัมน์อื่น ๆ อย่าส่งคืนสิ่งเหล่านั้น การคืนแถวแบบสุ่มสำหรับที่อยู่อีเมลแต่ละอันนั้นไม่มีประโยชน์อะไรสำหรับฉัน


@JohnFix ฉันต้องการกลับทั้งแถว ฉันไม่ต้องการให้ส่งคืนแถวเมื่อผลลัพธ์รวมแถวที่มีค่าเดียวกันในคอลัมน์อีเมลแล้ว
โจนาธานวู้ด

แล้วมันจะตัดสินใจได้อย่างไร คุณต้องการแบบสอบถามที่ส่งคืนแถวที่กำหนดเองสำหรับแต่ละอีเมลหรือไม่ มันมีกลิ่นเหมือนคุณอาจต้องคิดใหม่อีกครั้งว่าปัญหาที่คุณกำลังพยายามแก้ไข เกือบทุกครั้งที่ฉันถูกถามคำถามนี้ (และมีจำนวนมาก) ปรากฎว่าผู้พัฒนาไม่ได้คิดถึงผลที่ตามมาในแอพสำหรับพฤติกรรมนี้
JohnFx

6
ฉันมีปัญหาในการติดตามตรรกะของคุณ ตามที่ระบุไว้ในคำถามฉันต้องการสุดท้าย (เรียงตาม ID) ใช่ถ้ามันเลือกแถวแบบสุ่มที่จะไม่เป็นไร และใช่ฉันคิดเกี่ยวกับมัน
โจนาธานวู้ด


-2

ลองสิ่งนี้:

SELECT ID, Email, ProductName, ProductModel FROM Products WHERE ID IN (SELECT MAX(ID) FROM Products GROUP BY Email)

2
ทำไมเราควรลองสิ่งนี้ ทำไมดีกว่าคำตอบอื่น ๆ ที่โพสต์ที่นี่ใน 8 ปีที่ผ่านมา? หากคุณต้องการแบ่งปันวิธีที่ดีกว่าในการแก้ปัญหาคุณต้องอธิบายว่าทำไมคุณถึงแนะนำ
Dharman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.