SQL dense_rank()按组和计数

时间:2017-09-25 04:42:17

标签: sql-server

我有以下结果集。

Tmp_Table
Tag | Code | Rel
 A     ABC    1
 A     ABC    1
 A     ABC    1
 A     ABC    1
 B     XYZ    1
 B     XYZ    1
 B     XYZ    1
 B     XYZ    1
 B     XYZ    1
 C     QWE    1
 C     QWE    1
 C     QWE    1
 D     EFG    1

要求是:

  1. 按标记对记录进行分组,并为每个组添加组ID。
  2. 每个组不应超过4个记录/行
  3. 超过4条记录的假定组应分成不同的组。
  4. 我尝试使用dense_rank()为每条记录创建一个简单的增量。

    SELECT DENSE_RANK() OVER(Order By Tag)groupID, * FROM Tmp_Table
    

    这是当前的结果集。

    groupID | Tag | Code | Rel
     1         A     ABC    1
     1         A     ABC    1
     1         A     ABC    1
     1         A     ABC    1
     2         B     XYZ    1
     2         B     XYZ    1
     2         B     XYZ    1
     2         B     XYZ    1
     2         B     XYZ    1
     3         C     QWE    1
     3         C     QWE    1
     3         C     QWE    1
     4         D     EFG    1
    

    预期结果。

    groupID | Tag | Code | Rel
     1         A     ABC    1
     1         A     ABC    1
     1         A     ABC    1
     1         A     ABC    1
     2         B     XYZ    1
     2         B     XYZ    1
     2         B     XYZ    1
     2         B     XYZ    1
     3         B     XYZ    1
     4         C     QWE    1
     4         C     QWE    1
     4         C     QWE    1
     5         D     EFG    1
    

    我还尝试仅使用row_number() over (partition by Tag order by Tag)为每个标记添加增量。

     groupID | Tag | Code | Rel
     1         A     ABC    1
     2         A     ABC    1
     3         A     ABC    1
     4         A     ABC    1
     1         B     XYZ    1
     2         B     XYZ    1
     3         B     XYZ    1
     4         B     XYZ    1
     5         B     XYZ    1
    ...
    

    肯定遗漏了一些重要的事情。任何想法都将非常感谢!

3 个答案:

答案 0 :(得分:0)

实际上,你应该在你的行中有一些定义功能,以确保顺序是正确的(并且,通常,数据中的重复没有任何关于行的任何独特性是多余的),但没有它,这是一种方式你可以使用窗口函数的组合(假设SQL Server 2012 +):

DECLARE @T TABLE (Tag CHAR(1), Code CHAR(3), Rel INT);
INSERT @T VALUES
('A', 'ABC', 1),
('A', 'ABC', 1),
('A', 'ABC', 1),
('A', 'ABC', 1),
('B', 'XYZ', 1),
('B', 'XYZ', 1),
('B', 'XYZ', 1),
('B', 'XYZ', 1),
('B', 'XYZ', 1),
('C', 'QWE', 1),
('C', 'QWE', 1),
('C', 'QWE', 1),
('D', 'EFG', 1);

SELECT GroupID = SUM(RN2) OVER (ORDER BY RN1), Tag, Code, Rel
FROM
(
    SELECT *, RN2 = CASE WHEN ROW_NUMBER() OVER (PARTITION BY Tag ORDER BY RN1) % 4 = 1 THEN 1 ELSE 0 END
    FROM (
        SELECT *, RN1 = ROW_NUMBER() OVER (ORDER BY Tag)
        FROM @T
    ) AS T
) AS T;

答案 1 :(得分:0)

这是另一种选择......

IF OBJECT_ID('tempdb..#tmp_table', 'U') IS NOT NULL 
DROP TABLE #tmp_table;

CREATE TABLE #tmp_table (
    Tag CHAR(1),
    Code CHAR(3),
    Rel TINYINT
    );
INSERT #tmp_table (Tag, Code, Rel) VALUES
    ('A', 'ABC', 1),
    ('A', 'ABC', 1),
    ('A', 'ABC', 1),
    ('A', 'ABC', 1),
    ('B', 'XYZ', 1),
    ('B', 'XYZ', 1),
    ('B', 'XYZ', 1),
    ('B', 'XYZ', 1),
    ('B', 'XYZ', 1),
    ('C', 'QWE', 1),
    ('C', 'QWE', 1),
    ('C', 'QWE', 1),
    ('D', 'EFG', 1);

--=============================================

WITH 
    cte_AddRN AS (
        SELECT
            tt.Tag, tt.Code, tt.Rel,
            RN = ROW_NUMBER() OVER (PARTITION BY tt.Tag ORDER BY (SELECT NULL))
        FROM
            #tmp_table tt
        )
SELECT 
    GroupID = DENSE_RANK() OVER (ORDER BY gv.GroupVal),
    ar.Tag, ar.Code, ar.Rel
FROM
    cte_AddRN ar
    CROSS APPLY ( VALUES (ISNULL(NULLIF(ar.RN, 0), 4)) ) r (RN)
    CROSS APPLY ( VALUES (ISNULL(NULLIF(r.RN % 4, 0), 4)) ) m (ModRN)
    CROSS APPLY ( VALUES (ar.Tag + CAST(r.RN - m.ModRN AS VARCHAR(10))) ) gv (GroupVal);

结果...

GroupID              Tag  Code Rel
-------------------- ---- ---- ----
1                    A    ABC  1
1                    A    ABC  1
1                    A    ABC  1
1                    A    ABC  1
2                    B    XYZ  1
2                    B    XYZ  1
2                    B    XYZ  1
2                    B    XYZ  1
3                    B    XYZ  1
4                    C    QWE  1
4                    C    QWE  1
4                    C    QWE  1
5                    D    EFG  1

答案 2 :(得分:0)

我的#tmp_table包含您提供的数据。解决方案如下:

我添加标签1以记录超过四个,因此在此标记上您可以直接应用dense_rank()

select GroupID = dense_rank() over (order by OverFour), tag, code, rel from (
    select case when rn < 5 then tag + '0' else tag + '1' end as OverFour,
           tag, code, rel
    from (
        select rn = row_number() over (partition by tag order by tag),
        tag, code, rel
        from #tmp_table
    ) as a
) as b