如何在 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 的循环中对函数求和?的主要内容,如果未能解决你的问题,请参考以下文章