Sắp xếp theo thứ tự các giá trị trong mệnh đề SQL IN ()


154

Tôi tự hỏi liệu có cách nào (có thể là một cách tốt hơn) để sắp xếp theo thứ tự của các giá trị trong mệnh đề IN ().

Vấn đề là tôi có 2 truy vấn, một truy vấn lấy tất cả ID và lần thứ hai lấy tất cả thông tin. Cái đầu tiên tạo thứ tự của ID mà tôi muốn thứ hai đặt hàng theo. Các ID được đặt trong mệnh đề IN () theo đúng thứ tự.

Vì vậy, nó sẽ là một cái gì đó như (cực kỳ đơn giản):

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

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

Vấn đề là truy vấn thứ hai không trả về kết quả theo cùng thứ tự mà ID được đặt vào mệnh đề IN ().

Một giải pháp tôi đã tìm thấy là đặt tất cả các ID vào bảng tạm thời với trường tăng tự động sau đó được nối vào truy vấn thứ hai.

Có một lựa chọn tốt hơn?

Lưu ý: Vì truy vấn đầu tiên được chạy "bởi người dùng" và truy vấn thứ hai được chạy trong quy trình nền, không có cách nào để kết hợp truy vấn 2 thành 1 bằng các truy vấn phụ.

Tôi đang sử dụng MySQL, nhưng tôi nghĩ nó có thể hữu ích khi nó lưu ý những tùy chọn nào có cho các DB khác.

Câu trả lời:


186

Sử dụng FIELD()chức năng của MySQL :

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

FIELD() sẽ trả về chỉ số của tham số đầu tiên bằng tham số đầu tiên (khác với tham số đầu tiên).

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

sẽ trả lại 1

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

sẽ trả lại 3

Điều này sẽ làm chính xác những gì bạn muốn nếu bạn dán id vào IN()mệnh đề và FIELD()hàm theo cùng một thứ tự.


3
@Vojto sắp xếp theo một thứ tự tùy ý là chậm theo định nghĩa. Điều này làm tốt nhất của nó vì không có gì đảm bảo rằng INFIELDcác tham số là như nhau. Làm điều này trong một mã chương trình có thể nhanh hơn bằng cách sử dụng kiến ​​thức bổ sung đó. Tất nhiên, có thể khôn ngoan hơn khi đặt gánh nặng này lên máy khách hơn là máy chủ nếu bạn có ý định thực hiện máy chủ.
ivan_pozdeev

1
những gì về sqlite?
fnc12

15

Xem sau đây để có được dữ liệu sắp xếp.

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

Hai giải pháp mà mùa xuân đến trong tâm trí:

  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()Là từ Oracle; có thể bạn có locate()hay charindex()hoặc một cái gì đó như thế)


Đối với MySQL, FIELD_IN_SET () cũng có thể được sử dụng: dev.mysql.com/doc/refman/5.0/en/ Kẻ
Darryl Hein

6

Ans để có được dữ liệu sắp xếp.

SELECT ...
FROM ...
ORDER  BY FIELD(user_id,5,3,2,...,50)  LIMIT 10

6

Nếu bạn muốn thực hiện sắp xếp tùy ý trên một truy vấn bằng các giá trị được nhập bởi truy vấn trong MS SQL Server 2008+ , có thể thực hiện bằng cách tạo một bảng một cách nhanh chóng và thực hiện nối như vậy (sử dụng danh pháp từ 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

Nếu bạn thay thế câu lệnh VALUES bằng một thứ khác làm điều tương tự, nhưng trong ANSI SQL, thì nó sẽ hoạt động trên bất kỳ cơ sở dữ liệu SQL nào.

Lưu ý: Cột thứ hai trong bảng đã tạo (orderTbl.orderIdx) là cần thiết khi truy vấn bộ bản ghi lớn hơn 100 hoặc hơn. Ban đầu tôi không có cột orderIdx, nhưng thấy rằng với các tập kết quả lớn hơn 100 tôi phải sắp xếp rõ ràng theo cột đó; trong SQL Server Express 2014 dù sao đi nữa.


Câu hỏi này liên quan đến MySQL ... không chắc truy vấn của bạn sẽ hoạt động trong MySQL.
Darryl Hein

2
@Darryl, nó sẽ không. Nhưng bài đăng này là kết quả hàng đầu khi bạn google cách đặt hàng theo mệnh đề IN, vì vậy tôi cho rằng tôi cũng có thể đăng nó ở đây và phương pháp chung áp dụng cho tất cả các máy chủ SQL tuân thủ ANSI (chỉ cần thay thế câu lệnh VALUES bằng một tiêu chuẩn cách tạo bảng).
Ian

Lưu ý, điều này có hiệu quả với tôi, nhưng chỉ sau khi tôi thay thế các tham chiếu đến orderTbl.orderKey, orderTbl.orderIndexbởi orderKey,orderIndex
Peter

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)

làm việc rất tuyệt


Làm việc cho tôi trên Oracle. Nhanh chóng và dễ dàng. Cảm ơn.
Menachem

4

Mệnh đề IN mô tả một tập hợp các giá trị và các tập hợp không có thứ tự.

Giải pháp của bạn với một liên kết và sau đó đặt hàng trên display_ordercột là giải pháp gần như đúng nhất; bất cứ điều gì khác có lẽ là một hack cụ thể DBMS (hoặc đang thực hiện một số nội dung với các hàm OLAP trong SQL tiêu chuẩn). Chắc chắn, tham gia là giải pháp gần như di động nhất (mặc dù việc tạo dữ liệu với các display_ordergiá trị có thể có vấn đề). Lưu ý rằng bạn có thể cần phải chọn các cột đặt hàng; đó từng là một yêu cầu trong SQL tiêu chuẩn, mặc dù tôi tin rằng nó đã được nới lỏng như một quy tắc trước đây (có thể là từ lâu như SQL-92).


2

Đối với Oracle, giải pháp của John sử dụng hàm Bars () hoạt động. Đây là giải pháp hơi khác biệt có hiệu quả - SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)



2

Tôi vừa thử làm điều này là MS SQL Server nơi chúng tôi không có 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

Lưu ý rằng tôi cũng cho phép sao chép.


1

Suy nghĩ đầu tiên của tôi là viết một truy vấn duy nhất, nhưng bạn nói rằng điều đó là không thể bởi vì một cái được chạy bởi người dùng và cái kia được chạy trong nền. Làm thế nào bạn lưu trữ danh sách các id để chuyển từ người dùng sang quá trình nền? Tại sao không đặt chúng trong một bảng tạm thời với một cột để biểu thị thứ tự.

Vậy làm thế nào về điều này:

  1. Bit giao diện người dùng chạy và chèn các giá trị vào một bảng mới mà bạn tạo. Nó sẽ chèn id, vị trí và một số loại định danh số công việc)
  2. Số công việc được chuyển đến quá trình nền (thay vì tất cả các id)
  3. Quá trình nền thực hiện chọn từ bảng trong bước 1 và bạn tham gia để có được thông tin khác mà bạn yêu cầu. Nó sử dụng số công việc trong mệnh đề WHERE và các đơn đặt hàng theo cột vị trí.
  4. Quá trình nền, khi kết thúc, xóa khỏi bảng dựa trên mã định danh công việc.

1

Tôi nghĩ bạn nên quản lý để lưu trữ dữ liệu của mình theo cách đơn giản là bạn sẽ tham gia và nó sẽ hoàn hảo, vì vậy không có hack và những điều phức tạp đang diễn ra.

Ví dụ, tôi có một danh sách id theo dõi "được phát gần đây", trên SQLite tôi chỉ cần làm:

SELECT * FROM recently NATURAL JOIN tracks;

1
Ôi, ước gì cuộc sống thật đơn giản :)
Darryl Hein

0

Cung cấp cho một shot:

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

WHERE có thể sẽ có một chút tinh chỉnh để làm cho các truy vấn con tương quan hoạt động đúng, nhưng nguyên tắc cơ bản phải là âm thanh.


xem ghi chú và cũng như đã nêu, các ví dụ truy vấn cực kỳ đơn giản, vì vậy tôi không thể kết hợp chúng thành 1 truy vấn với các truy vấn phụ.
Darryl Hein
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.