วิธีการเลือกแถวแรกของแต่ละกลุ่ม?


57

ฉันมีโต๊ะแบบนี้:

 ID |  Val   |  Kind
----------------------
 1  |  1337  |   2
 2  |  1337  |   1
 3  |   3    |   4
 4  |   3    |   4

ฉันต้องการที่จะทำให้การSELECTที่จะกลับมาเพียงแค่แถวแรกสำหรับแต่ละการสั่งซื้อโดยValKind

ตัวอย่างผลลัพธ์:

 ID |  Val   |  Kind
----------------------
 2  |  1337  |   1
 3  |   3    |   4

ฉันจะสร้างแบบสอบถามนี้ได้อย่างไร


ทำไม 3 | 3 | 4 และไม่ใช่ 4 | 3 | 4 - เน็คไทคืออะไรหรือคุณไม่สนใจ?
แจ็คดักลาส

@ JackDouglas ที่จริงแล้วฉันมีORDER BY ID DESCแต่นั่นไม่เกี่ยวข้องกับคำถาม ในตัวอย่างนี้ฉันไม่สนใจ
BrunoLM

คำตอบ:


38

โซลูชันนี้ยังใช้keepแต่valและkindสามารถคำนวณได้ง่ายสำหรับแต่ละกลุ่มโดยไม่มีแบบสอบถามย่อย:

select min(id) keep(dense_rank first order by kind) id
     , val
     , min(kind) kind
  from mytable
 group by val;
ID | VAL | เมตตา
-: | ---: | ---:
 3 | 3 | 4
 2 | 1337 | 1

dbfiddle ที่นี่

KEEP ... FIRST และ KEEP … LAST เป็นคุณลักษณะเฉพาะของ Oracle ของการรวม - คุณสามารถอ่านได้ที่นี่ในเอกสาร Oracle หรือใน ORACLE_BASE :

ฟังก์ชัน FIRST และ LAST สามารถใช้เพื่อส่งคืนค่าแรกหรือค่าสุดท้ายจากลำดับที่สั่งซื้อ


62

ใช้การแสดงออกตารางทั่วไป (CTE) และฟังก์ชั่น Windowing / การจัดอันดับ / แบ่งพาร์ทิชันเช่นROW_NUMBER

แบบสอบถามนี้จะสร้างตารางในหน่วยความจำชื่อ ORDERED และเพิ่มคอลัมน์เพิ่มเติมของ rn ซึ่งเป็นลำดับของตัวเลขตั้งแต่ 1 ถึง N The PARTITION BYบ่งชี้ว่าควรเริ่มต้นใหม่ที่ 1 ทุกครั้งที่มูลค่าของ Val เปลี่ยนแปลงและเราต้องการสั่งซื้อ แถวตามค่าที่น้อยที่สุดของชนิด

WITH ORDERED AS
(
SELECT
    ID
,   Val
,   kind
,   ROW_NUMBER() OVER (PARTITION BY Val ORDER BY Kind ASC) AS rn
FROM
    mytable
)
SELECT
    ID
,   Val
,   Kind
FROM
    ORDERED
WHERE
    rn = 1;

วิธีการข้างต้นควรทำงานกับ RDBMS ใด ๆ ที่ใช้ฟังก์ชัน ROW_NUMBER () ออราเคิลมีฟังก์ชั่นการใช้งานที่หรูหราตามคำตอบของมิคซึ่งโดยทั่วไปจะให้ประสิทธิภาพที่ดีกว่าคำตอบนี้


25

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

SELECT ID, Val, Kind FROM
(
   SELECT First_Value(ID) OVER (PARTITION BY Val ORDER BY Kind) First, ID, Val, Kind 
   FROM mytable
)
WHERE ID = First;

ทดสอบข้อมูล

--drop table mytable;
create table mytable (ID Number(5) Primary Key, Val Number(5), Kind Number(5));

insert into mytable values (1,1337,2);
insert into mytable values (2,1337,1);
insert into mytable values (3,3,4);
insert into mytable values (4,3,4);

หากคุณต้องการนี่คือ CTE ที่เทียบเท่า

WITH FirstIDentified AS (
   SELECT First_Value(ID) OVER (PARTITION BY Val ORDER BY Kind) First, ID, Val, Kind 
   FROM mytable
   )
SELECT ID, Val, Kind FROM FirstIdentified
WHERE ID = First;

1
+1 แต่ฉันคิดว่ามันคุ้มค่าที่จะเน้นว่าคำตอบและบิลลิ่งของคุณนั้นไม่เหมือนidกันทุกประการยกเว้นว่าจะไม่ซ้ำกัน
แจ็คดักลาส

@ แจ็คดักลาส - จริงฉันคิดว่า
Leigh Riffel

14

คุณสามารถใช้keepเพื่อเลือกidจากแต่ละกลุ่ม:

select *
from mytable
where id in ( select min(id) keep (dense_rank first order by kind, id)
              from mytable
              group by val );
ID | VAL | เมตตา
-: | ---: | ---:
 2 | 1337 | 1
 3 | 3 | 4

dbfiddle ที่นี่


2
SELECT MIN(MyTable01.Id) as Id,
       MyTable01.Val     as Val,
       MyTable01.Kind    as Kind 
  FROM MyTable MyTable01,                         
       (SELECT Val,MIN(Kind) as Kind
          FROM MyTable                   
      GROUP BY Val) MyTableGroup
WHERE MyTable01.Val  = MyTableGroup.Val
  AND MyTable01.Kind = MyTableGroup.Kind
GROUP BY MyTable01.Val,MyTable01.Kind
ORDER BY Id;

นั่นจะมีประสิทธิภาพน้อยกว่าคำตอบอื่น ๆ เนื่องจากต้องใช้การสแกน MyTable สองครั้ง
a_horse_with_no_name

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