ĐẶT HÀNG theo danh sách giá trị IN


165

Tôi có một truy vấn SQL đơn giản trong PostgreQuery 8.3, lấy một loạt các bình luận. Tôi cung cấp một danh sách sắp xếp các giá trị cho INcấu trúc trong WHEREmệnh đề:

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

Điều này trả về các bình luận theo thứ tự tùy ý mà trong tôi tình cờ là id như thế nào 1,2,3,4.

Tôi muốn các hàng kết quả được sắp xếp như danh sách trong INcấu trúc : (1,3,2,4).
Làm thế nào để đạt được điều đó?


Và tôi không muốn tạo một bảng mới chỉ để sắp xếp (mặc dù độ tinh khiết của SQL).
nutcracker

2
Tôi đã có một loạt các câu trả lời ngay bây giờ. Tôi có thể nhận được một số bình chọn và ý kiến ​​để tôi biết ai là người chiến thắng! Cảm ơn tất cả :-)
nutcracker

Câu trả lời:


107

Bạn có thể làm điều đó khá dễ dàng với (được giới thiệu trong PostgreSQL 8.2) VALUES (), ().

Cú pháp sẽ như thế này:

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 Nếu có hàng ngàn giá trị trong mệnh đề IN thì sao? bởi vì tôi phải làm điều đó cho hàng ngàn hồ sơ
kamal

@kamal Cho rằng tôi đã sử dụng with ordered_products as (select row_number() OVER (ORDER BY whatever) as reportingorder, id from comments) ... ORDER BY reportingorder.
Noumenon

66

Chỉ vì nó rất khó tìm và nó phải được lan truyền: trong myQuery, việc này có thể được thực hiện đơn giản hơn nhiều , nhưng tôi không biết liệu nó có hoạt động trong SQL khác không.

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

3
Danh sách các giá trị phải được cung cấp hai lần , theo hai cách khác nhau. Không đơn giản lắm. Câu trả lời được chấp nhận chỉ cần một lần (ngay cả khi theo kiểu dài dòng hơn). Và nó thậm chí còn đơn giản hơn với Postgres hiện đại (như thể hiện trong các câu trả lời mới hơn). Ngoài ra, câu hỏi này dường như là về Postgres.
Erwin Brandstetter

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

54

Trong Postgres 9.4 trở lên, đây có lẽ là đơn giản và nhanh nhất :

SELECT c.*
FROM   comments c
JOIN   unnest('{1,3,2,4}'::int[]) WITH ORDINALITY t(id, ord) USING (id)
ORDER  BY t.ord;
  • Sử dụng cái mới WITH ORDINALITY, mà @a_horse đã đề cập .

  • Chúng ta không cần truy vấn con, chúng ta có thể sử dụng hàm trả về tập hợp như bảng.

  • Một chuỗi ký tự bằng tay trong mảng thay vì hàm tạo ARRAY có thể dễ thực hiện hơn với một số khách hàng.

Giải thích chi tiết:


46

Tôi nghĩ rằng cách này là tốt hơn:

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
Tôi đã có thể làm điều này với các giá trị ràng buộc, nghĩa là: ... order by id=? desc, id=? desc, id=? descvà nó dường như hoạt động tốt :-)
KajMagnus 15/03/2016

Hoạt động trong postgres và dường như là giải pháp tốt nhất!
Mike Szyndel

Giải pháp này đã tạo ra mánh khóe cho tôi, nhưng: Có ai nghiên cứu cách giải pháp này thực hiện hiệu quả không? Nó không thêm thứ tự nhiều bằng mệnh đề. Vì vậy, nó có thể (tôi chưa kiểm tra nó) chậm hơn theo cấp số nhân với số lượng id đơn hàng ngày càng tăng? Bất kỳ thông tin về điều này sẽ được đánh giá cao!
Fabian Schöner

1
LRI: danh sách mục tiêu có thể có tối đa 1664 mục -> khi bạn cố chạy truy vấn dài ...
Fatkhan Fauzi

@Quản lý MS SQL. Không thể nhớ phiên bản nào. Có thể là năm 2012.
biko

42

Với Postgres 9.4, điều này có thể được thực hiện ngắn hơn một chút:

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;

Hoặc nhỏ gọn hơn một chút mà không có bảng dẫn xuất:

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

Loại bỏ sự cần thiết phải tự gán / duy trì một vị trí cho từng giá trị.

Với Postgres 9.6, điều này có thể được thực hiện bằng cách sử dụng 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 được sử dụng để danh sách các giá trị chỉ cần được chỉ định một lần. Nếu điều đó không quan trọng thì điều này cũng có thể được viết là:

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

Điều này không lặp lại toàn bộ INdanh sách từ WHEREmệnh đề một lần nữa trong ORDER BYmệnh đề, điều này làm cho câu trả lời này tốt nhất ... Bây giờ chỉ để tìm một cái gì đó tương tự cho MySQL ...
Stijn de Witt

1
Câu trả lời yêu thích của tôi nhưng lưu ý rằng mảng_poseition không hoạt động với bigint và bạn cần phải sử dụng: order by array_position(array[42,48,43], c.id::int);điều này, có thể dẫn đến lỗi trong một số trường hợp.
aaandre

1
@aaandre Dàn diễn viên sau đây được làm việc tốt (trong Postgres 12 ít nhất) array_position(array[42, 48, 43]::bigint[], c.id::bigint), vì vậy không cần phải truncate bigintđể int.
Vic

29

Một cách khác để làm điều đó trong Postgres là sử dụng idxchức năng.

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

Đừng quên tạo idxchức năng trước, như được mô tả ở đây: http://wiki.postgresql.org/wiki/Array_Index


11
Chức năng này hiện có sẵn trong một tiện ích mở rộng đi kèm với PostgreSQL: postgresql.org/docs/9.2/static/intarray.html Cài đặt nó với CREATE EXTENSION intarray;.
Alex Kahn

1
Chỉ cần chồng chất thêm, đối với người dùng Amazon RDS, chức năng di chuyển ROR enable_extensionsẽ cho phép bạn kích hoạt điều này miễn là người dùng ứng dụng của bạn là thành viên của rds_superusernhóm.
Dave S.

trong PG 9.6.2 PG :: Không xác định Chức năng: ERROR: hàm idx (số nguyên [], số nguyên) không tồn tại
Yakob Ubaidi

Cảm ơn bạn, câu trả lời hay nhất khi được kết hợp với nhận xét của @ AlexKahn
Andrew

21

Trong Postgresql:

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

2
Hừm ... nó lỗi nếu position(id::text in '123,345,3,678'). Id 3sẽ khớp trước id 345, phải không?
alanjds

4
Tôi nghĩ rằng bạn đã đúng và cần phải có cả dấu phân cách bắt đầu và kết thúc, có thể như: sắp xếp theo vị trí (',' | | id :: text || ',' in ', 1,3,2,4, ')
Michael Rush

3

Nghiên cứu thêm về điều này tôi đã tìm thấy giải pháp này:

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

Tuy nhiên, điều này có vẻ khá dài dòng và có thể có vấn đề về hiệu năng với các bộ dữ liệu lớn. Bất cứ ai có thể nhận xét về những vấn đề này?


7
Chắc chắn, tôi có thể nhận xét về họ. Có những thứ SQL giỏi, và có những thứ nó không giỏi. SQL không tốt trong việc này. Chỉ cần sắp xếp kết quả theo bất kỳ ngôn ngữ nào bạn thực hiện truy vấn; nó sẽ giúp bạn tiết kiệm nhiều khóc lóc và nghiến răng. SQL là ngôn ngữ hướng theo tập hợp và các tập hợp không được sắp xếp theo bộ sưu tập.
kquinn

Hmmm ... Có phải đó là dựa trên kinh nghiệm và thử nghiệm cá nhân? Kinh nghiệm thử nghiệm của tôi là đây là một kỹ thuật khá hiệu quả để đặt hàng. (Tuy nhiên, câu trả lời được chấp nhận là tổng thể tốt hơn vì nó loại bỏ mệnh đề "IN (...)"). Hãy nhớ rằng đối với bất kỳ kích thước tập kết quả hợp lý nào, việc lấy tập hợp phải là phần đắt tiền. Một khi nó xuống tới vài trăm bản ghi hoặc ít hơn, việc sắp xếp là chuyện nhỏ.
dkretz

Điều gì nếu có hàng ngàn giá trị trong INmệnh đề? bởi vì tôi phải làm điều đó cho hàng ngàn hồ sơ.
kamal

2

Để làm điều này, tôi nghĩ rằng bạn có lẽ nên có một bảng "ĐẶT HÀNG" bổ sung để xác định ánh xạ ID để đặt hàng (thực hiện hiệu quả câu trả lời của bạn cho câu hỏi của bạn), sau đó bạn có thể sử dụng làm cột bổ sung cho lựa chọn của mình sau đó bạn có thể sắp xếp

Theo cách đó, bạn mô tả rõ ràng thứ tự bạn mong muốn trong cơ sở dữ liệu, nơi cần có.


Điều này có vẻ như đúng cách để làm điều đó. Tuy nhiên, tôi muốn tạo bảng đặt hàng đó một cách nhanh chóng. Tôi đã đề nghị sử dụng một bảng không đổi trong một trong những câu trả lời. Đây có phải là hiệu suất khi tôi xử lý hàng trăm hoặc hàng ngàn bình luận?
nutcracker

2

sans SEQUENCE, chỉ hoạt động trên 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

hoặc nếu bạn thích cái ác hơn cái thiện:

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

0

Và đây là một giải pháp khác hoạt động và sử dụng bảng không đổi ( 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

Nhưng một lần nữa tôi không chắc chắn rằng đây là biểu diễn.

Tôi đã có một loạt các câu trả lời ngay bây giờ. Tôi có thể nhận được một số bình chọn và ý kiến ​​để tôi biết ai là người chiến thắng!

Cảm ơn tất cả :-)


1
câu trả lời của bạn gần giống với depesz, chỉ cần xóa c.ID IN (1,3,2,4). dù sao thì anh ta cũng tốt hơn, anh ta sử dụng THAM GIA, càng nhiều càng tốt, sử dụng cách tham gia ANSI SQL, không sử dụng bảng dấu phẩy. Tôi nên đọc câu trả lời của bạn một cách cẩn thận, tôi đang gặp khó khăn khi tìm ra cách đặt bí danh cho hai cột, đầu tiên tôi đã thử điều này: (giá trị (1,1) là x (id, sort_order), (3,2), (2,3), (4,4)) là y. nhưng vô ích :- Câu trả lời của bạn có thể cung cấp cho tôi manh mối nếu tôi đã đọc kỹ :-)
Michael 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;

[BIÊN TẬP]

unest chưa được tích hợp sẵn trong 8.3, nhưng bạn có thể tự tạo một cái (vẻ đẹp của bất kỳ *):

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;
$$;

chức năng đó có thể hoạt động trong bất kỳ loại nào:

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

Cảm ơn Michael nhưng chức năng không tốt nhất dường như không tồn tại đối với PSQL của tôi và tôi cũng không thể tìm thấy bất kỳ đề cập nào về nó trong các tài liệu. Có phải là 8.4 không?
nutcracker

Unest chưa được tích hợp sẵn trong 8.3, nhưng bạn có thể tự thực hiện. xem mã ở trên
Michael Buen

0

Tôi nghĩ rằng cải tiến nhẹ so với phiên bản sử dụng trình tự:

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)

ở đây, [bbs] là bảng chính có một trường được gọi là id và id là mảng lưu trữ các bình luận.

thông qua trong postgresql 9.6


Bạn đã kiểm tra truy vấn này?
lalithkumar

ở đây, hãy nhớ rằng, id là một kiểu mảng, như, {1,2,3,4}.
dùng6161156

0

Hãy để có ấn tượng thị giác về những gì đã nói. Ví dụ: bạn có một bảng với một số tác vụ:

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

Và bạn muốn sắp xếp danh sách các nhiệm vụ theo trạng thái của nó. Trạng thái là danh sách các giá trị chuỗi:

(processing, pending,  completed, deleted)

Bí quyết là cung cấp cho mỗi giá trị trạng thái một interger và sắp xếp danh sách bằng số:

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;

Điều này dẫn đến:

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

Tín dụng @ user80168


-1

Tôi đồng ý với tất cả các áp phích khác nói rằng "không làm điều đó" hoặc "SQL không tốt ở đó". Nếu bạn muốn sắp xếp theo một số khía cạnh của nhận xét thì hãy thêm một cột số nguyên khác vào một trong các bảng của bạn để giữ tiêu chí sắp xếp của bạn và sắp xếp theo giá trị đó. ví dụ: "ĐẶT HÀNG theo ý kiến.sort DESC" Nếu bạn muốn sắp xếp chúng theo thứ tự khác nhau mỗi lần thì ... SQL sẽ không dành cho bạn trong trường hợp này.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.