SQL IN () yan tümcesindeki değerlerin sırasına göre sıralama


154

Ben bir IN () yan tümcesinde değer sırasına göre sipariş (muhtemelen daha iyi bir yol) olup olmadığını merak ediyorum.

Sorun 2 sorguları var, biri tüm kimlikleri alır ve ikincisi tüm bilgileri alır. Birincisi, ikincisinin sipariş etmesini istediğim kimliklerin sırasını oluşturur. Kimlikler IN () yan tümcesinde doğru sırada yerleştirilir.

Yani şöyle bir şey olurdu (son derece basitleştirilmiş):

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

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

Sorun ikinci sorgunun sonuçları IN () yantümcesine yerleştirilenlerle aynı sırayla döndürmemesi.

Bulduğum bir çözüm, tüm kimlikleri daha sonra ikinci sorguya birleştirilen otomatik artan bir alana sahip bir geçici tabloya koymaktır.

Daha iyi bir seçenek var mı?

Not: İlk sorgu "kullanıcı tarafından" ve ikinci sorgu bir arka plan işleminde çalıştırıldığından, alt sorguları kullanarak 2'yi 1 sorgusuna birleştirmenin bir yolu yoktur.

MySQL kullanıyorum, ancak diğer DB'ler için de hangi seçeneklerin bulunduğunu belirtmenin yararlı olabileceğini düşünüyorum.

Yanıtlar:


186

MySQL'in FIELD()fonksiyonunu kullanın :

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

FIELD() ilk parametreye (ilk parametrenin kendisinden başka) eşit olan ilk parametrenin dizinini döndürür.

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

geri dönecek 1

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

geri dönecek 3

Bu, kimlikleri IN()ve FIELD()işlevi aynı sırayla yapıştırırsanız tam olarak istediğinizi yapar .


3
@Vojto rasgele bir sıraya göre sıralama tanım gereği yavaştır. Hiçbir garantisi yoktur çünkü bu elinden geleni yapar INve FIELDeşit parametreleri. Bunu bir program kodunda yapmak, bu ek bilgiyi kullanarak daha hızlı olabilir. Tabii ki, sunucu performansınız varsa, bu yükü sunucu yerine istemciye koymak daha akıllıca olabilir.
ivan_pozdeev

1
Ne hakkında sqlite?
fnc12

15

Sıralı verilerin nasıl alınacağını takip edin.

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

Akla gelen iki çözüm:

  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'dan; belki de böyle bir şeye sahipsiniz locate()veya charindex()böyle bir şey)


MySQL için FIELD_IN_SET () da kullanılabilir: dev.mysql.com/doc/refman/5.0/en/…
Darryl Hein

6

Sıralı veri almak için Ans.

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

6

MS SQL Server 2008+ uygulamasında sorgu tarafından girilen değerleri kullanarak bir sorgu üzerinde rasgele sıralama yapmak istiyorsanız , bu işlem anında bir tablo oluşturarak ve böyle bir birleştirme yaparak ( OP'den adlandırmayı kullanarak) yapılabilir.

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 deyimini aynı şeyi yapan başka bir şeyle değiştirirseniz, ancak ANSI SQL'de değiştirirseniz, bunun herhangi bir SQL veritabanında çalışması gerekir.

Not: Oluşturulan tablodaki ikinci sütun (orderTbl.orderIdx), 100'den büyük kayıt kümelerini sorgularken gereklidir. Aslında bir orderIdx sütun yoktu, ancak 100'den büyük sonuç kümeleri ile açıkça bu sütuna göre sıralamak zorunda bulundu; SQL Server Express 2014'te zaten.


Bu soru MySQL ile ilgili ... sorgunuzun MySQL'de çalışacağından emin değilim.
Darryl Hein

2
@Darryl, olmaz. Ancak bu yazı IN yan tümcesi tarafından sipariş nasıl google en iyi sonuçları olarak geliyor, bu yüzden ben de burada gönderebilir ve genel yöntem tüm ANSI uyumlu SQL sunucuları için geçerli olduğunu düşündüm (sadece bir standart ile VALUES deyimi değiştirin tablo oluşturmanın yolu).
Ian

Not bu benim için çalıştı, ama başvurular yerini sonra orderTbl.orderKey, orderTbl.orderIndextarafından 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)

gerçekten harika çalıştı


Benim için Oracle üzerinde çalıştı. Çabuk ve kolay. Teşekkürler.
Menachem

4

IN yan tümcesi bir değer kümesini tanımlar ve kümelerin sırası yoktur.

Birleştirme ve daha sonra display_ordersütun üzerinde sipariş ile çözümünüz neredeyse doğru çözümdür; başka bir şey muhtemelen DBMS'ye özel bir saldırıdır (veya standart SQL'deki OLAP işlevleriyle bazı şeyler yapıyor). Kesinlikle, birleştirme en neredeyse taşınabilir çözümdür ( display_orderdeğerlerle veri oluşturmak sorunlu olabilir). Sipariş sütunlarını seçmeniz gerekebileceğini unutmayın; Bir süre önce (belki de SQL-92 kadar uzun bir süre önce) bir kural olarak rahat olduğuna inanıyorum rağmen standart SQL'de bir gereksinimdi.


2

Oracle için John'un instr () işlevini kullanan çözümü çalışır. İşte işe yarayan biraz farklı bir çözüm - SELECT id FROM table1 WHERE id IN (1, 20, 45, 60) ORDER BY instr('1, 20, 45, 60', id)



2

Ben sadece bunu SAELD () yok MS SQL Server yapmaya çalıştım:

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

Ben de çoğaltmaya izin veriyorum.


1

İlk düşüncem tek bir sorgu yazmaktı, ancak bunun bir kullanıcı tarafından ve diğerinin arka planda çalıştırılması nedeniyle mümkün olmadığını söylediniz. Kullanıcıdan arka plan işlemine geçirilecek kimliklerin listesini nasıl saklıyorsunuz? Neden siparişi belirtmek için bunları sütun içeren geçici bir tabloya koymuyorsunuz.

Peki buna ne dersiniz:

  1. Kullanıcı arabirimi biti çalışır ve oluşturduğunuz yeni bir tabloya değerler ekler. Kimlik, konum ve bir tür iş numarası tanımlayıcısı ekler)
  2. İş numarası arka plan işlemine aktarılır (tüm kimlikler yerine)
  3. Arka plan işlemi, 1. adımdaki tablodan bir seçim yapar ve istediğiniz diğer bilgileri almak için katılırsınız. WHERE yan tümcesindeki iş numarasını ve konum sütununa göre sıraları kullanır.
  4. Arka plan işlemi, işiniz bittiğinde, iş tanımlayıcısına göre tablodan silinir.

1

Bence verilerinizi sadece birleştirme yapacağınız şekilde depolamayı başarmalısınız ve mükemmel olacak, bu yüzden hack ve karmaşık şeyler olmayacak.

Örneğin sadece basitçe yapmak SQLite parça kimlikleri "Son zamanlarda çalınan" bir listesi var:

SELECT * FROM recently NATURAL JOIN tracks;

1
Oh keşke hayat bu kadar basit olsaydı :)
Darryl Hein

0

Bunu deneyin:

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'ler, ilişkili alt sorguların düzgün çalışmasını sağlamak için muhtemelen biraz tweaking alacaktır, ancak temel ilke sağlam olmalıdır.


nota bakın ve ayrıca belirtildiği gibi sorgu örnekleri son derece basitleştirilmiştir, bu yüzden onları alt sorgularla 1 sorguda birleştiremiyorum.
Darryl Hein
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.