2016-11-01 7 views
11

다음 문제에 대한 쿼리를 공식화하는 데 문제가 있습니다.특정 조건에 대해 고유 한 쌍 값을 선택하는 SQL 쿼리 작성 방법은 무엇입니까?

특정 점수가있는 쌍 값의 경우 가장 좋은 점수를 가진 별개 쌍 값만 반환하는 방식으로 그룹화하는 방법은 무엇입니까?

(t1,p1,65) 
(t1,p2,60) 
(t1,p3,20) 
(t2,p1,60) 
(t2,p2,59) 
(t2,p3,15) 

처음 두 열 쌍의 값을 표시하고 세 번째 열은 쌍 score.The 최고 점수가 (t1,p1,65)입니다 나타냅니다

예를 들어, 나는 다음과 같은 행 값이있는 테이블이 있다고 할 수 있습니다. 이제 t1과 p1이 사용되었으므로 이후 분석에서 제외하고 싶습니다.

다음으로 높은 점수는 (t2,p2,59)입니다. (t1,p2)의 점수가 60 점이라도 "t1"이 이미 사용 되었기 때문에 제외하고 싶습니다. (t2,p1)도 60의 스코어를 갖지만, P1이 이미 사용하고 있기 때문에,이 쌍은 제외 이것의 고유 한 쌍의 스코어 값을 초래

:.

(t1,p1,65) 
(t2,p2,59) 

가되어이 생성 할 수있는 방법을 결과를 그룹화하고 파티셔닝하는 방법을 생각해 보려고했으나 점수 순위에 따라 이미 사용 된 값에 대한 회계가 있어야하기 때문에 접근하기가 매우 어렵습니다.

편집 :

데이터 생성하려면 :

with t(t, p, score) as (
    (values ('t1','p1',65), 
      ('t1','p2',60), 
      ('t1','p3',20), 
      ('t2','p1',60), 
      ('t2','p2',59), 
      ('t2','p3',15) 
    )) 
select t.* from t; 
+0

흥미로운 질문입니다. :) – borowis

+2

''p1'을 제외해야한다면 왜'(t2, p1, 60)'이 나올까요? – borowis

+2

재귀 CTE를 사용하여이 문제를 해결해야합니다. –

답변

3

그것은 저장 기능을 사용하여 비교적 간단하다 :

--drop function if exists f(); 
--drop table if exists t; 
create table t(x text,y text, z int); 
insert into t values 
    ('t1','p1',65), 
    ('t1','p2',60), 
    ('t1','p3',20), 
    ('t2','p1',60), 
    ('t2','p2',59), 
    ('t2','p3',15)/*, 
    ('t3','p1',20), 
    ('t3','p2',60), 
    ('t3','p3',40)*/; 

create function f() returns setof t immutable language plpgsql as $$ 
declare 
    ax text[]; 
    ay text[]; 
    r t; 
begin 
    ax := '{}'; ay := '{}'; 
    loop 
    select * into r 
     from t 
     where x <> all(ax) and y <> all(ay) 
     order by z desc, x, y limit 1; 
    exit when not found; 
    ax := ax || r.x; ay := ay || r.y; 
    return next r; 
    end loop; 
end $$; 

select * from f(); 
╔════╤════╤════╗ 
║ x │ y │ z ║ 
╠════╪════╪════╣ 
║ t1 │ p1 │ 65 ║ 
║ t2 │ p2 │ 59 ║ 
╚════╧════╧════╝ 

그러나 값 결과의 세 번째 무리 주석 경우 동일한 테스트 데이터에 재귀 CTE를 사용하여 동등 :

╔════╤════╤════╗ 
║ x │ y │ z ║ 
╠════╪════╪════╣ 
║ t1 │ p1 │ 65 ║ 
║ t3 │ p2 │ 60 ║ 
║ t2 │ p3 │ 15 ║ 
╚════╧════╧════╝ 

UPD : 다를 것이다

with recursive r as (
    (select x, y, z, array[x] as ax, array[y] as ay from t order by z desc, x, y limit 1) 
    union all 
    (select t.x, t.y, t.z, r.ax || t.x, r.ay || t.y from t, r 
    where not (t.x = any(r.ax) or t.y = any(r.ay)) 
    order by t.z desc, t.x, t.y limit 1)) 
select * from r; 
+0

아름다운. 고맙습니다! –

+0

실제로 십만 행이 포함 된 데이터 집합에서이 작업을 시도했지만 재귀 때문에 매우 느립니다. 효율성을 높이는 방법에 대한 아이디어가 있습니까? –

+0

@StephenTableau 실제 데이터 구조, 선택도, 색인 등에 대한 지식 없이는 아무 말도하지 못합니다. 'where on'과 'order by'부분을 다루는 인덱스를 작성해보십시오. (예를 들어, ... on t (x, y)와 ... on t (z desc)). [고든 Linoff의 솔루션] (http://stackoverflow.com/a/40367066/593144)가 더 빠릅니까? 아마 더 효율적인 솔루션이었을 것입니다. – Abelisto

1

하면 첫 번째 쌍 값, 두 번째 쌍의 값을 서로 다른 열 (예를 들어, X 및 Y) 그럴 수 X에 의해 그룹에 집계 함수로 MAX (점수를) 할 X로 시작하는 튜플에 대한 최대 점수를 얻으십시오.

모든 단계는 사용자의 데이터에 따라 결정됩니다. 왜냐하면 모든 튜플이 반대로 전환 될 경우 원치 않는 중복이 계속 발생할 수 있기 때문입니다. 따라서 이러한 반대되는 튜플을 제외하려면 먼저 자체 조인을 수행하십시오.

2

t1이 사용되었으므로 (t1, p2) 제외되었지만 p1도 사용되었으므로 제외하지 않았습니다. 나를 위해 그것은 첫번째 열로 groupping하는 것처럼 보인다. table1 이름은 테이블과 c1입니다

select t1.c1, t2.c2, t1.s 
    from table1 t2 
    inner join (select c1, max(score) s from table1 group by t1) t1 
    on (t1.s=t2.score and t1.c1=t2.c1); 

먼저 c2초 및 score 세 번째 열이다;

+0

문제는 동일한 점수를 가진 t1, p1 및 p1, t1 튜플을 가질 수 있습니다. 두 번 나열하면 안됩니다. – borowis

+0

예제 결과가 실수였습니다. p2를 제외하도록 업데이트했습니다. –

+0

@StephenTableau 그렇다면 내 대답이 올바르지 않습니다. – Kacper

4

이 문제는 분명히 나를 괴롭 히고 있습니다.다음은 행에 방문 값의 배열을 유지하는 로직을 구현하기 위해 나타납니다

with recursive t(t, p, score) as (
    (values ('t1','p1',65), 
      ('t1','p2',60), 
      ('t1','p3',20), 
      ('t2','p1',60), 
      ('t2','p2',59), 
      ('t2','p3',15) 
    )), 
    cte(t, p, score, cnt, lastt, lastp, ts, ps) as (
     (select t.*, count(*) over()::int, tt.t, tt.p, ARRAY[tt.t], ARRAY[tt.p] 
     from t cross join 
       (select t.* from t order by score desc limit 1) tt 
     ) 
     union all 
     select t, p, score, 
       sum(case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then 1 else 0 end) over()::int, 
       first_value(t) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last), 
       first_value(p) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last), 
       ts || first_value(t) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last), 
       ps || first_value(p) over (order by case when not (ts @> ARRAY[t] or ps @> ARRAY[p]) then score end desc nulls last) 
     from cte 
     where cnt > 0 
     ) 
select * 
from cte 
where lastt = t and lastp = p and cnt > 0; 
+0

잘 작동합니다! 정말 고마워! –