【问题标题】:Returning the following season and year返回下一个季节和年份
【发布时间】:2021-04-23 10:02:09
【问题描述】:

对于开始的特定季节和年份,我必须得到下一个开始的季节和年份,例如对于 2021 年夏季,我应该获得 2021 年秋季,对于 2021 年冬季,我应该获得 2022 年春季。

“Seasons”表有“Order”列,表示一年中的季节顺序,如下所示:

SeasonID Order Name
1 1 Spring
2 2 Summer
3 3 Autumn
4 4 Winter

备注:“订单”列可能看起来多余,但这实际上是我遇到的问题的简化/改编版本,其中“订单”列是必要的。

我有一个以@Year 和@SeasonID 作为输入参数的存储过程。我必须在@FollowingSeasonID 和@FollowingYear 参数中获取以下季节/年份,稍后我将在存储过程中使用它们。我不确定我是否使用了最好的技术:

(...)

    DECLARE @FollowingSeasonID int;
    DECLARE @FollowingYear int;
    if object_id('tempdb..#Years') is not null drop table #Years
    CREATE TABLE #Years ([Year] int)
    INSERT INTO #Years ([Year]) VALUES (@Year), (@Year + 1)

    SELECT @FollowingSeasonID = FollowingSeasonID, @FollowingYear = FollowingYear
    FROM
        (SELECT SeasonID, 
                [Year], 
                LEAD(SeasonID) OVER (ORDER BY [Year], Order) AS FollowingSeasonID,
                LEAD([Year]) OVER (ORDER BY [Year], Order) AS FollowingYear
        FROM Seasons
        CROSS JOIN #Years) t
    WHERE SeasonID = @SeasonID AND [Year] = @Year

(...)

有没有更好的方法来做到这一点?是否可以在一个查询中实现?

我需要在多个存储过程/视图/...中使用这些值,所以如果是函数,我想提取那部分代码。标量值函数不能返回两个值,所以我必须创建一个表值函数(并且我必须使用表变量而不是临时表 #Years)。但是,有没有更好的方法来做到这一点,而不是让一个总是只返回一行的表值函数?

【问题讨论】:

    标签: sql sql-server tsql user-defined-functions window-functions


    【解决方案1】:

    您可以将此代码包装在 UDF 中,您应该能够获得 seasonId, year

    DECLARE @season table(SeasonId int, SeasonOrder int, Name varchar(10))
    INSERT INTo @season values
    (1,    1    ,'Spring')
    ,(2,    2   ,'Summer')
    ,(3,    3   ,'Autumn')
    ,(4,    4   ,'Winter');
    
    DECLARE @FollowingSeasonID int;
    DECLARE @FollowingYear int;
    DECLARE @year int = 2021, @SeasonId int = 2;
    
    ;WITH CTE_YearSeason AS
    (
    SELECT y,s.* FROM @season as s
    CROSS APPLY
    (VALUES (@year), (@year+1)
    ) as t(y)
    ), cte_ranking as
    (
    SELECT *, row_number() over (order by y,SeasonOrder) as rnk 
    FROM CTE_YearSeason)
    SELECT SeasonId, Y as year 
    FROM  cte_ranking as c
    where c.rnk = (SELECT c1.rnk 
    from cte_ranking as c1
    where c1.SeasonId = @SeasonId and c1.y = @year) +1
    
    SeasonId year
    3 2021

    【讨论】:

    • 感谢您的回答,我缺少的部分是(VALUES (@year), (@year+1)) as t(y)... 有了更改,我发布的版本似乎比这个更简单。顺便提一句。为什么要使用 CROSS APPLY,在这种情况下 CROSS JOIN 还不够吗?
    • @petra,是的。您可以在此处使用 CROSS JOIN 代替 CROSS APPLY。
    • @petra,是的。你的版本看起来很简单。
    【解决方案2】:

    除非我误解了,给定一个 seasonId,您只需要以下 Id 并将其重置为 1 并在必要时增加年份?

    declare @seasonid int=1, @Year int=2021
    
    select  FollowingSeasonID, @Year + yr FollowingYear
    from (
        select *,
            IsNull(Lead(SeasonId) over(order by [order]),1) FollowingSeasonID,
            case when Lead(SeasonId) over(order by [order]) is null then 1 else 0 end yr
        from seasons
    )s
    where SeasonId=@SeasonId 
    

    如果您不想使用 TVF,您可以将其用作视图/cte,加入 currentId 并返回 followingId 和 yr 值以添加到您的当前年份。

    【讨论】:

    • 已给出季节 ID 和年份。并不是我不想 使用 TVF,我只是想知道这是否是正确的方法,因为我必须返回整个表才能得到两个整数,这似乎很奇怪。我认为我不能把它作为一个视图,因为我不知道在“年份”列中放什么(我可以不放年份,而是增加年份的数字,就像你正在做的那样,但这会很多如果我能直接得到年份就更好了)。
    • 我不明白为什么你不能让它成为一个只返回一个值以添加到现有值的视图 - 也许提供一个实际的用例示例 - 你是否使用它单独的参数值或加入表等?
    • 如果我有这样的“未调整”数据——一列表示我应该使用的实际值,另一列表示我应该添加的值——它增加了我忘记它并犯错误的可能性。 .这是主要原因。现在,我只将它与提供的示例中的单个参数值一起使用,但实际上我迟早会在连接中需要它,然后我将不得不使用您建议的解决方案......
    猜你喜欢
    • 1970-01-01
    • 2021-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-30
    • 1970-01-01
    • 2017-12-09
    • 1970-01-01
    相关资源
    最近更新 更多