【问题标题】:Assign sequential numbers to rows with repeating values将序号分配给具有重复值的行
【发布时间】:2016-05-10 10:19:46
【问题描述】:

我有下表:

ITEM    DATE        VALUE
----------------------
ITEM1   2016-05-04  1
ITEM1   2016-05-05  3
ITEM1   2016-05-06  3
ITEM1   2016-05-09  3
ITEM1   2016-05-04  4
ITEM2   2016-05-10  1
ITEM2   2016-05-05  2
ITEM2   2016-05-06  3
ITEM2   2016-05-09  1
ITEM2   2016-05-10  1

我想知道,每个项目,有多少条目及时返回值列是相同的(平坦):

ITEM    DATE    VALUE   NUM_FLAT_ENTRYPOINTS
------------------------------
ITEM1   2016-05-04  1   0
ITEM1   2016-05-05  3   0 
ITEM1   2016-05-06  3   1
ITEM1   2016-05-09  3   2
ITEM1   2016-05-10  4   0
ITEM2   2016-05-04  1   0
ITEM2   2016-05-05  2   0
ITEM2   2016-05-06  3   0
ITEM2   2016-05-09  1   0
ITEM2   2016-05-10  1   1

我最初的想法是:

select 
    *,
    rank()-1 over (partition by ITEM,VALUE order by DATE) as NUM_FLAT_ENTRYPOINTS 
from my_table

但是,这不起作用,因为 ITEM2 会将 2016-05-04、2016-05-09 和 2016-05-10 分区在一起,并在最后一行的 NUM_FLAT_ENTRYPOINTS 中显示 2 而不是 1。

我使用的是 Microsoft SQL Server 2008。

有什么想法吗?

编辑:

在 Oracle(以及可能的其他 SQL Server)中,我似乎可以做到

select
    count(VALUE)-1 over (partition by ITEM,VALUE order by DATE) as NUM_FLAT_ENTRYPOINTS 
from my_table

但据我所知,这种语法在 SQL Server 2008 中不起作用。有什么办法可以解决它?

【问题讨论】:

  • 我假设在您的示例数据中,每个项目的最后一行应该是2016-05-10,而不是2016-05-04,并且DATE 定义了应该评估行的顺序?否则,请详细说明这里的规则...
  • 您说得对,先生,修正!

标签: sql sql-server sql-server-2008 window-functions


【解决方案1】:

它看起来像是缝隙和岛屿的变体。

样本数据

DECLARE @T TABLE (ITEM varchar(50), dt date, VALUE int);
INSERT INTO @T(ITEM, dt, VALUE) VALUES
('ITEM1', '2016-05-04', 1),
('ITEM1', '2016-05-05', 3),
('ITEM1', '2016-05-06', 3),
('ITEM1', '2016-05-09', 3),
('ITEM1', '2016-05-10', 4),
('ITEM2', '2016-05-04', 1),
('ITEM2', '2016-05-05', 2),
('ITEM2', '2016-05-06', 3),
('ITEM2', '2016-05-09', 1),
('ITEM2', '2016-05-10', 1);

查询

WITH
CTE
AS
(
    SELECT
        ITEM
        ,dt
        ,VALUE
        ,ROW_NUMBER() OVER (PARTITION BY ITEM ORDER BY dt) AS rn1
        ,ROW_NUMBER() OVER (PARTITION BY ITEM, VALUE ORDER BY dt) AS rn2
    FROM @T
)
SELECT
    ITEM
    ,dt
    ,VALUE
    ,rn1-rn2 AS rnDiff
    ,ROW_NUMBER() OVER 
        (PARTITION BY ITEM, VALUE, rn1-rn2 ORDER BY dt) - 1 AS NUM_FLAT_ENTRYPOINTS
FROM CTE
ORDER BY ITEM, dt;

结果

+-------+------------+-------+--------+----------------------+
| ITEM  |     dt     | VALUE | rnDiff | NUM_FLAT_ENTRYPOINTS |
+-------+------------+-------+--------+----------------------+
| ITEM1 | 2016-05-04 |     1 |      0 |                    0 |
| ITEM1 | 2016-05-05 |     3 |      1 |                    0 |
| ITEM1 | 2016-05-06 |     3 |      1 |                    1 |
| ITEM1 | 2016-05-09 |     3 |      1 |                    2 |
| ITEM1 | 2016-05-10 |     4 |      4 |                    0 |
| ITEM2 | 2016-05-04 |     1 |      0 |                    0 |
| ITEM2 | 2016-05-05 |     2 |      1 |                    0 |
| ITEM2 | 2016-05-06 |     3 |      2 |                    0 |
| ITEM2 | 2016-05-09 |     1 |      2 |                    0 |
| ITEM2 | 2016-05-10 |     1 |      2 |                    1 |
+-------+------------+-------+--------+----------------------+

【讨论】:

    【解决方案2】:

    假设对我在 cmets 中建议的样本数据进行更正,这似乎符合要求:

    declare @t table (ITEM char(5), Date date, Value tinyint)
    insert into @t(ITEM,DATE,VALUE) values
    ('ITEM1','20160504',1),
    ('ITEM1','20160505',3),
    ('ITEM1','20160506',3),
    ('ITEM1','20160509',3),
    ('ITEM1','20160510',4),
    ('ITEM2','20160504',1),
    ('ITEM2','20160505',2),
    ('ITEM2','20160506',3),
    ('ITEM2','20160509',1),
    ('ITEM2','20160510',1)
    
    ;With Ordered as (
        select
            Item,
            Date,
            Value,
            ROW_NUMBER() OVER (PARTITION BY Item ORDER BY Date) as rn
        from @t
    )
    select
        *,
        COALESCE(rn -
            (select MAX(o2.rn) from Ordered o2
            where o2.ITEM = o.ITEM and
                o2.rn < o.rn and
                o2.Value != o.Value) - 1
        , o.rn - 1) as NUM_FLAT_ENTRYPOINTS
    from
        Ordered o
    

    也就是说,我们分配行号(为每个项目单独分配),然后我们只需找到比当前行号更早的最新行号,其中Value 不同。减去这些行号(以及进一步的 1)产生我们需要的答案 - 假设可以找到这样一个较早的行。如果没有这样的较早行,那么我们显然处于特定项目开头的序列中 - 所以我们只需从行号中减去 1。

    我在这里选择了“显然正确” - 可能有一种方法可以产生效果更好的结果,但我现在不打算这样做。

    结果:

    Item  Date       Value rn                   NUM_FLAT_ENTRYPOINTS
    ----- ---------- ----- -------------------- --------------------
    ITEM1 2016-05-04 1     1                    0
    ITEM1 2016-05-05 3     2                    0
    ITEM1 2016-05-06 3     3                    1
    ITEM1 2016-05-09 3     4                    2
    ITEM1 2016-05-10 4     5                    0
    ITEM2 2016-05-04 1     1                    0
    ITEM2 2016-05-05 2     2                    0
    ITEM2 2016-05-06 3     3                    0
    ITEM2 2016-05-09 1     4                    0
    ITEM2 2016-05-10 1     5                    1
    

    【讨论】:

      【解决方案3】:

      试试这个:

      SELECT ITEM, [DATE], VALUE,
             ROW_NUMBER() OVER (PARTITION BY ITEM, VALUE, grp 
                                ORDER BY [DATE]) - 1 AS NUM_FLAT_ENTRYPOINTS 
      FROM (
      SELECT ITEM, [DATE], VALUE,
             ROW_NUMBER() OVER (PARTITION BY ITEM ORDER BY [DATE]) - 
             ROW_NUMBER() OVER (PARTITION BY ITEM, VALUE ORDER BY [DATE]) AS grp
      FROM mytable) AS t
      

      【讨论】:

        猜你喜欢
        • 2022-01-27
        • 2023-01-21
        • 1970-01-01
        • 2020-06-19
        • 2011-04-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多