如何在 sql 的循环中对函数求和?

Posted

技术标签:

【中文标题】如何在 sql 的循环中对函数求和?【英文标题】:how can I sum on a function in a loop in sql? 【发布时间】:2017-06-25 06:53:43 【问题描述】:

我们有一个函数,它接收 4 个变量并返回一个 int 结果。 我们希望对表中的每条记录都使用此函数,而这 4 个变量是从每条记录中获取的。

我该如何解决?

我的功能:

CREATE FUNCTION dbo.occupiedDaysPerListingFunction(@CheckIn date, @CheckOut date, @Email varchar(80), @Title varchar(50))
            RETURNS int
    AS
    BEGIN
    DECLARE @nightsInRange int  
    select @nightsInRange= (CASE  
    WHEN  DATEDIFF(day,@CheckIn,getdate()) >0 and DATEDIFF(day,@CheckOut,getdate())+100 <0 THEN 100
    WHEN  DATEDIFF(day,@CheckIn,getdate()) >0 and DATEDIFF(day,@CheckOut,getdate())+100 >0 THEN DATEDIFF(day,getdate(),@CheckOut)
    WHEN  DATEDIFF(day,getdate(),@CheckIn) >0 and DATEDIFF(day,getdate(),@CheckIn) <100 and DATEDIFF(day,@CheckOut,getdate())+100 >0 THEN DATEDIFF(day,@CheckIn,@CheckOut)
    WHEN  DATEDIFF(day,getdate(),@CheckIn) >0 and DATEDIFF(day,getdate(),@CheckIn) <100 and DATEDIFF(day,@CheckOut,getdate())+100 <0 THEN DATEDIFF(day,@CheckIn,getdate())+100
    WHEN  DATEDIFF(day,@CheckIn,getdate()) <0 THEN 0
END)
FROM [dbo].[ORDERS] o
        WHERE o.[E-mail] = @Email and o.[title]=@Title
        RETURN @nightsInRange
end

【问题讨论】:

您的问题标题提到了“sum”,但问题中没有出现。为什么不呢? 【参考方案1】:

我会开始确保你的函数总是返回一个整数。您当前的函数向您的 [Orders] 表发送一个查询,其中包含电子邮件和标题的过滤器。您应该考虑如果您的 [Orders] 表包含具有相同电子邮件和标题的多行,将会(应该)发生什么。

我会像这样重写你的函数:

CREATE FUNCTION dbo.occupiedDaysPerListingFunction(@CheckIn DATE, @CheckOut 
DATE, @Email VARCHAR(80), @Title VARCHAR(50))
RETURNS INT
AS
BEGIN
    DECLARE @nightsInRange INT = 0;
    SET @nightsInRange = (SELECT TOP 1 CASE  
        WHEN  DATEDIFF(day,@CheckIn,getdate()) >0 and     DATEDIFF(day,@CheckOut,getdate())+100 <0 THEN 100
        WHEN  DATEDIFF(day,@CheckIn,getdate()) >0 and     DATEDIFF(day,@CheckOut,getdate())+100 >0 THEN DATEDIFF(day,getdate(),@CheckOut)
        WHEN  DATEDIFF(day,getdate(),@CheckIn) >0 and     DATEDIFF(day,getdate(),@CheckIn) <100 and DATEDIFF(day,@CheckOut,getdate())+100     >0 THEN DATEDIFF(day,@CheckIn,@CheckOut)
        WHEN  DATEDIFF(day,getdate(),@CheckIn) >0 and     DATEDIFF(day,getdate(),@CheckIn) <100 and DATEDIFF(day,@CheckOut,getdate())+100     <0 THEN DATEDIFF(day,@CheckIn,getdate())+100
        WHEN  DATEDIFF(day,@CheckIn,getdate()) <0 THEN 0
        ELSE 0
    END
    FROM [dbo].[ORDERS] o
    WHERE o.[E-mail] = @Email and o.[title]=@Title)

    RETURN @nightsInRange
END

此功能仍然不是完美无缺的,但它确保: 1)您的函数在所有情况下都返回一个值(即使没有找到记录) 2)您的功能不会因为多个[Orders]具有相同的电子邮件和标题而失败

之后我会从这样的查询中调用你的函数:

SELECT
    CheckInDate,
    CheckOutDate,
    Email,
    Title,
    dbo.occupiedDaysPerListingFunction(CheckInDate, CheckOutDate, Email,     Title) AS OccupiedDaysPerListing
FROM
    [YourDatabase].[YourSchema].[YourTable]

希望对您有所帮助。

【讨论】:

通常TOP 应该与ORDER BY 配对。在这种情况下,任何行都可以,添加注释来解释预期的行为是值得的。请注意,GetDate() 的每个实例将为所有行返回一个值,但不同的实例可能返回不同的值。最佳做法是获取单个值,例如declare @Now as DateTime = GetDate();,并在函数的其余部分使用变量 @Now 同意你的观点,@HABO。这些将是查询的额外改进。在这种情况下,他很可能希望按 OrderDate 降序排序。 IE。 ORDER BY [OrderDate] DESC【参考方案2】:

Cursor 只是适合此任务的工具。 Сursor 是可能的选项之一。您还可以使用 WHILE LOOP、CTE 等。

DECLARE @iterator CURSOR
DECLARE @bufferTable TABLE 
(
    CheckIn date,
    CheckOut date,
    Email varchar(80),
    Title varchar(50)
)

SET @iterator = CURSOR FOR
SELECT
    CheckIn,
    CheckOut,
    Email,
    Title
FROM  [YourDatabase].[YourSchema].[YourTable]    

OPEN @iterator 

FETCH NEXT FROM @iterator 
INTO @bufferTable

WHILE @@FETCH_STATUS = 0
BEGIN

    /* Your function comes into play */
    dbo.occupiedDaysPerListingFunction(@bufferTable.CheckIn, @bufferTable.CheckOut, @bufferTable.Email, @bufferTable.Title)

    FETCH NEXT FROM @iterator 
    INTO @bufferTable 
END; 

CLOSE @iterator
DEALLOCATE @iterator

GO

编辑:

根据@Rasmus Dybkjær 的回答,您可以像这样缩小 SELECT 查询范围:

SELECT InnerQueryTable.OccupiedDaysPerListing FROM 
(
    SELECT
        CheckInDate,
        CheckOutDate,
        Email,
        Title,
        dbo.occupiedDaysPerListingFunction(CheckInDate, CheckOutDate, Email, Title) AS OccupiedDaysPerListing
    FROM
        [YourDatabase].[YourSchema].[YourTable]
) AS InnerQueryTable
GO

【讨论】:

以上是关于如何在 sql 的循环中对函数求和?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SQL 中对每个 UniqueId 只求和一次?

如何在SQL中对相邻行进行分组并对数据求和

如何在 SQL Server 中对具有相同 ID 的多行求和

如何在Python中对包含分类变量的列的行数求和[重复]

如何在 postgres 中对 jsonb 值求和?

如何在Ruby中对数组中对象的属性求和