web-dev-qa-db-de.com

BESTELLEN DURCH die IN-Werteliste

Ich habe eine einfache SQL-Abfrage in PostgreSQL 8.3, die einige Kommentare enthält. Ich biete eine sortierte Liste von Werten an das IN-Konstrukt in der WHERE-Klausel

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

Dies gibt Kommentare in einer willkürlichen Reihenfolge zurück, bei der es sich in meiner Regel um IDs wie 1,2,3,4 handelt.

Ich möchte, dass die resultierenden Zeilen wie die Liste im Konstrukt IN sortiert werden: (1,3,2,4).
.__ Wie erreicht man das?

121
nutcracker

Mit (in PostgreSQL 8.2 eingeführten) VALUES (), () ist das problemlos möglich.

Die Syntax wird wie folgt aussehen:

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
79
user80168

Nur weil es so schwer zu finden ist und verbreitet werden muss: In mySQL kann dies viel einfacher gemacht werden , aber ich weiß nicht, ob es in anderen SQL-Versionen funktioniert.

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

Ich finde dieser Weg ist besser:

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
40

In Postgres 9.4 oder höher ist dies wahrscheinlich einfachste und schnellste:

SELECT c.*
FROM   comments c
JOIN   unnest('{1,3,2,4}'::int[]) WITH ORDINALITY t(id, ord) USING (id)
ORDER  BY t.ord;
  • Mit dem neuen WITH ORDINALITY wird das @a_horse bereits erwähnt .

  • Wir benötigen keine Unterabfrage, wir können die set-returning-Funktion wie eine Tabelle verwenden.

  • Ein String-Literal, das anstelle eines ARRAY-Konstruktors im Array übergeben wird, kann bei einigen Clients möglicherweise einfacher implementiert werden.

Ausführliche Erklärung:

36

Eine andere Möglichkeit, dies in Postgres zu tun, wäre die Verwendung der Funktion idx.

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

Vergessen Sie nicht, zuerst die idx-Funktion zu erstellen, wie hier beschrieben: http://wiki.postgresql.org/wiki/Array_Index

27
Carl Mercier

Mit Postgres 9.4 kann dies etwas kürzer erfolgen:

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

Die Notwendigkeit, jedem Wert manuell eine Position zuzuweisen/beizubehalten, entfällt. 

Mit Postgres 9.6 kann dies mit array_position() erfolgen:

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

Der CTE wird verwendet, damit die Liste der Werte nur einmal angegeben werden muss. Wenn das nicht wichtig ist, kann dies auch geschrieben werden als:

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

In Postgresql:

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

Bei der Recherche fand ich diese Lösung:

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

Dies scheint jedoch recht ausführlich zu sein und kann zu Leistungsproblemen bei großen Datensätzen führen. Kann sich jemand zu diesen Problemen äußern?

4
nutcracker

sans SEQUENCE, funktioniert nur am 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
2
Michael Buen

Ich denke, Sie sollten wahrscheinlich eine zusätzliche "ORDER" -Tabelle haben, in der die Zuordnung von IDs nach Reihenfolge definiert wird (was die Antwort auf Ihre eigene Frage bedeutet), die Sie als zusätzliche Spalte in Ihrer Auswahl verwenden können Sie können dann weiter sortieren.

Auf diese Weise beschreiben Sie explizit die gewünschte Reihenfolge in der Datenbank, wo sie sein sollte.

2
Paul Sonier
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

oder wenn Sie das Böse dem Guten vorziehen:

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

Verschaffen wir uns einen visuellen Eindruck von dem, was bereits gesagt wurde. Zum Beispiel haben Sie eine Tabelle mit einigen Aufgaben:

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

Und Sie möchten die Liste der Aufgaben nach ihrem Status sortieren. Der Status ist eine Liste von Zeichenfolgenwerten:

(processing, pending,  completed, deleted)

Der Trick besteht darin, jedem Statuswert ein Interger zuzuweisen und die Liste numerisch zu ordnen:

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;

Was dazu führt:

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

Gutschrift @ user80168

0
Manuel
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)

hier ist [bbs] die Haupttabelle mit einem Feld namens ids und ids ist das Array, in dem die comments.id gespeichert ist.

bestanden in postgresql 9.6

0
user6161156
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;

[BEARBEITEN]

unnest ist in 8.3 noch nicht integriert, aber Sie können selbst eine erstellen (die Schönheit eines beliebigen *):

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

diese Funktion kann in jedem Typ arbeiten:

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

Und hier ist eine andere Lösung, die funktioniert und eine konstante Tabelle verwendet ( 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

Ich bin aber auch nicht sicher, ob dies performant ist.

Ich habe jetzt eine Menge Antworten. Kann ich ein paar Abstimmungen und Kommentare bekommen?

Vielen Dank an alle :-) 

0
nutcracker

Eine leichte Verbesserung gegenüber der Version, die eine Sequenz verwendet, denke ich:

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
Jon Erdman

Ich stimme mit allen anderen Plakaten überein, die sagen, "mach das nicht" oder "SQL ist nicht gut". Wenn Sie nach einer Facette von Kommentaren sortieren möchten, fügen Sie einer Ihrer Tabellen eine weitere Ganzzahlenspalte hinzu, um die Sortierkriterien zu speichern und nach diesem Wert zu sortieren. ZB "ORDER BY comments.sort DESC" Wenn Sie diese jedes Mal in einer anderen Reihenfolge sortieren möchten, dann ist ... SQL in diesem Fall nicht für Sie.

0
Trey