IN 값 목록으로 주문


165

PostgreSQL 8.3에는 많은 주석을 얻는 간단한 SQL 쿼리가 있습니다. 나는 제공 정렬 받는 값 목록 IN의 구조 WHERE절을 :

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

이것은 내에서 id와 같은 임의의 순서로 주석을 반환합니다 1,2,3,4.

결과 행이 IN구문 의 목록처럼 정렬되기를 원합니다 (1,3,2,4).
그것을 달성하는 방법?


그리고 정렬을 위해 새 테이블을 작성하지 않는 것이 좋습니다 (SQL 순도에도 불구하고).
nutcracker

2
나는 지금 많은 답변을 얻었습니다. 어느 쪽이 승자인지 알 수 있도록 투표와 의견을받을 수 있습니까? 모두 감사합니다 :-)
호두 까기 인형 1:15에

답변:


106

(PostgreSQL 8.2에서 도입) VALUES (), ()를 사용하면 쉽게 할 수 있습니다.

구문은 다음과 같습니다.

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
IN 절에 수천 개의 값이 있으면 어떻게됩니까? 수천 개의 기록을 위해해야했기 때문에
kamal

@kamal 나는 그것을 사용했다 with ordered_products as (select row_number() OVER (ORDER BY whatever) as reportingorder, id from comments) ... ORDER BY reportingorder.
누 메논

66

찾기가 너무 어렵고 확산되어야하기 때문에 mySQL에서는 훨씬 간단하게 수행 할 수 있지만 다른 SQL에서 작동하는지는 알 수 없습니다.

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

3
값 목록은 두 가지 다른 방식으로 두 제공되어야 합니다. 그렇게 간단하지 않습니다. 허용되는 답변은 한 번만 필요합니다 (더 자세한 방법으로도). 최신 Postgres에서는 더 간단합니다 (최신 답변에서 입증 된 것처럼). 또한이 질문은 결국 Postgres에 관한 것 같습니다.
Erwin Brandstetter

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

54

Postgres 9.4 이상 에서는 아마도 가장 간단하고 빠릅니다 .

SELECT c.*
FROM   comments c
JOIN   unnest('{1,3,2,4}'::int[]) WITH ORDINALITY t(id, ord) USING (id)
ORDER  BY t.ord;
  • @a_horse 는 새로운 WITH ORDINALITY것을 사용하여 이미 언급했다 .

  • 하위 쿼리가 필요하지 않으며 테이블처럼 set-returning 함수를 사용할 수 있습니다.

  • ARRAY 생성자 대신 배열에 전달할 문자열 리터럴 은 일부 클라이언트에서 구현하기가 더 쉬울 수 있습니다.

상해:


46

나는이 방법이 더 낫다고 생각한다.

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
나는 바인딩 값, 즉,이 작업을 수행 할 수 있었다 : ... order by id=? desc, id=? desc, id=? desc그것은 :-) 잘 작동 보인다
KajMagnus

postgres에서 작동하며 최고의 솔루션 인 것 같습니다!
Mike Szyndel

이 솔루션은 나를 위해 속임수를 썼지 만,이 솔루션이 어떻게 성능 측면에서 잘 수행되고 있는지 조사한 사람이 있습니까? 여러 개의 order by 절을 추가합니다. 따라서 주문 ID 수가 증가함에 따라 (아직 테스트하지는 않았지만) 기하 급수적으로 느려질 수 있습니까? 이것에 대한 정보는 높이 평가할 것입니다!
Fabian Schöner

1
오류 : 대상 목록은 최대 1664 개의 항목을 가질 수 있습니다.-> 긴 쿼리를 실행하려고 할 때 ...
Fatkhan Fauzi

@Manngo MS SQL. 어떤 버전을 기억할 수 없습니다.
9

42

Postgres 9.4를 사용하면 조금 더 짧게 수행 할 수 있습니다.

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;

또는 파생 테이블이 없으면 조금 더 컴팩트합니다.

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

각 값에 위치를 수동으로 할당 / 유지할 필요가 없습니다.

포스트 그레스 9.6 이 사용하여 수행 할 수 있습니다 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는 값 목록을 한 번만 지정하면되므로 사용됩니다. 이것이 중요하지 않은 경우 다음과 같이 작성할 수도 있습니다.

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

이것은 절 IN에서 WHERE절의 전체 목록을 다시 반복하지 않으므로 ORDER BY이것이 가장 좋은 대답이됩니다 ... 이제는 MySQL과 비슷한 것을 찾기 위해 ...
Stijn de Witt

1
내가 가장 좋아하는 대답이지만 array_position은 bigint와 함께 작동하지 않으며 캐스팅해야합니다 order by array_position(array[42,48,43], c.id::int);. 경우에 따라 버그가 발생할 수 있습니다.
aaandre

1
다음 주조는 (적어도 포스트 그레스 12)의 벌금을하고있다 @aaandre array_position(array[42, 48, 43]::bigint[], c.id::bigint)필요가 없습니다 그래서 잘라 내기에, bigintint.

29

Postgres에서 다른 방법으로 idx기능 을 사용하는 것입니다.

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

idx여기에 설명 된대로 먼저 함수 를 작성하는 것을 잊지 마십시오 . http://wiki.postgresql.org/wiki/Array_Index


11
이 기능은 이제 PostgreSQL과 함께 제공되는 확장에서 사용 가능합니다 : postgresql.org/docs/9.2/static/intarray.html로 설치하십시오 CREATE EXTENSION intarray;.
Alex Kahn

1
더 나아가 Amazon RDS 사용자의 경우 ROR 마이그레이션 기능 enable_extension을 사용하면 앱 사용자가 rds_superuser그룹 의 구성원 인 한이 기능 을 활성화 할 수 있습니다 .
Dave S.

PG 9.6.2에서 PG :: UndefinedFunction : 오류 : 함수 idx (integer [], integer)가 존재하지 않음
Yakob Ubaidi

감사합니다, @AlexKahn의 의견과 함께 할 때 가장 좋은 답변
Andrew

21

PostgreSQL에서 :

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

2
흠 ... 경우 버그 position(id::text in '123,345,3,678'). ID 3가 ID 보다 먼저 일치 345합니까?
alanjds

4
나는 당신이 옳다고 생각하고 시작과 끝 구분 기호를 둘 필요가있을 것입니다. ', 1,3,2,4에서 order by position (', '|| id :: text ||', ', ')
Michael Rush

3

이것을 좀 더 연구 하면서이 해결책을 찾았습니다.

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

그러나 이것은 다소 장황하게 보이며 큰 데이터 세트에서 성능 문제가있을 수 있습니다. 누구든지이 문제에 대해 언급 할 수 있습니까?


7
물론, 나는 그들에 대해 언급 할 수 있습니다. SQL이 좋고 잘하지 않는 것이 있습니다. SQL은 이것에 좋지 않습니다. 쿼리 할 언어에 따라 결과를 정렬하십시오. 그것은 당신에게 치아의 많은 울부 짖음과 gn 거리는 것을 저장합니다. SQL은 집합 지향 언어이며 집합은 순서가 지정된 모음이 아닙니다.
kquinn

흠 ... 개인적인 경험과 테스트에 기초한 것입니까? 테스트를 거친 경험은 이것이 주문에 매우 효과적인 기술이라는 것입니다. 그러나 "IN (...)"절을 제거하므로 허용되는 답변이 전체적으로 더 좋습니다. 합리적인 결과 집합 크기의 경우 집합을 파생시키는 데 많은 비용이 듭니다. 수백 레코드 이하로 떨어지면 정렬이 쉽지 않습니다.
dkretz 2016 년

IN절에 수천 개의 값이 있으면 어떻게 됩니까? 수천 개의 레코드를 처리해야하기 때문입니다.
kamal

2

이렇게하려면 주문할 ID 매핑을 정의하는 추가 "ORDER"테이블이 있어야한다고 생각합니다 (실제로 자신의 질문에 대한 응답이 수행 한 작업 수행). 그런 다음 정렬 할 수 있습니다.

그런 식으로 데이터베이스에서 원하는 순서를 명시해야합니다.


이것은 올바른 방법처럼 보입니다. 그러나 주문 테이블을 즉시 만들고 싶습니다. 답변 중 하나에 상수 테이블을 사용하는 것이 좋습니다. 수백 또는 수천 개의 댓글을 처리 할 때이 기능이 제대로 작동합니까?
호두 까 기 인형 1

2

sans SEQUENCE는 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

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

또는 선보다 악을 선호하는 경우 :

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

0

그리고 상수 테이블 ( 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

그러나 다시 한 번 나는 이것이 성능이 확실하지 않습니다.

나는 지금 많은 답변을 얻었습니다. 어느 쪽이 승자인지 알 수 있도록 투표와 의견을받을 수 있습니까?

모두 감사합니다 :-)


1
당신의 대답은 depesz와 거의 동일합니다 .c.ID IN (1,3,2,4)를 제거하십시오. 어쨌든 그의 낫습니다. 그는 가능한 한 ANSI SQL 조인 방법을 사용하고 테이블 쉼표 테이블을 사용하지 마십시오. 나는 당신의 대답을주의 깊게 읽었을 것입니다. 나는 두 열의 별칭을 지정하는 방법을 알아내는 데 어려움을 겪고 있습니다. 먼저 이것을 시도했습니다 : (values ​​(1,1) as x (id, sort_order), (3,2), (2,3), (4,4))는 y입니다. 그러나 아무 소용이 없다 : -D 내가주의 깊게 읽었다면 당신의 대답은 나에게 단서를 제공 할 수 있습니다 :-)
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;

[편집하다]

unnest는 8.3에 아직 내장되어 있지 않지만 직접 만들 수 있습니다 (어떤 아름다움의 *).

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

이 기능은 모든 유형에서 작동 할 수 있습니다.

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

고마워 마이클하지만 내 PSQL에는 unnest 함수가 존재하지 않는 것 같으며 문서에서도 언급 할 수 없습니다. 8.4 만입니까?
호두 까 기 인형 1

unnest는 8.3에 아직 내장되어 있지 않지만 직접 구현할 수 있습니다. 위의 코드를 참조하십시오
Michael Buen

0

내가 생각하는 시퀀스를 사용하는 버전에 비해 약간의 개선 :

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)

여기서 [bbs]는 ids라는 필드가있는 기본 테이블이고 ids는 comments.id를 저장하는 배열입니다.

postgresql 9.6에 전달


이 쿼리를 테스트 했습니까?
lalithkumar

여기서 id는 {1,2,3,4}와 같은 배열 유형입니다.
user6161156

0

이미 말한 것에 대해 시각적 인 인상을 줄 수 있습니다. 예를 들어 몇 가지 작업이있는 테이블이 있습니다.

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

상태별로 작업 목록을 정렬하려고합니다. 상태는 문자열 값의 목록입니다.

(processing, pending,  completed, deleted)

비결은 각 상태 값에 interger를 부여하고 목록을 숫자로 정렬하는 것입니다.

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;

어느 것이

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

크레딧 @ user80168


-1

"하지 마십시오"또는 "SQL이 좋지 않다"는 다른 모든 포스터에 동의합니다. 주석의 일부 패싯별로 정렬하려면 테이블 중 하나에 다른 정수 열을 추가하여 정렬 기준을 보유하고 해당 값을 기준으로 정렬하십시오. 예를 들어 "ORDER BY comments.sort DESC"매번 다른 순서로 이들을 정렬하려면이 경우 SQL이 적합하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.