Memesan berdasarkan urutan nilai dalam klausa SQL IN ()

154

Saya bertanya-tanya apakah ada jauh (mungkin cara yang lebih baik) untuk memesan dengan urutan nilai-nilai dalam klausa IN ().

Masalahnya adalah bahwa saya memiliki 2 pertanyaan, yang mendapatkan semua ID dan yang kedua yang mengambil semua informasi. Yang pertama membuat urutan ID yang saya ingin yang kedua memesan. ID dimasukkan dalam klausa IN () dengan urutan yang benar.

Jadi itu akan menjadi sesuatu seperti (sangat disederhanakan):

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

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

Masalahnya adalah bahwa kueri kedua tidak mengembalikan hasil dalam urutan yang sama bahwa ID dimasukkan ke dalam klausa IN ().

Salah satu solusi yang saya temukan adalah dengan meletakkan semua ID ke tabel temp dengan bidang penambahan otomatis yang kemudian bergabung ke dalam kueri kedua.

Apakah ada opsi yang lebih baik?

Catatan: Karena kueri pertama dijalankan "oleh pengguna" dan kueri kedua dijalankan dalam proses latar belakang, tidak ada cara untuk menggabungkan kueri 2 menjadi 1 menggunakan sub kueri.

Saya menggunakan MySQL, tapi saya pikir mungkin berguna untuk mencatat opsi apa yang ada untuk DB lain juga.

Darryl Hein
sumber

Jawaban:

186

Gunakan FIELD()fungsi MySQL :

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

FIELD() akan mengembalikan indeks parameter pertama yang sama dengan parameter pertama (selain parameter pertama itu sendiri).

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

akan kembali 1

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

akan kembali 3

Ini akan melakukan apa yang Anda inginkan jika Anda menempelkan id ke dalam IN()klausa dan FIELD()fungsi dalam urutan yang sama.

ʞɔıu
sumber
3
@ Vojto menyortir dengan perintah sewenang-wenang lambat menurut definisi. Ini melakukan yang terbaik karena tidak ada jaminan INdan FIELDparameter sama. Melakukan ini dalam kode program mungkin lebih cepat dengan menggunakan pengetahuan tambahan itu. Tentu saja, akan lebih bijaksana untuk menempatkan beban ini pada klien daripada server jika Anda memiliki kinerja server dalam pikiran.
ivan_pozdeev
1
bagaimana sqlite?
fnc12
15

Lihat mengikuti cara mendapatkan data yang diurutkan.

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
Pradeep Singh
sumber
11

Dua solusi yang muncul dalam pikiran:

  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()berasal dari Oracle; mungkin Anda memiliki locate()atau charindex()atau sesuatu seperti itu)

John Nilsson
sumber
Untuk MySQL, FIELD_IN_SET () juga dapat digunakan: dev.mysql.com/doc/refman/5.0/en/…
Darryl Hein
6

Ans untuk mendapatkan data yang diurutkan.

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

Jika Anda ingin melakukan pengurutan sewenang-wenang pada kueri menggunakan nilai yang dimasukkan oleh kueri di MS SQL Server 2008+ , itu bisa dilakukan dengan membuat tabel saat itu juga dan melakukan penggabungan seperti itu (menggunakan nomenklatur dari 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

Jika Anda mengganti pernyataan VALUES dengan sesuatu yang lain melakukan hal yang sama, tetapi dalam ANSI SQL, maka ini harus bekerja pada basis data SQL apa pun.

Catatan: Kolom kedua dalam tabel yang dibuat (orderTbl.orderIdx) diperlukan ketika kueri catatan set lebih besar dari 100 atau lebih. Saya awalnya tidak memiliki kolom orderIdx, tetapi menemukan bahwa dengan set hasil lebih besar dari 100 saya harus secara eksplisit mengurutkan berdasarkan kolom itu; dalam SQL Server Express 2014.

Ian
sumber
Pertanyaan ini tentang MySQL ... tidak yakin permintaan Anda akan berfungsi di MySQL.
Darryl Hein
2
@ Darryl, tidak akan. Tapi posting ini muncul sebagai hasil teratas ketika Anda google cara memesan dengan klausa IN, jadi saya pikir saya mungkin juga mempostingnya di sini dan metodologi umum berlaku untuk semua server SQL yang sesuai dengan ANSI (hanya ganti pernyataan VALUES dengan standar cara membuat tabel).
Ian
Catatan, ini berhasil untuk saya, tetapi hanya setelah saya mengganti referensi dengan orderTbl.orderKey, orderTbl.orderIndexoleh 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)

bekerja sangat bagus

Ravi Ranjan
sumber
Bekerja untuk saya di Oracle. Cepat dan mudah. Terima kasih.
Menachem
4

Klausa IN menjelaskan seperangkat nilai, dan set tidak memiliki urutan.

Solusi Anda dengan gabungan dan kemudian memesan pada display_orderkolom adalah solusi yang hampir benar; hal lain mungkin merupakan peretas khusus DBMS (atau sedang melakukan beberapa hal dengan fungsi OLAP dalam SQL standar). Tentu saja, gabungannya adalah solusi yang hampir portabel (meskipun menghasilkan data dengan display_ordernilai - nilai mungkin bermasalah). Perhatikan bahwa Anda mungkin perlu memilih kolom pemesanan; yang dulunya persyaratan dalam SQL standar, meskipun saya percaya itu santai sebagai aturan beberapa waktu lalu (mungkin dulu sebagai SQL-92).

Jonathan Leffler
sumber
2

Untuk Oracle, solusi John menggunakan fungsi instr () berfungsi. Inilah solusi yang sedikit berbeda yang berhasil - SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)

V Patel
sumber
2

Gunakan fungsi FIND_IN_SET MySQL :

  SELECT * 
    FROM table_name 
   WHERE id IN (..,..,..,..) 
ORDER BY FIND_IN_SET (coloumn_name, .., .., ..);
Sarthak Sawhney
sumber
2

Saya hanya mencoba melakukan ini adalah MS SQL Server di mana kami tidak memiliki 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

Perhatikan bahwa saya juga memperbolehkan duplikasi.

Tony
sumber
1

Pikiran pertama saya adalah menulis satu permintaan, tetapi Anda mengatakan itu tidak mungkin karena satu dijalankan oleh pengguna dan yang lainnya dijalankan di latar belakang. Bagaimana Anda menyimpan daftar id untuk diteruskan dari pengguna ke proses latar belakang? Mengapa tidak meletakkannya di tabel sementara dengan kolom untuk menandakan pesanan.

Jadi bagaimana dengan ini:

  1. Bit antarmuka pengguna berjalan dan memasukkan nilai ke dalam tabel baru yang Anda buat. Itu akan memasukkan id, posisi dan semacam pengidentifikasi nomor pekerjaan)
  2. Nomor pekerjaan diteruskan ke proses latar belakang (bukan semua id)
  3. Proses latar belakang memilih dari tabel pada langkah 1 dan Anda bergabung untuk mendapatkan informasi lain yang Anda butuhkan. Ia menggunakan nomor pekerjaan dalam klausa dan perintah WHERE oleh kolom posisi.
  4. Proses latar belakang, setelah selesai, dihapus dari tabel berdasarkan pengidentifikasi pekerjaan.
WW.
sumber
1

Saya pikir Anda harus mengelola untuk menyimpan data Anda dengan cara yang Anda hanya akan bergabung dan itu akan sempurna, sehingga tidak ada peretasan dan hal-hal rumit yang terjadi.

Misalnya saya memiliki daftar "track baru-baru ini" dari id lagu, di SQLite saya cukup lakukan:

SELECT * FROM recently NATURAL JOIN tracks;
kroe
sumber
1
Oh saya berharap hidup sesederhana itu :)
Darryl Hein
0

Coba ini:

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 mungkin perlu sedikit penyesuaian agar subquery yang berkorelasi berfungsi dengan baik, tetapi prinsip dasarnya haruslah baik.

kekacauan
sumber
lihat catatan dan juga, seperti yang dinyatakan, contoh kueri sangat sederhana, jadi saya tidak bisa menggabungkannya menjadi 1 kueri dengan sub kueri.
Darryl Hein