【问题标题】:Valid periods - SQL VIEW有效期 - SQL VIEW
【发布时间】:2017-02-01 09:47:15
【问题描述】:

我有 2 个表(实际上有 4 个,但现在假设是 2 个),数据如下:

Table PersonA   
ClientID    ID  From        Till
1           10  1.1.2017    30.4.2017   
1           12  1.8.2017    2.1.2018

Table PersonB       
ClientID    ID  From        Till
1           6   1.3.2017    30.6.2017

我需要生成显示如下内容的视图:

ClientID        From        Till        PersonA     PersonB
1               1.1.2017    28.2.2017    10         NULL
1               1.3.2017    30.4.2017    10          6
1               1.5.2017    30.6.2017   NULL         6
1               1.8.2017    02.1.2018    12         NULL

所以基本上我需要创建一个视图来显示每个客户在特定时期内拥有的“人”。
因此,当存在重叠时,客户同时拥有 PersonA 和 PersonB(同样适用于 PersonC 和 PersonD)。
因此,在最终视图中,一个客户不能有任何重叠的日期。

我不知道如何处理。

【问题讨论】:

  • 这样做的目的是什么?为附加值添加列通常是较差的 SQL 实践,因为它违反了基于集合的查询原则。如果您只想要一份月份列表和分配的人员,您最好使用在表示层中格式化的常规数据集。
  • @iamdave - 这只是给我的一个任务。我也不喜欢它,我会尝试找到不同的方法,但现在我被这个卡住了
  • 再说一遍,目的是什么?数据最终将在哪里使用?如果它要进入 excel 进行分析,您可以在那里旋转一个标准数据集。如果你有一个像 SSRS 这样的报告层,你可以在那里旋转它。我拒绝相信您被要求直接从查询中获取这种格式的数据而没有其他用途?
  • @iamdave 我不知道 - 但不幸的是,它已经在各个地方使用(遗留代码)。除非该观点存在一些严重错误。所以我是从头开始做的..
  • @quin61 我们可以指定任何 From 和 Till 日期始终是一个时期(月)的第一天或最后一天吗? PersonA 中 Till 的值“2.1.2018”怎么样,如果有后续间隔,应该如何处理?接下来从 3.1.2018 开始?

标签: tsql join view


【解决方案1】:

在this algorithm 的改编版中,我们已经可以处理重叠:

declare @PersonA table(ClientID int, ID int, [From] date, Till date);
insert into @PersonA values (1,10,'20170101','20170430'),(1,12,'20170801','20180112');

declare @PersonB table(ClientID int, ID int, [From] date, Till date);
insert into @PersonB values (1,6,'20170301','20170630');

declare @PersonC table(ClientID int, ID int, [From] date, Till date);
insert into @PersonC values (1,12,'20170401','20170625');

declare @PersonD table(ClientID int, ID int, [From] date, Till date);
insert into @PersonD values (1,14,'20170501','20170525'),(1,14,'20170510','20171122');

with X(ClientID,EdgeDate)
     as (select ClientID
               ,case
                    when toggle = 1
                        then Till
                    else [From]
                end as EdgeDate
         from
              (
              select ClientID,[From],Till from @PersonA
              union all
              select ClientID,[From],Till from @PersonB
              union all
              select ClientID,[From],Till from @PersonC
              union all
              select ClientID,[From],Till from @PersonD
              ) as concated
             cross join
              (
              select-1 as toggle
              union all
              select 1 as toggle
              ) as toggler
),merged
     as (select distinct
                S.ClientID
               ,S.EdgeDate as [From]
               ,min(E.EdgeDate) as Till
         from
              X as S
             inner join X as E
                 on S.ClientID = E.ClientID
                    and S.EdgeDate < E.EdgeDate
         group by S.ClientID
                 ,S.EdgeDate
),prds
     as (select distinct
                merged.ClientID
               ,merged.[From]
               ,merged.Till
               ,A.ID as PersonA
               ,B.ID as PersonB
               ,C.ID as PersonC
               ,D.ID as PersonD
         from
              merged
             left join @PersonA as A
                 on merged.ClientID = A.ClientID
                    and A.[From] <= merged.[From]
                    and merged.Till <= A.Till
             left join @PersonB as B
                 on merged.ClientID = B.ClientID
                    and B.[From] <= merged.[From]
                    and merged.Till <= B.Till
             left join @PersonC as C
                 on merged.ClientID = C.ClientID
                    and C.[From] <= merged.[From]
                    and merged.Till <= C.Till
             left join @PersonD as D
                 on merged.ClientID = D.ClientID
                    and D.[From] <= merged.[From]
                    and merged.Till <= D.Till
         where not(A.ID is null
                   and B.ID is null
                   and C.ID is null
                   and D.ID is null
                  )
)
select ClientID
    ,[From]
    ,case
        when Till = lead([From]
                ) over(order by Till)
            then dateadd(d,-1,Till)
        else Till
    end as Till
    ,PersonA
    ,PersonB
    ,PersonC
    ,PersonD
from
    prds
order by ClientID
        ,[From]
        ,Till;

仅包含问题中给出的两个 Person 表的输出:

+----------+------------+------------+---------+---------+
| ClientID |    From    |    Till    | PersonA | PersonB |
+----------+------------+------------+---------+---------+
|        1 | 2017-01-01 | 2017-02-28 | 10      | NULL    |
|        1 | 2017-03-01 | 2017-04-29 | 10      | 6       |
|        1 | 2017-04-30 | 2017-06-30 | NULL    | 6       |
|        1 | 2017-08-01 | 2018-01-12 | 12      | NULL    |
+----------+------------+------------+---------+---------+

上面的脚本输出,有四个Person 表:

+----------+------------+------------+---------+---------+---------+---------+
| ClientID |    From    |    Till    | PersonA | PersonB | PersonC | PersonD |
+----------+------------+------------+---------+---------+---------+---------+
|        1 | 2017-01-01 | 2017-02-28 | 10      | NULL    | NULL    | NULL    |
|        1 | 2017-03-01 | 2017-03-31 | 10      | 6       | NULL    | NULL    |
|        1 | 2017-04-01 | 2017-04-29 | 10      | 6       | 12      | NULL    |
|        1 | 2017-04-30 | 2017-04-30 | NULL    | 6       | 12      | NULL    |
|        1 | 2017-05-01 | 2017-05-09 | NULL    | 6       | 12      | 14      |
|        1 | 2017-05-10 | 2017-05-24 | NULL    | 6       | 12      | 14      |
|        1 | 2017-05-25 | 2017-06-24 | NULL    | 6       | 12      | 14      |
|        1 | 2017-06-25 | 2017-06-29 | NULL    | 6       | NULL    | 14      |
|        1 | 2017-06-30 | 2017-07-31 | NULL    | NULL    | NULL    | 14      |
|        1 | 2017-08-01 | 2017-11-21 | 12      | NULL    | NULL    | 14      |
|        1 | 2017-11-22 | 2018-01-12 | 12      | NULL    | NULL    | NULL    |
+----------+------------+------------+---------+---------+---------+---------+

【讨论】:

  • 我从这个查询得到的输出与问题中的不匹配。您没有为每个新的人员组合显示行/期间。
  • 谢谢。 OP 示例数据中的日期不连贯,但我明白了……让我们尝试修复。
  • 它们对我来说很有意义,应该是重叠的。输出必须是每个不同 Person 组合的开始和结束日期的一行,其中包含组合开始和结束的日期以及包含哪些 Person。
  • 我找到了另一个非常相似的答案(参考包含在帖子中)并针对这个问题进行了调整 - 更新见上文。也许我们需要添加一个 LAG 构造,以使 +/- 一天规则适用于月份的开始和结束。
  • @dlatikay 我已编辑您的脚本以包含 4 个person 表格,并将期间结束日期调整为下一个期间开始前一天,期间期间是并发的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-31
  • 2013-07-28
  • 1970-01-01
  • 1970-01-01
  • 2016-02-28
  • 2021-05-28
  • 2013-01-09
相关资源
最近更新 更多