【问题标题】:SQL to return list of years since a specific yearSQL 返回特定年份以来的年份列表
【发布时间】:2009-03-09 15:49:23
【问题描述】:

我需要一个从 2004 年到当前年份的年份列表作为记录集(按降序排列),无需编写存储过程。这可能吗? (SQL Server 2005)。所以它应该返回:

2009
2008
2007
2006
2005
2004

【问题讨论】:

  • 我想创建临时表或 UDF 也是禁止的?
  • 虽然是一个有趣的谜题,但我怀疑需要这个的原因。虽然 Lieven 的解决方案会起作用,但它高度依赖于表是否有足够的行。它会产生不良的依赖关系。
  • @Will,您提出了一个有效的观点,说明人们需要这个的原因。我不确定依赖问题。该语句很可能会在更大的语句中使用。如果使用这个更大语句中的表,我会接受这种依赖。
  • 要求只是在 SQL 报告中填充参数下拉列表,以允许选择自 2004 年以来的任何年份。
  • @Graeme。在这种情况下,Will 有一点关于创建不良依赖关系的观点。为了尽量减少影响,我会选择一个包含足够行且不太可能发生变化的系统表。

标签: sql-server sql-server-2005 tsql


【解决方案1】:

这是从 2004 年到现在的所有年份,使用递归 CTE:

with yearlist as 
(
    select 2004 as year
    union all
    select yl.year + 1 as year
    from yearlist yl
    where yl.year + 1 <= YEAR(GetDate())
)

select year from yearlist order by year desc;

【讨论】:

  • 我倾向于保留那些明显错误的答案,而不是我根本不喜欢的答案。我知道这是一个非常主观的领域,但我认为我们三个人不值得你投反对票。
  • +1 btw 我今天推荐的解决方案(但当时没有掌握)
  • @Lieven,我只对 2 个答案投了反对票,你的和 DJ 的(你怎么知道的?只是猜测?)。 DJ 对我来说显然是错误的。它不符合问题的基本要求。你的工作,但对我来说似乎有潜在的危险,因为搜索这个问题的人可能会使用该解决方案,而不考虑如果他们的表太小,或者被截断,或者稍后发生什么。依赖可能更改或无法控制的数据似乎是一个糟糕的解决方案。否决票可能太苛刻了……不确定。但我现在似乎无法撤消它。
  • 您概述的风险是正确的,但我的回答中也提到了它们。猜测并不是那么困难。 2年后的另一个答案,两次否决,您发布了关于错误答案的新评论并发布了新的(fwiw,更好)答案...。
  • 现在在 2014 年 - 这就是我要使用的,所以将答案从 Lieven 换到了这里..
【解决方案2】:

更新为返回当前年份加上前 5 年。应该非常快,因为这是一个小记录集。

SELECT YEAR(GETDATE()) as YearNum
UNION
SELECT YEAR(GETDATE()) - 1 as YearNum
UNION
SELECT YEAR(GETDATE()) - 2 as YearNum
UNION
SELECT YEAR(GETDATE()) - 3 as YearNum
UNION
SELECT YEAR(GETDATE()) - 4 as YearNum
UNION
SELECT YEAR(GETDATE()) - 5 as YearNum
ORDER BY YearNum DESC

【讨论】:

  • -1 union 的性能很差,如果不修改查询,这不包括“当前年份”。
  • 嘿 - 错过了当年的评论,但这么小的东西对性能的影响可以忽略不计
  • @DJ 抱歉,我完全误读了预期的查询,出于某种原因,我认为这将与订单表或其他东西结合在一起。我的错。您的查询非常好。
  • 此查询不符合要求,因为它需要每年添加另一个 UNIONed 选择。
【解决方案3】:

在任何足够大(稳定)表的任何列上使用ROW_NUMBER 是一种方法。

SELECT *
FROM (
  SELECT TOP 100 2003 + ROW_NUMBER() OVER (ORDER BY <AnyColumn>) AS Yr
  FROM dbo.<AnyTable>
  ) Years
WHERE Yr <= YEAR(GETDATE())

请注意,&lt;AnyTable&gt; 至少应包含与您需要的年数相等的行数。

编辑(库多给约书亚)

  • 最好选择一个您知道不会被截断和/或删除的表。应该想到一个足够大的system table
  • 目前,我年纪大了,也更聪明了(至少年纪大了),我会使用 CTE 来实现这个要求,就像 Joshua 给出的答案中提到的那样。与当前给定的ROW_NUMBER 解决方案相比,CTE 技术要优越得多且不易出错。

【讨论】:

  • 这行得通 - 大概我应该选择一个只有几行的表,还是没有区别?
  • @Graeme,通过进入前 100 名,您选择的桌子几乎没有什么区别,但我会使用一个小的。
  • 您必须创建一个表,添加当前年份 - 2004 年的行数,然后运行此查询?明年会怎样?
  • @scotty2012。没有创建表。明年将包含在 YEAR(GETDATE()) 语句中。唯一固定的是起始年份。没有 2004 行,只有 TOP 100 子句中指定的 100 行。此查询将持续到 2103 年
  • 当您选择的表中只有 2 行时会发生什么?
【解决方案4】:
DECLARE @YEARS TABLE (Y INT)
DECLARE @I INT, @NY INT
SELECT @I = 2004, @NY = YEAR(GETDATE())
WHILE @I <= @NY BEGIN
    INSERT @YEARS SELECT @I
    SET @I = @I + 1
END
SELECT Y 
FROM @YEARS
ORDER BY Y DESC

【讨论】:

    【解决方案5】:

    试试这个:

        declare @lowyear int
    set @lowyear = 2004
    declare @thisyear int
    set @thisyear = year(getdate())
    while @thisyear >= @lowyear
    begin
    print @thisyear
    set @thisyear = (@thisyear - 1)
    end
    

    返回

    2009
    2008
    2007
    2006
    2005
    2004
    

    当您点击 2010 年 1 月 1 日时。将返回相同的代码:

    2010
    2009
    2008
    2007
    2006
    2005
    2004
    

    【讨论】:

    • 这仅在表格中有每年的日期时才有效。例如,如果没有 2006 年的条目,则不会出现 2006 年。
    • @Jonathan,此代码独立于表。此代码打印“从 2004 年到当前年份的年份列表作为记录集(按 desc 顺序),无需编写存储过程”。虽然我认为打印这些值是错误的选择,但我的解决方案同样会返回结果集
    • 这行得通,但我喜欢上面的 Lievens 回答,因为它是一个 select 语句,并且使用了一些我需要学习的 SQL 2005 内容 :-)
    • 我喜欢这个解决方案。我将年份插入到将 @TmpYears 声明为 table(Year int)
    【解决方案6】:
    WITH n(n) AS
    (
        SELECT 0
        UNION ALL
        SELECT n+1 FROM n WHERE n < 10
    )
    SELECT year(DATEADD( YY, -n, GetDate())) 
    FROM n ORDER BY n
    

    【讨论】:

      【解决方案7】:
      SET NOCOUNT ON
      DECLARE @max int
      set @max = DATEPART(year, getdate())
      
      CREATE TABLE #temp (val int)
      
      while @max >= 2004
        BEGIN
          insert #temp(val) values(@max)
          set @max = @max - 1
        END
      
      SELECT * from #temp
      

      【讨论】:

        【解决方案8】:

        这是一个简单的查询,检查一下

        (SELECT REPLACE((TO_CHAR(SYSDATE,'YYYY')-Rownum)+1,' ',NULL) yr FROM dual CONNECT BY LEVEL < 32) year
        

        【讨论】:

          【解决方案9】:

          我的两分钱:

          CTE 是最好的答案,但我喜欢 @Lieven Keersmaekers 的答案,因为它不依赖递归/循环或创建其他对象,例如函数/日期表。关键的变化是我简单地使用带有表达式的 Top 子句,而不是前 100 个(基本上是整个表),我首先要求 N 个 unordered 行。如果您的基表恰好很大,这会更快。

          在 SQL Server 2016 上测试

          DECLARE @startYear smallint;
          SET @startYear = 2004;
          
          DECLARE @endYear smallint;
          SET @endYear = YEAR(GETDATE());
          
          -- Top uses expression to bring in N number of rows. The (@startYear - 1) is to retain the start year
          SELECT [Yr]
          FROM (
              SELECT TOP (@endYear - (@startYear - 1)) 
                    COUNT([object_id]) OVER (ORDER BY [object_id] DESC) + (@startYear - 1) AS [Yr] -- Add initial start year to Count
              FROM sys.all_objects
          ) AS T1
          ORDER BY [Yr] DESC
          

          上面的 cmets 是有效的,但是由于 OP 的要求实际上是从 2004 年开始的一小组值,因此 sys.all_objects 就足够了,因为 SQL Server 安装附带了大量的系统对象。

          希望这是一个不同的看法。

          【讨论】:

          • @LievenKeersmaekers 提交了您的变体。这是一个老帖子,但我喜欢生活在“老而金”的哲学下,因为我可以从中学到很多东西。
          【解决方案10】:

          我认为您需要创建一个日期表,然后从中选择您的范围。当您需要选择一个附有 X 数据且没有任何错过天数的日期范围时,它也会派上用场。

          【讨论】:

          • 您如何建议保持这个日期表是最新的?您的应用程序是否会因为有人忘记填写明年的日期而在几年后的 1 月 1 日崩溃?
          猜你喜欢
          • 2019-08-10
          • 1970-01-01
          • 2020-06-11
          • 1970-01-01
          • 2012-02-14
          • 1970-01-01
          • 1970-01-01
          • 2016-09-02
          • 1970-01-01
          相关资源
          最近更新 更多