最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

sql server - Automate up to 10 years in a result set - Stack Overflow

programmeradmin0浏览0评论

If I was to do a simple select statement that returned one row, where the first column is a row number - in this instance a 1, a start date and an end date. .

SELECT 1, GETDATE(), DATEADD(year,1,GETDATE()) 'EffectiveToDate' 

So this would give me 1, 2025-01-17 and one year later 2026-01-16.

What I want to achieve, is a further 9 rows - 2 through to 10 that would then be the same as row one - but one year one.

So Row 2 would be -

2, 2026-01-17 and 2027-01-16

and so on. . .

If I was to do a simple select statement that returned one row, where the first column is a row number - in this instance a 1, a start date and an end date. .

SELECT 1, GETDATE(), DATEADD(year,1,GETDATE()) 'EffectiveToDate' 

So this would give me 1, 2025-01-17 and one year later 2026-01-16.

What I want to achieve, is a further 9 rows - 2 through to 10 that would then be the same as row one - but one year one.

So Row 2 would be -

2, 2026-01-17 and 2027-01-16

and so on. . .

Share Improve this question edited Jan 19 at 20:17 Thom A 96.3k11 gold badges61 silver badges95 bronze badges asked Jan 17 at 15:44 ikilledbillikilledbill 2411 gold badge3 silver badges19 bronze badges 3
  • 1 Use a Calendar table. If you don't have one, invest in one. – Thom A Commented Jan 17 at 15:52
  • 2 stackoverflow/questions/21425546/… – BenderBoy Commented Jan 17 at 15:57
  • 2 As a side note, don't use single quotes (') for aliases. Single quotes are for literal strings, not delimit identifying object names. They have some "gotchas" as their behaviour is not consistent depending on where they are referenced. Also some syntaxes with literal string aliases are deprecated. Stick to object and alias names that don't need delimit identifying, and if you must delimit identify them use T-SQL's identifier, brackets ([]), or ANSI-SQL's, double quotes ("). – Thom A Commented Jan 17 at 16:07
Add a comment  | 

2 Answers 2

Reset to default 1

Managed to do it this way -

    declare @startDate datetime,  
        @endDate datetime;  
  
select  @startDate = getdate(),  
        @endDate = dateadd(year,1,getdate()) -1


;with myCTE as  
   (  
      select 1 as ROWNO,@startDate "StartDate" ,@EndDate "EndDate"
  
  union all  
       select  ROWNO+1 ,dateadd(YEAR, 1, StartDate) ,  dateadd(YEAR, 1, EndDate)
 
  FROM  myCTE  

  where ROWNO+1 <= 10

    )  
select ROWNO,Convert(varchar(10),StartDate,105)  as StartDate ,Convert(varchar(10),EndDate,105) from myCTE 

There are many way you can achieve this. One of the simplest is likely using GENERATE_SERIES:

SELECT GS.value+1,
       DATEADD(YEAR, -GS.Value,GETDATE()) AS DateFrom,
       DATEADD(YEAR, -GS.Value+1,GETDATE()) AS DateTo
FROM GENERATE_SERIES(0,9,1) GS;

If you aren't on SQL Server 2022+, then you won't have access to the function. For such a small number, you could just put the literal values in a VALUES clause:

SELECT V.value+1,
       DATEADD(YEAR,-V.Value, GETDATE()) AS DateFrom,
       DATEADD(YEAR, -V.Value+1,GETDATE()) AS DateTo
FROM (VALUES(0),(1),(2),(3),(4),(5),(6),(7),(8),(9))V(Value);

If you wanted this scale, however, you could use an inline tally:

DECLARE @I int = 10;

WITH N AS(
    SELECT N
    FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
Tally AS(
    SELECT TOP (@I) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
    FROM N N1, N N2, N N3, N N4, N N5, N N6, N N7) --Up to 10,000,000 rows
SELECT T.I,
       DATEADD(YEAR,-T.I+1, GETDATE()) AS DateFrom,
       DATEADD(YEAR, -T.I+2,GETDATE()) AS DateTo
FROM Tally T;

Or you could also create a UDF for the Tally and use that:

CREATE   FUNCTION [fn].[Tally] (@LastNumber bigint, @Zero bit) 
RETURNS table
WITH SCHEMABINDING
AS RETURN

    WITH N AS(
        SELECT N
        FROM (VALUES(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL),(NULL))N(N)),
    Tally AS(
        SELECT 0 AS I
        WHERE @Zero = 0
          AND @LastNumber IS NOT NULL
        UNION ALL
        SELECT TOP (ISNULL(@LastNumber,0))
               ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS I
        FROM N N1, N N2, N N3, N N4, N N5, N N6, N N7) --Up to 10,000,000 rows
    SELECT I
    FROM Tally T;
GO

SELECT T.I+1,
       DATEADD(YEAR, -T.I+1,GETDATE()) AS DateFrom,
       DATEADD(YEAR, -T.I+2,GETDATE()) AS DateTo
FROM fn.Tally(9,0) T;

Or, finally, you could use a Calendar table. One method could look like this, however, note this actually fails if the current date is 29 February:

SELECT ROW_NUMBER() OVER (ORDER BY C.CalendarDate DESC),
       C.CalendarDate AS DateFrom,
       DATEADD(YEAR, 1, C.CalendarDate) AS DateTo
FROM tbl.Calendar C
WHERE C.CalendarMonth = MONTH(GETDATE())
  AND C.CalendarDay = DAY(GETDATE())
  AND C.CalendarYear >= YEAR(GETDATE()) - 9
  AND C.CalendarYear <=  YEAR(GETDATE());

It would, in truth, likely be easier to use a Tally here, rather than a Calendar table.

发布评论

评论列表(0)

  1. 暂无评论