【问题标题】:ID Filtering based on single date field with no end date基于没有结束日期的单个日期字段的 ID 过滤
【发布时间】:2018-04-11 21:10:59
【问题描述】:

表格类别

+----+--------+----------+------------+
| ID | Active | Category | Effective  |
+----+--------+----------+------------+
|  1 | FALSE  | A        | 1/29/2009  |
|  1 | FALSE  | B        | 5/13/2014  |
|  1 | TRUE   | B        | 9/21/2017  |
|  2 | FALSE  | B        | 3/4/2010   |
|  2 | TRUE   | A        | 2/19/2016  |
|  3 | FALSE  | A        | 10/15/2015 |
|  3 | TRUE   | B        | 8/12/2017  |
+----+--------+----------+------------+

运行时

+----+------------+
| ID |  RunDate   |
+----+------------+
|  1 | 6/14/2015  |
|  1 | 9/14/2015  |
|  1 | 10/4/2016  |
|  2 | 5/1/2014   |
|  2 | 9/21/2016  |
|  3 | 3/12/2016  |
|  3 | 12/14/2017 |
+----+------------+

我试图只选择那些有运行日期的IDs,而Category A 是有效的时间点。所以ID 1 不应该出现在输出中,因为即使它有 A 类,它的运行日期也是在 B 类生效之后。

预期输出

+----+
| ID |
+----+
|  2 |
|  3 |
+----+

尝试:

SELECT DISTINCT
    RT.ID
FROM Category C
INNER JOIN Runtime RT
    ON C.ID=RT.ID
WHERE 1=1
    AND C.Category='A'
    AND RT.rundate >= C.effective
ORDER BY RT.ID 

但是,这不考虑中间更改,只是根据每个 ID 的任何生效日期是否早于运行日期来选择结果

【问题讨论】:

  • 简而言之,您似乎想在类别表上使用交叉应用(例如cross apply (select top 1 c2.effective from category as c2 where c2.id = c.id and c2.effective > c.effective and c2.category != 'a' order by c2.effective) as c2)然后执行附加子句(where rt.rundate <= c2.effective)或类似的东西。使用自联接而不是交叉应用可以达到同样的效果,但您必须应用类似 row_number() 窗口函数的东西并只选择第一个结果。
  • 您也可以使用LEAD() 窗口函数(因为即使下一行也是“A”,也不会有太大区别)。例如select distinct rt.id from (select *, nextEffective = lead(effective) over (partition by id order by effective) from category) as c... and rt.rundate >= c.effective and (c.nextEffective is null or rt.rundate <= c.nextEffective)...

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


【解决方案1】:

-- 请试试这个查询

;with cte_category(Id, Effective,Rundate)
AS
(
SELECT c.id, max(c.effective)effective, max(r.rundate)rundate
FROM Category c
INNER JOIN runtime r on r.id = c.id
WHERE r.rundate>c.effective
GROUP BY c.id
)
SELECT DISTINCT t1.id
FROM cte_category t1
INNER JOIN cte_category t2 
    ON t1.id > t2.Id
    AND t2.Effective < t1.Rundate
ORDER BY t1.ID

【讨论】:

    【解决方案2】:

    样本数据准备

    declare @category table ( ID int, Active varchar(10), Category char(1), Effective date)
    insert into @category
    values (1, 'FALSE', 'A', '20090129')
        , (1, 'FALSE', 'B', '20140513'), (1, 'TRUE ', 'B', '20170921')
        , (2, 'FALSE', 'B', '20100304'), (2, 'TRUE ', 'A', '20160219')
        , (3, 'FALSE', 'A', '20151015'), (3, 'TRUE ', 'B', '20170812')
    
        , (4, 'FALSE', 'B', '20151015'), (4, 'FALSE ', 'A', '20170812')
        , (4, 'True ', 'A', '20180812')
        , (3, 'TRUE ', 'A', '20270812')
    
    declare @runtime table (ID int, RunDate date)
    insert into @runtime
    values (1, '20150614')
        , (1, '20150914'), (1, '20161004'), (2, '20140501')
        , (2, '20160921'), (3, '20160312'), (3, '20171214')
    

    我相信您的主要问题是具有相同价值的连续类别。您需要对这些值进行分组并找到每个此类组的最短生效日期。这就是cte 部分所做的。然后只需加入两个表即可获得预期的输出。

    ;with cte as (
        select
            ID, Category, Effective = min(Effective)
            , EndDate = isnull(lead(min(Effective)) over (partition by ID order by min(Effective)), '99991231')
        from (
            select 
                *, grp = row_number() over (partition by ID order by Effective)
                - row_number() over (partition by ID, Category order by Effective)
            from 
                @category
        ) t
        group by ID, Category, grp
    )
    
    select
        r.ID
    from
        @runtime r
        join cte c on r.ID = c.ID and r.RunDate >= c.Effective and r.RunDate < c.EndDate
    where
        c.Category = 'A'
    

    【讨论】:

      猜你喜欢
      • 2020-03-13
      • 2017-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 2022-06-23
      相关资源
      最近更新 更多