ORDER BY IN değer listesi


166

Bir sürü yorum kapmak PostgreSQL 8.3 basit bir SQL sorgusu var. Ben sağlamak sıralı bir değerler listesi INiçinde yapı WHEREmaddesi:

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

Bu, yorumlarımın kimlikleri gibi rasgele bir sırayla döndürür 1,2,3,4.

Elde edilen satırları INyapı listesi gibi sıralanmış istiyorum (1,3,2,4).
Bunu nasıl başarabilirim?


Ve sadece (SQL saflığına rağmen) sıralama için yeni bir tablo oluşturmak tercih etmem.
fındıkkıran

2
Şimdi bir sürü cevabım var. Bazı oylama ve yorumlar alabilir miyim, bu yüzden kazanan olduğunu biliyorum! Teşekkürler :-)
fındıkkıran

Yanıtlar:


107

Bunu (PostgreSQL 8.2'de tanıtıldı) VALUES (), () ile kolayca yapabilirsiniz.

Sözdizimi şöyle olacaktır:

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 IN yan tümcesinde binlerce değer varsa ne olur? çünkü binlerce kayıt için yapmam gerekiyor
kamal

@kamal Bunun için kullandım with ordered_products as (select row_number() OVER (ORDER BY whatever) as reportingorder, id from comments) ... ORDER BY reportingorder.
Noumenon

66

Bulması çok zor olduğu ve yayılması gerektiğinden: mySQL'de bu çok daha basit yapılabilir , ancak diğer SQL'de çalışıp çalışmadığını bilmiyorum.

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

3
Değerlerin listesi temin edilmelidir iki kez iki farklı şekilde. O kadar basit değil. Kabul edilen cevabın sadece bir kez ihtiyacı vardır (daha ayrıntılı bir şekilde olsa bile). Modern Postgres ile daha da basittir (daha yeni yanıtlarda gösterildiği gibi). Ayrıca, bu soru sonuçta Postgres ile ilgili gibi görünüyor.
Erwin Brandstetter

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

54

Postgres 9.4 veya sonrasında, bu muhtemelen en basit ve en hızlıdır :

SELECT c.*
FROM   comments c
JOIN   unnest('{1,3,2,4}'::int[]) WITH ORDINALITY t(id, ord) USING (id)
ORDER  BY t.ord;

Detaylı açıklama:


46

Bu şekilde daha iyi olduğunu düşünüyorum:

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
Bunu bağlı değerlerle yapabildim, yani: ... order by id=? desc, id=? desc, id=? descve iyi çalışıyor gibi görünüyor :-)
KajMagnus 15:14

Postgres çalışır ve en iyi çözüm gibi görünüyor!
Mike Szyndel

Bu çözüm benim için hile yaptı, ama: Herkes bu çözümün performans açısından nasıl bir performans gösterdiğini araştırdı mı? Cümleler tarafından çoklu sipariş ekler. Bu nedenle (henüz test etmedim) artan sayıda sipariş kimliği ile katlanarak yavaşlayabilir? Bu konuda herhangi bir bilgi çok takdir edilecektir!
Fabian Schöner

1
HATA: hedef listeleri en fazla 1664 girişe sahip olabilir -> uzun sorgu çalıştırmaya çalıştığınızda ...
Fatkhan Fauzi

@Manngo MS SQL. Hangi sürümü hatırlayamıyorum. 2012 olabilir.
biko

43

İle Postgres 9.4 bu biraz daha kısa yapılabilir:

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;

Veya türetilmiş bir tablo olmadan biraz daha kompakt:

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

Her bir değere manuel olarak bir konum atama / koruma ihtiyacını ortadan kaldırır.

İle Postgres 9.6 Bu kullanılarak yapılabilir 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, değerler listesinin yalnızca bir kez belirtilmesi için kullanılır. Bu önemli değilse, aynı zamanda şu şekilde de yazılabilir:

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

Bütün bu tekrar etmez INlisteyi WHEREtekrar fıkra ORDER BY... Şimdi sadece MySQL için benzer bir şey bulmak için ... Bu en iyi yanıt IMHO yapar maddesi,
Stijn Witt de

1
En sevdiğim cevabım, ancak array_position'ın bigint ile çalışmadığını ve order by array_position(array[42,48,43], c.id::int);bazı durumlarda hatalara neden olabileceğini belirtmeniz gerektiğini unutmayın .
aaandre

1
Aşağıdaki döküm (en azından Postgres 12) iyi çalışıyor @aaandre array_position(array[42, 48, 43]::bigint[], c.id::bigint)gerek çok kesecek şekilde, bigintiçin int.
Vic

29

Postgres'de bunu yapmanın başka bir yolu da idxişlevi kullanmak olacaktır .

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

idxİlk önce burada açıklandığı gibi bir işlev oluşturmayı unutmayın : http://wiki.postgresql.org/wiki/Array_Index


11
Bu işlev artık PostgreSQL ile birlikte gelen bir uzantıda kullanılabilir: postgresql.org/docs/9.2/static/intarray.html Bunu yükleyin CREATE EXTENSION intarray;.
Alex Kahn

1
Amazon RDS kullanıcıları için ROR taşıma işlevi enable_extension, uygulama kullanıcınız rds_superusergrubun bir üyesi olduğu sürece bunu etkinleştirmenize izin verir .
Dave S.

PG 9.6.2'de PG :: Tanımsız İşlev: HATA: işlev idx (tamsayı [], tamsayı) mevcut değil
Yakob Ubaidi

Teşekkürler, @ AlexKahn yorumuyla birleştirildiğinde en iyi cevap
Andrew

21

Postgresql'de:

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

2
Hum ... eğer böcek position(id::text in '123,345,3,678'). Kimlik 3, kimlikten önce eşleşecek 345, değil mi?
alanjds

4
Sanırım haklısınız ve hem başlangıç ​​hem de bitiş sınırlayıcısına sahip olmanız gerekir, belki şöyle olabilir: konuma göre sırala (',' || id :: metin || ',' in ', 1,3,2,4, ')
Michael Rush

3

Bunu biraz araştırırken şu çözümü buldum:

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

Ancak bu oldukça ayrıntılı görünmektedir ve büyük veri kümelerinde performans sorunları olabilir. Herkes bu konularda yorum yapabilir mi?


7
Tabii, onlar hakkında yorum yapabilirim. SQL'in iyi olduğu ve iyi olmadığı şeyler vardır. SQL bu konuda iyi değil. Sonuçları sorgulama yaptığınız her hangi dilde sıralamanız yeterlidir; dişlerin çok ağlama ve gıcırdamasını önleyecektir. SQL küme odaklı bir dildir ve kümeler sıralı koleksiyonlar değildir.
kquinn

Hmmm ... Bu kişisel deneyime ve testlere dayanıyor mu? Test ettiğim deneyim, bunun sipariş için oldukça etkili bir teknik olduğudur. (Ancak, kabul edilen cevap genel olarak daha iyidir çünkü "IN (...)" yan tümcesini ortadan kaldırır). Herhangi bir makul sonuç kümesi boyutu için, seti türetmenin pahalı kısım olması gerektiğini unutmayın. Yüzlerce veya daha az kayda düştüğünde sıralama önemsizdir.
dkretz

INCümlede binlerce değer varsa ne olur ? çünkü binlerce kayıt için yapmam gerekiyor.
kamal

2

Bunu yapmak için, muhtemelen daha sonra seçiminizde ek bir sütun olarak kullanabileceğiniz sipariş için kimliklerin eşlemesini tanımlayan ek bir "SİPARİŞ" tablonuz olması gerektiğini düşünüyorum (kendi sorunuza yanıtınızı söylediklerini etkili bir şekilde yapın) sonra sıralayabilirsiniz.

Bu şekilde, veritabanında olmasını istediğiniz sıralamayı, nerede olması gerektiğini açıkça tanımlarsınız.


Bunu yapmanın doğru yolu gibi görünüyor. Ancak bu sipariş tablosunu anında oluşturmak istiyorum. Yanıtlardan birinde sabit bir tablo kullanmanızı önerdim. Yüzlerce veya binlerce yorumla uğraşırken bu performans olacak mı?
fındıkkıran

2

sans SEQUENCE, yalnızca 8.4'te çalışır:

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

ya da kötülüğü iyiye tercih ediyorsanız:

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

0

Ve burada sabit bir tablo çalışan ve kullanan başka bir çözüm ( 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

Ama yine de bunun performansından emin değilim.

Şimdi bir sürü cevabım var. Bazı oylama ve yorumlar alabilir miyim, bu yüzden kazanan olduğunu biliyorum!

Hepinize teşekkürler :-)


1
cevabınız depesz ile hemen hemen aynıdır, c.ID IN'yi (1,3,2,4) kaldırın. neyse onun daha iyi, o JOIN kullanır, mümkün olduğunca ANSI SQL katılma yolunu kullanın, tablo virgül tablosu kullanmayın. Cevabınızı dikkatlice okumalıydım, iki sütunu nasıl takma adlandıracağımı anlamaya zorlanıyorum, ilk önce bunu denedim: (değerler (1,1) x (id, sort_order), (3,2), (2,3), (4,4)) gibi y. ama boşuna: -D dikkatlice okuduysanız cevabınız bana bir ipucu verebilir :-)
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;

[DÜZENLE]

unnest henüz 8.3'te yerleşik değildir, ancak kendiniz bir tane oluşturabilirsiniz (herhangi birinin güzelliği *):

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

bu işlev herhangi bir türde çalışabilir:

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

Teşekkürler Michael ama benim PSQL için dürüst olmayan işlevi var gibi görünmüyor ve ben de dokümanlar herhangi bir söz bulamıyorum. Sadece 8.4 mü?
fındıkkıran

unnest henüz 8.3'te yerleşik değildir, ancak birini kendiniz uygulayabilirsiniz. yukarıdaki koda bakınız
Michael Buen

0

Bence bir dizi kullanan sürüm üzerinde hafif iyileşme:

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)

burada, [bbs] ids adında bir alanı olan ana tablodur ve ids, comments.id dosyasını depolayan dizidir.

postgresql 9.6 içinde geçti


bu sorguyu test ettin mi
lalithkumar

burada, ids, {1,2,3,4} gibi bir dizi türüdür.
user6161156

0

Daha önce söylenenler hakkında görsel bir izlenim edelim. Örneğin, bazı görevleri içeren bir tablonuz var:

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

Ve görev listesini durumuna göre sıralamak istersiniz. Durum, dize değerlerinin bir listesidir:

(processing, pending,  completed, deleted)

Hüner, her durum değerine bir tamsayı vermek ve listeyi sayısal olarak sıralamaktır:

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;

Hangi yol açar:

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

Credit @ user80168


-1

"Bunu yapma" veya "SQL bu konuda iyi değil" diyen diğer tüm posterleri kabul ediyorum. Yorumların bazı yönlerine göre sıralamak istiyorsanız, sıralama ölçütlerinizi tutmak ve bu değere göre sıralamak için tablolarınızdan birine başka bir tamsayı sütunu ekleyin. örneğin "ORDER BY comments.sort DESC" Bunları her seferinde farklı bir sırayla sıralamak istiyorsanız ... SQL bu durumda sizin için olmayacaktır.

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.