การจัดเรียงตามลำดับของค่าในส่วนคำสั่ง SQL IN ()


154

ฉันสงสัยว่ามีคำสั่งซื้อที่ไม่อยู่ (อาจเป็นวิธีที่ดีกว่า) ตามลำดับของค่าในประโยค IN ()

ปัญหาคือฉันมี 2 แบบสอบถามหนึ่งที่ได้รับ ID ทั้งหมดและที่สองที่ดึงข้อมูลทั้งหมด อันแรกสร้างลำดับของ ID ที่ฉันต้องการลำดับที่สองโดย ID จะอยู่ในข้อ IN () ตามลำดับที่ถูกต้อง

ดังนั้นมันจึงเป็นสิ่งที่ต้องการ (เรียบง่ายมาก):

SELECT id FROM table1 WHERE ... ORDER BY display_order, name

SELECT name, description, ... WHERE id IN ([id's from first])

ปัญหาคือแบบสอบถามที่สองไม่ส่งคืนผลลัพธ์ในลำดับเดียวกันกับที่ ID ถูกใส่ลงในส่วนคำสั่ง IN ()

ทางออกหนึ่งที่ฉันได้พบคือการใส่ ID ทั้งหมดลงในตาราง temp ที่มีฟิลด์การเพิ่มอัตโนมัติซึ่งจะเข้าร่วมในแบบสอบถามที่สอง

มีตัวเลือกที่ดีกว่านี้ไหม?

บันทึก:เนื่องจากมีการเรียกใช้แบบสอบถามครั้งแรก "โดยผู้ใช้" และครั้งที่สองเรียกใช้ในกระบวนการพื้นหลังจึงไม่มีวิธีรวมแบบสอบถาม 2 เป็น 1 โดยใช้แบบสอบถามย่อย

ฉันใช้ MySQL แต่ฉันคิดว่ามันอาจมีประโยชน์ที่จะให้สังเกตว่ามีตัวเลือกใดบ้างสำหรับฐานข้อมูลอื่นเช่นกัน

คำตอบ:


186

ใช้FIELD()ฟังก์ชั่นของ MySQL :

SELECT name, description, ...
FROM ...
WHERE id IN([ids, any order])
ORDER BY FIELD(id, [ids in order])

FIELD() จะส่งคืนดัชนีของพารามิเตอร์แรกที่เท่ากับพารามิเตอร์แรก (นอกเหนือจากพารามิเตอร์แรกเอง)

FIELD('a', 'a', 'b', 'c')

จะส่งคืน 1

FIELD('a', 'c', 'b', 'a')

จะส่งคืน 3

สิ่งนี้จะทำสิ่งที่คุณต้องการอย่างแน่นอนหากคุณวางรหัสลงในส่วนIN()คำสั่งและFIELD()ฟังก์ชั่นในลำดับเดียวกัน


3
@Vojto การจัดเรียงตามคำสั่งโดยพลการช้าตามคำจำกัดความ สิ่งนี้ทำได้ดีที่สุดเนื่องจากไม่มีการรับประกันว่าINและFIELDพารามิเตอร์จะเท่ากัน การทำสิ่งนี้ในรหัสโปรแกรมอาจเร็วขึ้นโดยใช้ความรู้เพิ่มเติมนั้น แน่นอนว่ามันอาจฉลาดกว่าที่จะวางภาระนี้ให้กับลูกค้าแทนที่จะเป็นเซิร์ฟเวอร์หากคุณมีประสิทธิภาพของเซิร์ฟเวอร์
ivan_pozdeev

1
แล้วsqliteไงล่ะ
fnc12

15

ดูวิธีการรับข้อมูลที่เรียงลำดับ

SELECT ...
  FROM ...
 WHERE zip IN (91709,92886,92807,...,91356)
   AND user.status=1
ORDER 
    BY provider.package_id DESC 
     , FIELD(zip,91709,92886,92807,...,91356)
LIMIT 10

11

โซลูชันสองประการที่ควรคำนึงถึง:

  1. order by case id when 123 then 1 when 456 then 2 else null end asc

  2. order by instr(','||id||',',',123,456,') asc

( instr()คือจาก Oracle; บางทีคุณอาจมีlocate()หรือcharindex()หรือสิ่งที่ต้องการ)


สำหรับ MySQL สามารถใช้ FIELD_IN_SET (): dev.mysql.com/doc/refman/5.0/en/…
Darryl Hein


6

หากคุณต้องการทำการเรียงลำดับโดยใช้แบบสอบถามโดยใช้ค่าที่ป้อนโดยแบบสอบถามในMS SQL Server 2008+คุณสามารถทำได้โดยสร้างตารางทันทีและทำการเข้าร่วมเช่นนั้น (ใช้ระบบเรียกชื่อจาก OP)

SELECT table1.name, table1.description ... 
FROM (VALUES (id1,1), (id2,2), (id3,3) ...) AS orderTbl(orderKey, orderIdx) 
LEFT JOIN table1 ON orderTbl.orderKey=table1.id
ORDER BY orderTbl.orderIdx

หากคุณแทนที่คำสั่ง VALUES ด้วยสิ่งอื่นที่ทำสิ่งเดียวกัน แต่ใน ANSI SQL นี่ควรทำงานกับฐานข้อมูล SQL ใด ๆ

หมายเหตุ: คอลัมน์ที่สองในตารางที่สร้างขึ้น (orderTbl.orderIdx) เป็นสิ่งจำเป็นเมื่อทำการสอบถามชุดระเบียนที่มีขนาดใหญ่กว่า 100 หรือมากกว่านั้น ตอนแรกฉันไม่มีคอลัมน์ orderIdx แต่พบว่าด้วยชุดผลลัพธ์มีขนาดใหญ่กว่า 100 ฉันต้องเรียงลำดับคอลัมน์นั้นอย่างชัดเจน ใน SQL Server Express 2014 อย่างไรก็ตาม


คำถามนี้เกี่ยวกับ MySQL ... ไม่แน่ใจว่าแบบสอบถามของคุณจะทำงานใน MySQL หรือไม่
Darryl Hein

2
@ Darryl มันจะไม่ แต่โพสต์นี้เกิดขึ้นเป็นผลลัพธ์สูงสุดเมื่อคุณ google วิธีการเรียงลำดับตามในประโยคดังนั้นฉันคิดว่าฉันอาจโพสต์ได้ที่นี่และวิธีการทั่วไปใช้กับเซิร์ฟเวอร์ SQL ที่สอดคล้องกับ ANSI ทั้งหมด (เพียงแทนที่คำสั่ง VALUES ด้วยมาตรฐาน วิธีสร้างตาราง)
เอียน

หมายเหตุนี้ทำงานให้ฉัน แต่หลังจากที่ฉันแทนที่อ้างอิงถึงorderTbl.orderKey, orderTbl.orderIndexโดยorderKey,orderIndex
ปีเตอร์

5
SELECT ORDER_NO, DELIVERY_ADDRESS 
from IFSAPP.PURCHASE_ORDER_TAB 
where ORDER_NO in ('52000077','52000079','52000167','52000297','52000204','52000409','52000126') 
ORDER BY instr('52000077,52000079,52000167,52000297,52000204,52000409,52000126',ORDER_NO)

ทำงานได้ดีจริงๆ


ทำงานให้ฉันบน Oracle ง่ายและรวดเร็ว ขอบคุณ
Menachem

4

ส่วนคำสั่ง IN อธิบายชุดของค่าและชุดไม่มีคำสั่ง

โซลูชันของคุณด้วยการเข้าร่วมจากนั้นสั่งซื้อในdisplay_orderคอลัมน์เป็นโซลูชันที่ถูกต้องที่สุด สิ่งอื่นน่าจะเป็นการแฮ็คเฉพาะ DBMS (หรือกำลังทำบางสิ่งกับฟังก์ชัน OLAP ใน SQL มาตรฐาน) แน่นอนการเข้าร่วมเป็นโซลูชั่นแบบพกพาเกือบทั้งหมด (แม้ว่าการสร้างข้อมูลด้วยdisplay_orderค่าอาจเป็นปัญหา) โปรดทราบว่าคุณอาจต้องเลือกคอลัมน์การสั่งซื้อ ที่เคยเป็นข้อกำหนดใน SQL มาตรฐาน แต่ฉันเชื่อว่ามันผ่อนคลายตามกฎเมื่อนานมาแล้ว (อาจนานเท่า SQL-92)


2

สำหรับ Oracle โซลูชันของ John ที่ใช้ฟังก์ชั่น instr () ทำงานได้ นี่คือวิธีแก้ปัญหาที่แตกต่างกันเล็กน้อยที่ใช้งานได้ - SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)



2

ฉันพยายามทำเช่นนี้คือ MS SQL Server ที่เราไม่มี FIELD ():

SELECT table1.id
... 
INNER JOIN
    (VALUES (10,1),(3,2),(4,3),(5,4),(7,5),(8,6),(9,7),(2,8),(6,9),(5,10)
    ) AS X(id,sortorder)
        ON X.id = table1.id
    ORDER BY X.sortorder

โปรดทราบว่าฉันกำลังอนุญาตการทำซ้ำเช่นกัน


1

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

ดังนั้นเกี่ยวกับสิ่งนี้:

  1. บิตส่วนต่อประสานผู้ใช้จะรันและแทรกค่าลงในตารางใหม่ที่คุณสร้าง มันจะแทรก id ตำแหน่งและตัวระบุหมายเลขงานบางอย่าง)
  2. หมายเลขงานถูกส่งผ่านไปยังกระบวนการพื้นหลัง (แทนรหัสทั้งหมด)
  3. กระบวนการพื้นหลังจะเลือกจากตารางในขั้นตอนที่ 1 และคุณเข้าร่วมเพื่อรับข้อมูลอื่น ๆ ที่คุณต้องการ มันใช้หมายเลขงานในส่วนคำสั่งซื้อ WHERE และคำสั่งซื้อโดยคอลัมน์ตำแหน่ง
  4. กระบวนการพื้นหลังเมื่อเสร็จแล้วลบออกจากตารางตามตัวระบุงาน

1

ฉันคิดว่าคุณควรจัดการเก็บข้อมูลของคุณในแบบที่คุณจะเข้าร่วมและมันจะสมบูรณ์แบบดังนั้นจึงไม่มีแฮ็กและสิ่งที่ซับซ้อนเกิดขึ้น

ฉันมีตัวอย่างเช่นรายการ "เพิ่งเล่น" ของรหัสติดตามบน SQLite ฉันทำได้:

SELECT * FROM recently NATURAL JOIN tracks;

1
โอ้ฉันหวังว่าชีวิตจะเป็นเรื่องง่าย :) :)
ดาร์ริลเฮ็น

0

ลองยิงดู:

SELECT name, description, ...
WHERE id IN
    (SELECT id FROM table1 WHERE...)
ORDER BY
    (SELECT display_order FROM table1 WHERE...),
    (SELECT name FROM table1 WHERE...)

WHEREs อาจใช้เวลาปรับแต่งเล็กน้อยเพื่อให้เคียวรีย่อยที่สัมพันธ์กันทำงานได้อย่างถูกต้อง แต่หลักการพื้นฐานควรฟังดูดี


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