3

我是 SQL 的新手,但通过该站点获得了许多有用的想法。现在我被困在一段看起来很简单的代码上,但由于某种原因,我无法理解它。

我正在尝试根据下面的前两列创建第三列(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 回答