2

我有一个表,其中一列的值将是这样的。

C
H
C
H
H
H
H
C
H
H
H

它将有一个“C”和一个或多个“H”记录。

我试图为每组“C”和一个或多个“H”提供一个组号。

C 1
H 1
C 2
H 2
H 2
H 2
H 2
C 3
H 3
H 3
H 3

由于担心性能,我不想使用游标。如何为“C”的每个子集和一个或多个“H”记录提供唯一的编号?

4

2 回答 2

1

只要您的数据干净且一致,这在支持标准 SQL 窗口函数的平台上并不太难。确实需要另一列可以有意义地排序。

让我们一次一个地构建它。(用 PostgreSQL 9.3 编写。)

create table test (
  test_id serial primary key,
  test_val char(1)
);
insert into test(test_val) values
('C'), ('H'), 
('C'),('H'),('H'),('H'),('H'),
('C'),('H'),('H'),('H');

我们可以通过查看下一行来判断一个组何时开始。

select test_id, test_val,
       lead(test_val) over (order by test_id) next_test_val
from test;

该查询的前三行。

test_id test_val next_test_val
--
1 通道
2 碳氢化合物
3通道
...

通过检查“C”和“H”组合,我们可以识别组的开始。(前面的查询变成了公用表表达式。)

with next_vals as (
select test_id, test_val,
       lead(test_val) over (order by test_id) next_test_val
from test
)
select *, case when test_val = 'C' and next_test_val = 'H' then test_id 
      end as grp
from next_vals;

这是该结果集中的前四行。id 号便于识别组。

test_id test_val next_test_val grp
--
1 通道 1
2 碳氢化合物
3 通道 3
4 小时
...

另一个窗口函数填补了空白。同样,前面的查询变成了 CTE。WHERE 子句防止出现“C”行后跟另一个“C”行。

with next_vals as (
select test_id, test_val,
       lead(test_val) over (order by test_id) next_test_val
from test
), group_starts as (
select *
    , case when test_val = 'C' and next_test_val = 'H' then test_id 
      end as grp
from next_vals
)
select test_id, test_val, max(grp) over (order by test_id) as ch_group
from group_starts
where not (test_val = 'C' and next_test_val = 'C')
order by test_id;
test_id test_val ch_group
--
1 C 1
2 小时 1

3 C 3
4 小时 3
5 小时 3
6 小时 3
7 小时 3

8 C 8
9 小时 8
10 小时 8
11 小时 8

我添加了一些换行符以使其更易于阅读。

我不知道这是否会比游标更好。


对于连续组号。. .

with next_vals as (
select test_id, test_val,
       lead(test_val) over (order by test_id) next_test_val
from test
), group_starts as (
select *
    , case when test_val = 'C' and next_test_val = 'H' then test_id 
      end as grp
from next_vals
), grouped_values as (
select test_id, test_val, max(grp) over (order by test_id) as ch_group
from group_starts
where not (test_val = 'C' and next_test_val = 'C')
)
select test_id, test_val, 
       dense_rank() over (order by ch_group)
from grouped_values
order by test_id;
于 2015-01-01T01:42:29.167 回答
0

这是一种适用于 MS SQL Server 2008 的可能解决方案,它没有LEAD功能(在更高版本中添加)。此外,此解决方案编号按顺序分组,没有间隙,如所需输出所示。

它只使用ROW_NUMBER()函数和CROSS APPLY

有必要拥有ID唯一标识每一行的列,我们可以使用它来对结果进行排序。

使用样本数据创建一个测试表:

DECLARE @TT TABLE (ID int IDENTITY(1,1) PRIMARY KEY, Val char(1));

INSERT INTO @TT VALUES('C');
INSERT INTO @TT VALUES('H');
INSERT INTO @TT VALUES('C');
INSERT INTO @TT VALUES('H');
INSERT INTO @TT VALUES('H');
INSERT INTO @TT VALUES('H');
INSERT INTO @TT VALUES('H');
INSERT INTO @TT VALUES('C');
INSERT INTO @TT VALUES('H');
INSERT INTO @TT VALUES('H');
INSERT INTO @TT VALUES('H');

获取所有具有C值的行的列表。每个组都以 开头C,因此C数据中的组数将与 s 一样多。此列中的其他值无关紧要,也可以有其他值,而不仅仅是H. 查询中没有硬编码H,只有C.

WITH
CTE_C
AS
(
    SELECT ID, Val, ROW_NUMBER() OVER(ORDER BY ID) AS rn
    FROM @TT AS T
    WHERE Val = 'C'
)

此 CTE 的输出为: ( SELECT * FROM CTE_C)

ID   Val   rn
1    C     1
3    C     2
8    C     3

现在我们只需要为原始数据的每一行在 CTE 中找到一个合适的行,该行具有合适的 ID,因此具有合适的 rn。我们用CROSS APPLY它。

WITH
CTE_C
AS
(
    SELECT ID, Val, ROW_NUMBER() OVER(ORDER BY ID) AS rn
    FROM @TT AS T
    WHERE Val = 'C'
)
SELECT T.ID, T.Val, CTE_rn.rn
FROM
    @TT AS T
    CROSS APPLY
    (
        SELECT TOP(1) CTE_C.rn
        FROM CTE_C
        WHERE CTE_C.ID <= T.ID
        ORDER BY CTE_C.ID DESC
    ) AS CTE_rn
ORDER BY T.ID;

这是最终结果:

ID   Val   rn
1    C     1
2    H     1
3    C     2
4    H     2
5    H     2
6    H     2
7    H     2
8    C     3
9    H     3
10   H     3
11   H     3

在性能方面,您需要使用实际数据和实际系统测试各种解决方案。ID 应该有唯一的索引。Val 的指数很可能也是有益的。

于 2015-01-01T04:47:54.893 回答