【问题标题】:Extracting value per year from a range从一个范围内每年提取价值
【发布时间】:2020-09-17 06:18:11
【问题描述】:

我有一张这样的桌子

ID    START        END             VALUE
1     2010-01-01   2013-12-31       RED
1     2015-01-01   2015-04-11       BLUE
2     2012-01-01   2013-06-05       YELLOW
2     2013-08-04   2013-12-31       RED
2     2017-01-01   2018-12-31       GREY

期望的输出:

ID   YEAR     VALUE
1    2010      RED
1    2011      RED
1    2012      RED
1    2013      RED
1    2015      BLUE
2    2012      YELLOW
2    2013      RED
2    2017      GREY
2    2018      GREY

我尝试过的:

select
     case 
     when (START > '2010-01-01' or END < '2010-12-31') = TRUE THEN '2010'
     when (START > '2011-01-01' or END < '2011-12-31') = TRUE THEN '2011'
     ...

但这并没有得到所有值,例如 YEAR = 2011 的 VALUE = RED。我得到 YEAR = 2010 的 VALUE = RED,所以在开始或结束日期中只表示一年的日期。

【问题讨论】:

  • OUTER JOIN 一年帮助表/cte.
  • 你的 dbms 是什么?
  • 你试过存储过程吗?
  • 我必须用 sql 来做这件事。
  • 您使用的是什么 DBMS?什么版本?您可能需要在这里进行递归查询。

标签: sql sqlite


【解决方案1】:

您可以使用递归 CTE 来做到这一点:

WITH cte AS (
  SELECT ID, START, END, VALUE, STRFTIME('%Y', START) + 0 Year
  FROM tablename
  UNION ALL
  SELECT ID, START, END, VALUE, Year + 1
  FROM cte  
  WHERE Year < STRFTIME('%Y', END) + 0
)  
SELECT DISTINCT ID, Year, 
       FIRST_VALUE(VALUE) OVER (PARTITION BY ID, Year ORDER BY END DESC) VALUE
FROM cte
ORDER BY ID, Year

请参阅demo

【讨论】:

  • 正确。应该补充一点,递归 CTE 从 SQLite 3.8.3 开始可用,从 SQLite 3.25.0 开始提供窗口函数。后者现在已经五岁了,但这应该不是问题:-)
  • @forpas 非常感谢。我不知道,你可以做一个递归 cte。谢谢!
【解决方案2】:

CASE WHEN 只考虑第一个匹配项。如果范围是 2010-01-01 到 2013-12-31,那么第一个案例匹配并且该行将仅在此方法中被视为“2010”。而且,你弄错了条件。当开始在“2010-01-01”之后或结束在“2010-12-31”之前,但当开始小于或等于“2010-12-31”且结束大于或等于时,一行不在 2010 年'2010-01-01'。

选择 案子 当 (START > '2010-01-01' 或 END '2011-01-01' 或 END

如果您想要一个仅适用于一组固定年份(例如 2010 年到 2020 年)的非常简单的查询,您会寻找UNION ALL

select id, 2010 as year, value from mytable where start <= '2010-12-31' and end >= '2010-01-01'
union all
select id, 2011 as year, value from mytable where start <= '2011-12-31' and end >= '2011-01-01'
union all
select id, 2012 as year, value from mytable where start <= '2012-12-31' and end >= '2012-01-01'
...
order by id, year;

这可以为每个 ID 和年份提供多行。如果你不想要这个,那么聚合这个结果。

如果您想允许表中的所有年份,无论是哪个年份,并且您不想从第 0 年开始编写一个联合所有查询,那么您将需要一个递归查询。

【讨论】:

    【解决方案3】:

    请试试这个

    DECLARE @MinDate DATE 
    DECLARE @MaxDate DATE
    
    set @MinDate = (select top 1 min([START]) from [dbo].[Table_1] )
    set @MaxDate = (select top 1 max([END]) from [dbo].[Table_1]  )
    
    SELECT [ID],year(tt.Date) as year , [VALUE] FROM [dbo].[Table_1] as t1
    left outer join (SELECT  TOP  (DATEDIFF(YEAR , @MinDate, @MaxDate) + 1) Date = DATEADD(YEAR , ROW_NUMBER() OVER(ORDER BY a.object_id) - 1, @MinDate)
    FROM    sys.all_objects a CROSS JOIN sys.all_objects b) tt on tt.Date between DATEADD(yy, DATEDIFF(yy, 0,  t1.[START]), 0) and  DATEADD (dd, -1, DATEADD(yy, DATEDIFF(yy, 0,  t1.[END]) +1, 0))
    

    【讨论】:

    • 这段代码可能(也可能不会)在 SQL Server 中工作,但在 SQLite 中不起作用。
    • 您可以将两个日期之间生成日期的代码替换为: WITH RECURSIVE dates(date) AS ( VALUES('2015-10-03') UNION ALL SELECT date(date, '+1 day') FROM dates WHERE date
    猜你喜欢
    • 2016-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多