2016-10-09 6 views
3

*이 도전의 더 세련된 버전은 here 찾을 수 있습니다.SQL 퍼즐 : 행 번호를 생성하는 방법? (잔인한 트위스트와 고전적인 퍼즐)


퍼즐

우리는 테이블 t을 가지고

:

목표는, 아래에 규정 된 요구 사항에 따라 , 같은 결과를 반환하는 쿼리 작성하는 것입니다
create table t (i int not null); 

-

select t.i,row_number() over (order by t.i) as rn from t; 

데이터베이스 같아 테이블 -

create table s (i int not null unique); 

모든 나는 테이블 에 대해 말할 수는 그 정의를 제외하고는 테이블 t, 아니면 등의 같은 수의 행을 가지고 있다는 것입니다.

요구 사항

  • 이 솔루션은 단일 SQL 쿼리를해야한다 (하위 쿼리는 괜찮습니다).
  • 나타난 기능을 포함 t (아마도 S)를 제외한 모든 테이블의 사용은 는 허용되지 않는다.
  • 만 다음과 같은 조항이 허용됩니다 FROM, 을 선택 , WHERE, 그룹 HAVING , BY, (하지만 재귀!)를BY ORDER.
  • 분석 함수 의 사용은 허용되지 않습니다..
  • ROWNUM의 사용은 , GUID과 같은 이 허용되지을 rowid로.
  • T-SQL, PL/SQL 등 의 사용은 허용되지 않습니다..
  • UDF (사용자 정의 함수) 의 사용은 허용되지 않습니다..
  • 변수 의 사용은 허용되지 않습니다..

다음은 거의 모든 시간 작동 데이터 샘플

create table t (i int not null); 

insert into t (i) values (1); 
insert into t (i) values (2); 
insert into t (i) values (3); 
insert into t (i) values (3); 
insert into t (i) values (4); 
insert into t (i) values (5); 
insert into t (i) values (5); 
insert into t (i) values (5); 
insert into t (i) values (6); 
insert into t (i) values (7); 

create table s (i int not null unique); 

insert into s (i) values (3); 
insert into s (i) values (12); 
insert into s (i) values (13); 
insert into s (i) values (28); 
insert into s (i) values (41); 
insert into s (i) values (52); 
insert into s (i) values (56); 
insert into s (i) values (57); 
insert into s (i) values (83); 
insert into s (i) values (91); 
insert into s (i) values (97); 
insert into s (i) values (99); 

요청 결과

  I   RN 
---------- ---------- 
     1   1 
     2   2 
     3   3 
     3   4 
     4   5 
     5   6 
     5   7 
     5   8 
     6   9 
     7   10 
+2

퍼즐의 전체 SE 사이트가 있습니다. 그러나 이것은 장소가 아닙니다. –

+0

1) 이유가 무엇입니까? 2) "puzzle"에 대한 stackoverflow를 검색하여 29,743 개의 결과를 얻었습니다. –

+2

@sstan - OP에 해결책이 있다고 확신합니다. 그것을 보여주는 것은 퍼즐을 다소 손상시킬 것입니다. –

답변

3

:

with tt as (
     select t.i, t.i + rand() as new_i 
     from t 
    ) 
select tt.i, 
     (select count(*) 
     from tt tt2 
     where tt2.new_i <= tt.new_i 
     ) as rn 
from tt; 

참고 : rand()의 기능은 모든 데이터베이스에 존재를 그 지원 with, 비록 정확한 기능 (또는 조합)은 데이터베이스에 따라 다릅니다.

편집 :

그것은 훨씬 더 복잡 모든 시간을 작동하는 무언가를 얻는 것입니다. 그러나 :

with n as (
     select (select count(*) from s s2 where s2.i <= s.i) as n 
     from s 
    ), 
    tt as (
     select i, count(*) as num 
     from t 
     group by i 
    ), 
    ttt as 
     (select tt.*, 
      (select sum(num) from tt tt2 where tt2.i < tt.i 
      ) as cume_num 
     from tt 
    ) 
select ttt.i, coalesce(cume_num, 0) + n.n 
from ttt join 
    n 
    on n.n <= ttt.num; 

나는 종류의 첫 번째 방법은 더 나은처럼,

+0

예, "항상"좋을 것입니다 :-) –

+0

구문이 올바르게 보이지 않습니다. –

+0

첫 번째 해결책은 잘 동작합니다. +1 –

2

) 나는이 제안 :

with 
    grp as 
     (select i, 
        count(*) cnt, 
        (select count(*) from t where i < t1.i) cntBefore 
     from  t t1 
     group by i), 
    r as 
     (select (select count(*) from s where i <= s1.i) rn 
     from  s s1) 
select  grp.i, r.rn 
from  r 
inner join grp on r.rn between grp.cntBefore + 1 and grp.cntBefore + grp.cnt; 
1
with 
    num_gen as (
     select (select count(*) from s where i <= s1.i) n 
     from  s s1 
    ), 
    groups as (
     select i, count(*) as ct 
     from  t 
     group by i 
    ) 
select g.i, ng.n + (select count(*) from t where t.i < g.i) rn 
from num_gen ng inner join groups g on ng.n <= g.ct; 

을 : 처음에 내가 같이 다른 솔루션을 제공 아래에 역사적 관점 (처음 두 주석은이 관점을 언급한다). 물론 OP가 맞습니다. 나는 좋은 아이디어를 도출했다. 위의 솔루션에서 나는 적절한 단순성으로 아이디어를 복원했습니다.

-- OLD solution (replaced by the one above) 
with 
    num_gen as (
     select (select count(*) from s where i <= s1.i) n 
     from  s s1 
    ), 
    groups as (
     select i, count(*) as ct 
     from  t 
     group by i 
    ), 
    new_numbers as (
     select g.i i, g.i + power(10, -ng.n) new_i 
     from num_gen ng inner join groups g on ng.n <= g.ct 
    ) 
select nn.i, (select count(*) from new_numbers where new_i <= nn.new_i) rn 
from  new_numbers nn; 
+0

괜찮습니다. 하지만 POWER의 사용은 위험합니다. 오버 플로우 문제는 매우 빠르게 발생할 수 있습니다. –

+0

lol - right; 적어도 2의 힘을 사용해야합니다. 그러나 논리적 문제를 해결할 때 우리는 물리적 계층에 대해 생각하지 않아도됩니다. 맞습니까? – mathguy

+0

@DuduMarkovitz - 나는 푸줏간 주인에게 좋은 생각을했다. 그것은 자정이 지난 것입니다 ... 지금 고쳐주었습니다. 내가 trincot의 해답을 정확하게 읽는다면, 지금은 그의 것과 거의 같습니다. – mathguy

1

내 변화 :

with  seq_num (i) 
      as 
      (
       select  (select count (*) as i from s s2 where s2.i <= s1.i) 

       from  s s1 
      )   

      ,t_with_seq (i,i_seq) 
      as 
      (
       select  t.i  as i 
          ,s.i  as i_seq 

       from     (select  i 
                ,count (*) as occurrences 

             from  t 

             group by i 
             ) 
             t 

          join  seq_num s 

          on   s.i <= t.occurrences 
      ) 

select  ts1.i 
      ,(select count (*) from t_with_seq ts2 where ts2.i < ts1.i or (ts2.i = ts1.i and ts2.i_seq <= ts1.i_seq)) as rn 

from  t_with_seq ts1 

order by rn 
;