【问题标题】:How to do partition by in sql for the below example以下示例如何在 sql 中进行分区
【发布时间】:2018-09-28 05:26:39
【问题描述】:

我有一张如下表:

  EmpID   | Type | timestamp |         | block_id
    1     |  'R' | 2018-04-15 01:13:15 | 1234
    1     |  'P' | 2018-04-15 05:13:15
    1     |  'P' | 2018-04-15 05:13:15
    1     |  'P' | 2018-04-15 05:13:15
    1     |  'D' | 2018-04-15 07:13:15
    1     |  'D' | 2018-04-15 08:13:15
    1     |  'D' | 2018-04-15 10:13:15
    1     |  'R' | 2018-04-15 13:13:00 | 3453
    1     |  'P' | 2018-04-15 13:15:15
    1     |  'P' | 2018-04-15 13:15:15
    1     |  'P' | 2018-04-15 13:15:15
    1     |  'D' | 2018-04-15 14:13:00
    1     |  'D' | 2018-04-15 15:13:00
    1     |  'D' | 2018-04-15 16:13:37
    2     |  'R' | 2018-04-15 04:15:00 | 0986
    2     |  'P' | 2018-04-15 04:20:00
    2     |  'D' | 2018-04-15 05:11:33

我正在尝试获得如下输出:

EmpID  | begin_timestamp     | end_timestamp      | block_id | P_count | D_count
1      | 2018-04-15 01:13:15 |2018-04-15 10:13:15 | 1234    | 3       | 3
1      | 2018-04-15 13:13:00 | 2018-04-15 16:13:37| 3453    | 3       | 3
2      | 2018-04-15 04:15:00 | 2018-04-15 05:11:33| 0986    | 1       | 1

即,这些是某种块,每个 empId 可以有多个块。所以从上面的示例表中,empID'1' 有 2 个块(从第 1 行开始到第 7 行),第 2 个块从第 8 行到第 14 行; empID 2 有 1 个块,第 14 行到第 16 行。

如何获得所需的输出?我被困住了!!请帮忙。

此外,该表没有排序,如上所示,仅用于说明。

更新:抱歉,我之前没有提到,block_id 具有字母数字值,而不仅仅是数字值。 block_id 可以是 'AX2#2', '123R2*',... 任何随机值。

【问题讨论】:

  • 向我们展示您的查询?
  • 尝试使用group by
  • 在手机上确实无法尝试任何代码,但您可以尝试查看子查询以将这些块分配给所有日期
  • 您的 SQL 服务器版本是多少?

标签: sql sql-server group-by amazon-redshift window-functions


【解决方案1】:

group by 子句与条件聚合用于计数

select EmpID, min(timestamp) as begin_timestamp,
              max(timestamp) as end_timestamp, new_block_id as block_id,
              sum(case when Type = 'P' then 1 else 0 end) as P_count,
              sum(case when Type = 'D' then 1 else 0 end) as D_count
from table t
group by EmpID, block_id;

编辑: 至于 MtwStark 如果 block_id 是 nullblank,我已经添加了其他版本

select EmpID, min(timestamp) as begin_timestamp,
              max(timestamp) as end_timestamp, block_id,
              sum(case when Type = 'P' then 1 else 0 end) as P_count,
              sum(case when Type = 'D' then 1 else 0 end) as D_count
from (select *,
           sum(block_id) over (partition by empid order by timestamp) as new_block_id
      from table)t
group by EmpID, new_block_id;

【讨论】:

  • 不起作用,block_id = null 不是新组,block_id = null 的行应计入正确组
  • @MtwStark... 这假设block_id 没有null
  • block_id = '' 出现同样的问题,因为您看到输入数据 block_id 仅对第一行块有值
  • @MtwStark .. 好吧,这总是假设 block_id 总是格式正确的。顺便说一句,与其他版本一起添加。
  • @YogeshSharma 对不起,block_id 不一定只有数字值,它是字母数字。
【解决方案2】:

此查询适用于 SQL Server 2012+。对于旧版本,将内部查询更改为自联接

select
    EmpID, begin_timestamp = min([timestamp]), end_timestamp = max([timestamp])
    , block_id = max(block_id), P_count = sum(iif([Type] = 'P', 1, 0))
    , D_count = sum(iif([Type] = 'D', 1, 0))
from (
    select
        *, grp = sum(block_id) over (partition by EmpID order by [timestamp])
    from
        myTable
) t
group by EmpID, grp

【讨论】:

    【解决方案3】:

    试试这样...

         ;WITH CTE
         AS
        (
            SELECT  1   AS RowNo,
            (SELECT COUNT(*) FROM Mytable) AS RowCnt,
            EmpID,
            Type,
            timestamp,
            block_id,
            CASE WHEN Type = 'P' THEN 1 ELSE 0 END AS P_count,
            CASE WHEN Type = 'D' THEN 1 ELSE 0 END AS D_count
    FROM    (SELECT ROW_NUMBER()OVER(ORDER BY EmpID) AS ID,* FROM Mytable) t
    WHERE   t.ID    =   1
    
    UNION ALL
    
    SELECT  CTE.RowNo+1,
            CTE.RowCnt - 1,
            t1.EmpID,
            t1.Type,
            t1.timestamp,
            CASE WHEN t1.block_id IS NULL THEN CTE.block_id ELSE t1.block_id END 
            AS block_id,
            CASE WHEN t1.Type = 'P' THEN 1 ELSE 0 END AS P_count,
            CASE WHEN t1.Type = 'D' THEN 1 ELSE 0 END AS D_count
    FROM    CTE
    JOIN    (SELECT ROW_NUMBER()OVER(ORDER BY EmpID) AS ID,* FROM Mytable) t1
        ON  (1  =   1)
    WHERE   t1.ID   =   CTE.RowNo+1
        AND CTE.RowCnt  <>  0
      )
    
      SELECT EmpID,MIN(timestamp)  begin_timestamp,MAX(timestamp) 
            end_timestamp,block_id,SUM(P_count) P_count,SUM(D_count) D_count
       FROM CTE
       GROUP BY EmpID,block_id
       ORDER BY EmpID
    

    【讨论】:

    • 我收到以下错误:无效操作:关系“cte”不存在;
    • 我们也可以避免在这里使用 UNION ALL 吗?因为桌子真的很大。
    【解决方案4】:

    条件聚合是一个非常好的选择,这个版本几乎与已经发布的版本相同,但对block_id 进行了(非常)小的优化

    select EmpID, begin_timestamp = min([timestamp]), end_timestamp = max([timestamp]), block_id, P_count = sum(iif([Type] = 'P', 1, 0)), D_count = sum(iif([Type] = 'D', 1, 0))
    from (
        select EmpID, Type, timestamp, max(block_id) over (partition by EmpID order by [timestamp]) block_id  from d
    ) d
    group by EmpID, block_id
    order by EmpID, block_id
    

    仅用于演示目的,您也可以考虑PIVOT解决方案:

    select EmpID, block_id, MIN(timestamp) begin_timestamp, MAX(timestamp) end_timestamp, SUM(D) D, SUM(P) P
    from (
        select *
        from (
            select EmpID, Type, block_id, [timestamp]
            from (
                select EmpID, Type, timestamp, max(block_id) over (partition by EmpID order by [timestamp]) block_id from d
            ) t
        ) g
        pivot (count(type) for type in (D,P)) p
    ) c
    group by EmpID, block_id
    order by EmpID, block_id
    

    编辑

    我考虑过block_id 字母数字而不是数字,因为我注意到其中一个的前导零。这就是只使用MAX 而不是SUM + MAX 的原因。

    MAX 适用于数字和 alpha,SUM 仅适用于数值。此外,您不需要对该字段进行第二次聚合。

    这里唯一真正的问题是行的时间顺序,如果相同EmpID 的两个块可以重叠,并且block_id 仅在每个块的第一行(类型'R')上具有值,你有一个真正的大问题,因为您没有信息可用于在类型“R”的第二行之后分配类型“D”和“P”行。
    在这种情况下,抱歉,无法解决您的问题。

    如果我们可以确保块不重叠或 block_id 在所有行中都有值,max(block_id) over (partition by EmpID order by [timestamp]) 将完成这项工作。

    在 redshift 中,您应该使用 UNBOUNDED FOLLOWING 而不是 CURRENT ROW

    【讨论】:

    • 哦,顺便说一句,我确实必须将您的查询中的一行更改为:max(route_details_route_id) over (partition by execution_id order by timestamp rows BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) 因为我在红移。不确定这是否会影响。
    • 我刚刚检查了更大的数据集,它没有给出准确的结果。我认为原因是 block_id 是随机的字母数字值,做 max 并没有给出结果。
    • MAX() 不是问题,它适用于字母数字值。如果您需要帮助,您应该提取一些有问题的数据,然后将它们发布,以便我检查。唯一的问题可能是时期重叠。另外,检查UNBOUNDED FOLLOWING 而不是CURRENT ROW
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-03
    • 1970-01-01
    • 2021-06-14
    • 2012-02-20
    • 1970-01-01
    • 2015-12-14
    相关资源
    最近更新 更多