3

私は SQL には比較的慣れていませんが、このサイトを通じて多くの有用なアイデアを得ることができました。今、私は十分に単純に見えるコードに行き詰まっていますが、何らかの理由で頭を包み込むことができません。

以下の最初の 2 つの列に基づいて、3 番目の列 (列 Z) を作成しようとしています。

Column X   Column Y
-------------------
 1          a
 1          b
 1          c
 2          a
 2          d
 2          e
 2          f
 4          b
 5          i
 5          c
 3          g
 3          h
 6          j
 6          k
 6          l

列 Z で発生する必要があること:

  • 列 Y にある個々の値について、列 X の値をメモします。
  • 同様に、列 X の個々の値について、列 Y の値に注意してください。
  • 次に、これらを以下に示すグループにクラスター化 (RANK/ROW_NUMBER?) します。
Column X   Column Y  Column Z
-----------------------------
 1          a         1
 1          b         1
 1          c         1
 2          a         1
 2          d         1
 2          e         1
 2          f         1
 4          b         1
 5          i         1
 5          c         1
 3          g         2
 3          h         2
 6          j         3
 6          k         3
 6          l         3

物事を過度に複雑にすることなく、十分に明確になったことを願っています。午前中ずっと頭がぐるぐるしています。誰かがさらに情報が必要な場合はお知らせください。

事前に大歓迎です!

4

3 に答える 3

2

おそらく最善の方法ではありませんが、それは機能します

SQLFiddle http://sqlfiddle.com/#!3/99532/1

;WITH cte AS (
    SELECT  *, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS row_nb
    FROM    #t
)
, c2 AS (
    SELECT e1.*
    ,CASE WHEN EXISTS(SELECT * FROM cte e2 WHERE e1.Y = e2.Y and e2.row_nb < e1.row_nb) THEN 1 ELSE 0 END as ex
FROM cte e1 
)
, c3 AS (
    SELECT  X,1 - SIGN(SUM(ex)) as ex,MAX(row_nb) as max_row_nb
    FROM    c2
    GROUP BY X
)
SELECT  
    cte.X,cte.Y
    ,(SELECT SUM(cc3.ex) FROM c3 cc3 where cc3.max_row_nb<= c3.max_row_nb) AS Z 
FROM    cte 
INNER JOIN c3 
    ON  c3.X = cte.X
ORDER BY cte.row_nb
于 2012-09-13T14:21:14.010 に答える
2

過去のいくつかの分析で、まさにこの問題に直面しました。それを機能させる唯一の方法は、情報を段階的に追加するループを実行することです。

ループは、各グループ内の最小の「x」値をグループ ID として割り当てます。ルールにより、これは一意であることが保証されます。現在の x 値を z に代入することから始めます。次に、x 次元と y 次元に沿って最小の z を見つけます。レコードが変更されなくなるまで、このプロセスが繰り返されます。

データが与えられた場合、その方法の概要は次のとおりです。

update t set z = x

while 1=1
begin
    with toupdate as (
         select t.*,
                min(z) over (partition by x) as idx,
                min(z) over (partition by y) as idy from t
         )
    update toupdate
        set z = (case when idx < idy then idx else idy end)
        where z > idx or z > idy;
    if (@@ROWCOUNT = 0) break;   
end;

;with a as
(
  select z, dense_rank() over (order by z) newZ from t
)
update a set z = newZ
于 2012-09-13T14:21:35.360 に答える
1
declare @t table (x tinyint, y char(1), z tinyint)
insert @t (x,y) values(1,'a'),(1,'b'),(1,'c'),(2,'a'),(2,'d'),(2,'e'),(2,'c'),
(2,'f'),(4,'b'),(5,'i'),(5,'c'),(3,'g'),(3,'h'),(6,'j'),(6,'k'),(6,'l'),(7,'v')

;with a as
(
  select x,parent from 
  (
    select x, min(x) over (partition by y) parent from @t
  ) a
  where x > parent
), b as
(
  select x, parent from a
  union all
  select a.x, b.parent
  from a join b on a.parent = b.x
), c as
(

  select x, min(parent) parent
  from b
  group by x
), d as
(
  select t.x,t.y, t.z, 
  dense_rank() over (order by coalesce(c.parent, t.x)) calculatedZ 
  from @t t 
  left join c on t.x = c.x
)
select x,y,calculatedZ as z from d
-- if you want to update instead of selecting, replace last line with: 
-- update d set z = newz
-- select x,y,z from @t
option (maxrecursion 0)

結果:

x y z
1 a 1
1 b 1
1 c 1
2 a 1
2 d 1
2 e 1
2 c 1
2 f 1
4 b 1
5 i 1
5 c 1
3 g 2
3 h 2
6 j 3
6 k 3
6 l 3
8 j 3
7 v 4
于 2012-09-13T14:36:02.617 に答える