เรียงตามรายการค่า IN


166

ฉันมีแบบสอบถาม SQL แบบง่ายใน PostgreSQL 8.3 ที่จับมัดข้อคิดเห็น ฉันให้รายการเรียงลำดับของค่าการINสร้างในWHEREข้อ:

SELECT * FROM comments WHERE (comments.id IN (1,3,2,4));

1,2,3,4ผลตอบแทนนี้ความคิดเห็นในการสั่งซื้อโดยพลการของฉันซึ่งเกิดขึ้นเป็นรหัสเช่น

ฉันต้องการแถวผลลัพธ์เรียงเหมือนรายการในINโครงสร้าง: (1,3,2,4).
ทำอย่างไรถึงจะบรรลุเป้าหมายนั้น?


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

2
ตอนนี้ฉันมีคำตอบมากมาย ฉันจะได้รับการโหวตและความคิดเห็นเพื่อให้ฉันรู้ว่าใครเป็นผู้ชนะ! ขอบคุณทุกคน :-)
แคร็กเกอร์

คำตอบ:


107

คุณสามารถทำได้ง่าย ๆ ด้วย (แนะนำใน PostgreSQL 8.2) ค่า (), ()

ไวยากรณ์จะเป็นดังนี้:

select c.*
from comments c
join (
  values
    (1,1),
    (3,2),
    (2,3),
    (4,4)
) as x (id, ordering) on c.id = x.id
order by x.ordering

2
@ user80168 จะเกิดอะไรขึ้นหากมีค่าหลายพันค่าในข้อย่อย? เพราะฉันต้องทำมันเพื่อบันทึกหลายพันรายการ
kamal

@kamal with ordered_products as (select row_number() OVER (ORDER BY whatever) as reportingorder, id from comments) ... ORDER BY reportingorderสำหรับที่ฉันได้ใช้
Noumenon

66

เพียงเพราะมันยากที่จะหาและจะต้องมีการแพร่กระจาย: ใน mySQL นี้สามารถทำได้ง่ายขึ้นมากแต่ฉันไม่รู้ว่ามันทำงานใน SQL อื่น ๆ

SELECT * FROM `comments`
WHERE `comments`.`id` IN ('12','5','3','17')
ORDER BY FIELD(`comments`.`id`,'12','5','3','17')

3
รายการค่าจะต้องมีสองครั้งในสองวิธีที่แตกต่างกัน ไม่ง่ายนัก คำตอบที่ได้รับการยอมรับต้องการเพียงครั้งเดียว (แม้ว่าจะอยู่ในรูปแบบที่ละเอียดมากขึ้น) และมันก็ง่ายขึ้นด้วย Postgres ที่ทันสมัย ​​(ดังที่แสดงในคำตอบที่ใหม่กว่า) นอกจากนี้คำถามนี้ดูเหมือนจะเกี่ยวกับ Postgres หลังจากทั้งหมด
Erwin Brandstetter

8
ERROR: cannot pass more than 100 arguments to a function
brauliobo

54

ใน Postgres 9.4หรือใหม่กว่านี่อาจจะง่ายและเร็วที่สุด :

SELECT c.*
FROM   comments c
JOIN   unnest('{1,3,2,4}'::int[]) WITH ORDINALITY t(id, ord) USING (id)
ORDER  BY t.ord;
  • การใช้ใหม่WITH ORDINALITYที่@a_horse กล่าวแล้ว

  • เราไม่ต้องการแบบสอบถามย่อยเราสามารถใช้ฟังก์ชัน set-return เช่นตาราง

  • สตริงตัวอักษรที่จะส่งมอบในอาร์เรย์แทนตัวสร้าง ARRAYอาจจะง่ายต่อการใช้กับลูกค้าบางราย

คำอธิบายโดยละเอียด:


46

ฉันคิดว่าวิธีนี้ดีกว่า:

SELECT * FROM "comments" WHERE ("comments"."id" IN (1,3,2,4))
    ORDER BY  id=1 DESC, id=3 DESC, id=2 DESC, id=4 DESC

1
ฉันสามารถทำได้ด้วยค่าที่ถูกผูกไว้เช่น: ... order by id=? desc, id=? desc, id=? descและดูเหมือนว่าจะทำงานได้ดี :-)
KajMagnus

ทำงานใน postgres และดูเหมือนจะเป็นทางออกที่ดีที่สุด!
Mike Szyndel

วิธีแก้ปัญหานี้ทำเพื่อฉัน แต่: มีใครค้นคว้าวิธีแก้ปัญหานี้ทำงานได้ดีหรือไม่? มันเพิ่มคำสั่ง multple ตามคำสั่ง ดังนั้นมันอาจ (ฉันยังไม่ได้ทดสอบ) จะช้าลงชี้แจงเพิ่มจำนวนรหัสคำสั่ง? ข้อมูลใด ๆ เกี่ยวกับเรื่องนี้จะได้รับการชื่นชมอย่างมาก!
Fabian Schöner

1
ข้อผิดพลาด: รายการเป้าหมายสามารถมีได้สูงสุด 1664 รายการ -> เมื่อคุณพยายามเรียกใช้ข้อความค้นหาที่ยาวนาน ...
Fatkhan Fauzi

@ Mango MS SQL จำรุ่นที่ไม่ได้ อาจได้รับ 2012
biko

43

ด้วยPostgres 9.4สิ่งนี้สามารถทำให้สั้นลงได้เล็กน้อย:

select c.*
from comments c
join (
  select *
  from unnest(array[43,47,42]) with ordinality
) as x (id, ordering) on c.id = x.id
order by x.ordering;

หรือเพิ่มขนาดกะทัดรัดขึ้นอีกเล็กน้อยโดยไม่มีตารางที่ได้รับ:

select c.*
from comments c
  join unnest(array[43,47,42]) with ordinality as x (id, ordering) 
    on c.id = x.id
order by x.ordering

การลบความจำเป็นในการกำหนด / รักษาตำแหน่งให้กับแต่ละค่าด้วยตนเอง

ด้วยPostgres 9.6สิ่งนี้สามารถทำได้โดยใช้array_position():

with x (id_list) as (
  values (array[42,48,43])
)
select c.*
from comments c, x
where id = any (x.id_list)
order by array_position(x.id_list, c.id);

CTE ถูกใช้เพื่อให้รายการค่าต้องถูกระบุเพียงครั้งเดียว หากไม่สำคัญสิ่งนี้สามารถเขียนเป็น:

select c.*
from comments c
where id in (42,48,43)
order by array_position(array[42,48,43], c.id);

นี้ไม่ได้ทำซ้ำINรายการทั้งหมดจากWHEREข้ออีกครั้งในORDER BYข้อซึ่งทำให้คำตอบที่ดีที่สุด imho ... ตอนนี้เท่านั้นที่จะหาสิ่งที่คล้ายกันสำหรับ MySQL ...
Stijn de Witt

1
คำตอบที่ฉันชอบ แต่โปรดทราบว่า array_position ไม่สามารถทำงานได้กับบิ๊กโทนและคุณต้องใช้วิธีนี้: order by array_position(array[42,48,43], c.id::int);ซึ่งอาจทำให้เกิดข้อบกพร่องในบางกรณี
aaandre

1
@aaandre หล่อต่อไปนี้จะทำงานได้ดี (ใน Postgres 12 อย่างน้อย) array_position(array[42, 48, 43]::bigint[], c.id::bigint)จึงไม่จำเป็นต้องตัดไปbigint int
Vic

29

อีกวิธีในการทำใน Postgres ก็คือการใช้idxฟังก์ชั่น

SELECT *
FROM comments
ORDER BY idx(array[1,3,2,4], comments.id)

อย่าลืมสร้างidxฟังก์ชันก่อนตามที่อธิบายไว้ที่นี่: http://wiki.postgresql.org/wiki/Array_Index


11
ฟังก์ชั่นนี้อยู่ในขณะนี้ในส่วนขยายที่มาพร้อมกับ PostgreSQL นี้: postgresql.org/docs/9.2/static/intarray.htmlCREATE EXTENSION intarray;ติดตั้งด้วย
Alex Kahn

1
เพียงแค่ทำการซ้อนต่อไปสำหรับผู้ใช้ Amazon RDS ฟังก์ชั่นการโยกย้าย ROR enable_extensionจะช่วยให้คุณเปิดใช้งานได้ตราบใดที่ผู้ใช้แอปของคุณเป็นสมาชิกของrds_superuserกลุ่ม
เดฟเอส

ใน PG 9.6.2 PG :: ไม่ได้กำหนดฟังก์ชัน: ข้อผิดพลาด: ฟังก์ชัน idx (จำนวนเต็ม [], จำนวนเต็ม) ไม่มีอยู่จริง
Yakob Ubaidi

ขอบคุณคำตอบที่ดีที่สุดเมื่อรวมกับความคิดเห็นของ @ AlexKahn
Andrew

21

ใน Postgresql:

select *
from comments
where id in (1,3,2,4)
order by position(id::text in '1,3,2,4')

2
Hum ... position(id::text in '123,345,3,678')มันโรคจิตถ้า รหัส3จะตรงกันก่อนรหัส345ใช่ไหม
alanjds

4
ฉันคิดว่าคุณถูกต้องและจำเป็นต้องมีทั้งตัวคั่นเริ่มต้นและจุดสิ้นสุดอาจจะ: เรียงตามตำแหน่ง (',' | | id :: text || ',' in ', 1,3,2,4, ')
Michael Rush

3

ในการค้นคว้าเพิ่มเติมฉันพบวิธีแก้ปัญหานี้:

SELECT * FROM "comments" WHERE ("comments"."id" IN (1,3,2,4)) 
ORDER BY CASE "comments"."id"
WHEN 1 THEN 1
WHEN 3 THEN 2
WHEN 2 THEN 3
WHEN 4 THEN 4
END

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


7
แน่นอนว่าฉันสามารถแสดงความคิดเห็นได้ มีหลายสิ่งที่ SQL ทำได้ดีและสิ่งที่ไม่ดี SQL ไม่ดีเท่านี้ เพียงเรียงลำดับผลลัพธ์เป็นภาษาใดก็ตามที่คุณต้องการสืบค้น มันจะช่วยให้คุณประหยัดมากคร่ำครวญและขบเขี้ยวเคี้ยวฟัน SQL เป็นภาษาที่มุ่งเน้นการตั้งค่าและชุดไม่ได้สั่งซื้อคอลเลกชัน
kquinn

อืม ... มันขึ้นอยู่กับประสบการณ์ส่วนตัวและการทดสอบ? ประสบการณ์การทดสอบของฉันคือว่านี่เป็นเทคนิคที่ค่อนข้างมีประสิทธิภาพสำหรับการสั่งซื้อ (อย่างไรก็ตามคำตอบที่ได้รับการยอมรับนั้นดีกว่าเพราะจะกำจัดข้อ "IN (... )" โปรดจำไว้ว่าสำหรับขนาดชุดผลลัพธ์ที่สมเหตุสมผลการได้มาของชุดควรเป็นส่วนที่มีราคาแพง เมื่อลงไปหลายร้อยหรือน้อยกว่าการเรียงลำดับเป็นเรื่องไม่สำคัญ
dkretz

เกิดอะไรขึ้นถ้ามีค่าหลายพันในINข้อ? เพราะฉันต้องทำเพื่อบันทึกหลายพันรายการ
kamal

2

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

ด้วยวิธีดังกล่าวคุณจะอธิบายการเรียงลำดับที่คุณต้องการในฐานข้อมูลอย่างชัดเจน


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

2

sans SEQUENCE ทำงานได้เฉพาะบน 8.4:

select * from comments c
join 
(
    select id, row_number() over() as id_sorter  
    from (select unnest(ARRAY[1,3,2,4]) as id) as y
) x on x.id = c.id
order by x.id_sorter

1
SELECT * FROM "comments" JOIN (
  SELECT 1 as "id",1 as "order" UNION ALL 
  SELECT 3,2 UNION ALL SELECT 2,3 UNION ALL SELECT 4,4
) j ON "comments"."id" = j."id" ORDER BY j.ORDER

หรือถ้าคุณชอบความชั่วร้ายมากกว่าความดี:

SELECT * FROM "comments" WHERE ("comments"."id" IN (1,3,2,4))
ORDER BY POSITION(','+"comments"."id"+',' IN ',1,3,2,4,')

0

และนี่คือโซลูชันอื่นที่ใช้งานและใช้ตารางคงที่ ( http://www.postgresql.org/docs/8.3/interactive/sql-values.html ):

SELECT * FROM comments AS c,
(VALUES (1,1),(3,2),(2,3),(4,4) ) AS t (ord_id,ord)
WHERE (c.id IN (1,3,2,4)) AND (c.id = t.ord_id)
ORDER BY ord

แต่อีกครั้งฉันไม่แน่ใจว่านี่เป็นนักแสดง

ตอนนี้ฉันมีคำตอบมากมาย ฉันจะได้รับการโหวตและความคิดเห็นเพื่อให้ฉันรู้ว่าใครเป็นผู้ชนะ!

ขอบคุณทุกคน :-)


1
คำตอบของคุณใกล้เคียงกับ depesz เพียงลบ c.ID IN (1,3,2,4) อย่างไรก็ตามเขาดีกว่าเขาใช้ JOIN ให้มากที่สุดเท่าที่เป็นไปได้ใช้วิธีการเข้าร่วม ANSI SQL อย่าใช้ตารางจุลภาคของตาราง ฉันควรอ่านคำตอบของคุณอย่างถี่ถ้วนฉันกำลังยากที่จะหาวิธีตั้งชื่อแทนสองคอลัมน์ก่อนอื่นฉันลองทำสิ่งนี้: (ค่า (1,1) เป็น x (id, sort_order), (3,2), (2,3), (4,4)) เป็น y แต่จะไม่มีประโยชน์ :-D คำตอบของคุณจะได้ให้ฉันเบาะแสถ้าฉันได้อ่านอย่างรอบคอบ :-)
ไมเคิล Buen

0
create sequence serial start 1;

select * from comments c
join (select unnest(ARRAY[1,3,2,4]) as id, nextval('serial') as id_sorter) x
on x.id = c.id
order by x.id_sorter;

drop sequence serial;

[แก้ไข]

ที่ไม่ได้เลวร้ายนั้นยังไม่ได้มีมาให้ใน 8.3 แต่คุณสามารถสร้างได้ด้วยตัวเอง (ความงามของ * ใด ๆ ):

create function unnest(anyarray) returns setof anyelement
language sql as
$$
    select $1[i] from generate_series(array_lower($1,1),array_upper($1,1)) i;
$$;

ฟังก์ชั่นนั้นสามารถทำงานได้ทุกประเภท:

select unnest(array['John','Paul','George','Ringo']) as beatle
select unnest(array[1,3,2,4]) as id

ขอบคุณ Michael แต่ฟังก์ชั่นที่ไม่ธรรมดานั้นไม่มีอยู่สำหรับ PSQL ของฉันและฉันไม่สามารถพูดถึงมันในเอกสารได้ เป็น 8.4 เท่านั้น?
แคร็กเกอร์

ที่ไม่ได้เลวร้ายนั้นยังไม่ได้ติดตั้งมาในตัว 8.3 แต่คุณสามารถติดตั้งได้ด้วยตัวเอง ดูโค้ดข้างต้น
ไมเคิล Buen

0

การปรับปรุงเล็กน้อยกับเวอร์ชั่นที่ใช้ลำดับฉันคิดว่า:

CREATE OR REPLACE FUNCTION in_sort(anyarray, out id anyelement, out ordinal int)
LANGUAGE SQL AS
$$
    SELECT $1[i], i FROM generate_series(array_lower($1,1),array_upper($1,1)) i;
$$;

SELECT 
    * 
FROM 
    comments c
    INNER JOIN (SELECT * FROM in_sort(ARRAY[1,3,2,4])) AS in_sort
        USING (id)
ORDER BY in_sort.ordinal;

0
select * from comments where comments.id in 
(select unnest(ids) from bbs where id=19795) 
order by array_position((select ids from bbs where id=19795),comments.id)

ที่นี่ [bbs] เป็นตารางหลักที่มีเขตข้อมูลที่เรียกว่ารหัสและรหัสเป็นอาร์เรย์ที่เก็บความคิดเห็น

ผ่านไปใน postgresql 9.6


คุณทดสอบคำค้นหานี้หรือไม่
lalithkumar

ที่นี่จำไว้รหัสเป็นประเภทอาร์เรย์เช่น {1,2,3,4}
user6161156

0

ให้รับความประทับใจเกี่ยวกับสิ่งที่พูดไปแล้ว ตัวอย่างเช่นคุณมีตารางที่มีงานบางอย่าง:

SELECT a.id,a.status,a.description FROM minicloud_tasks as a ORDER BY random();

 id |   status   |   description    
----+------------+------------------
  4 | processing | work on postgres
  6 | deleted    | need some rest
  3 | pending    | garden party
  5 | completed  | work on html

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

(processing, pending,  completed, deleted)

เคล็ดลับคือการให้ค่าสถานะแต่ละค่าเป็น interger และเรียงลำดับรายการเป็นตัวเลข:

SELECT a.id,a.status,a.description FROM minicloud_tasks AS a
  JOIN (
    VALUES ('processing', 1), ('pending', 2), ('completed', 3), ('deleted', 4)
  ) AS b (status, id) ON (a.status = b.status)
  ORDER BY b.id ASC;

ซึ่งนำไปสู่:

 id |   status   |   description    
----+------------+------------------
  4 | processing | work on postgres
  3 | pending    | garden party
  5 | completed  | work on html
  6 | deleted    | need some rest

เครดิต @ user80168


-1

ฉันเห็นด้วยกับผู้โพสต์อื่น ๆ ที่บอกว่า "อย่าทำอย่างนั้น" หรือ "SQL ไม่เก่ง" หากคุณต้องการจัดเรียงตามแง่มุมของความคิดเห็นให้เพิ่มคอลัมน์จำนวนเต็มอีกคอลัมน์หนึ่งในตารางของคุณเพื่อเก็บเกณฑ์การเรียงลำดับและเรียงตามค่านั้น เช่น "ORDER BY comments.sort DESC" หากคุณต้องการเรียงลำดับเหล่านี้ตามลำดับที่แตกต่างกันทุกครั้ง ... SQL จะไม่เหมาะกับคุณในกรณีนี้

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