嗨,我有一点功能,我无法固定下来。我通常会在应用程序层执行此操作,但没有选项,我的T-SQL有点生锈。
简化功能:(记录日期并按间隔计算重复日期)
CREATE FUNCTION [dbo].[fn_GetRunDatesShort]
(@startDate DateTime, @weeks INTEGER)
RETURNS VARCHAR(1000)
AS
BEGIN
DECLARE @i INTEGER
DECLARE @interval INTEGER
DECLARE @outPut VARCHAR(500)
SET @outPut = ''
SET @i = 0
SET @interval = 7
WHILE (@i < @weeks)
BEGIN
SET @output = @outPut + ', ' +
Convert(varchar, DATEPART(MONTH,
DATEADD(d, @interval, @startDate)),101) + '-'
+ Convert(varchar, DATEPART(d,
DATEADD(d, @interval, @startDate)))
SET @interval = @interval + 7
SET @i = @i + 1
END
RETURN @output
END
GO
SELECT dbo.fn_GetRunDatesShort(GETDATE(), 4)
所以这会返回
, 8-21, 8-28, 9-4, 9-11
我需要让它返回
August 21, 28 | September 4, 11
答案 0 :(得分:1)
尝试avoid loops when you can generate sets much more efficiently和please stop declaring varchar
without length。
这是一个结合了两个要求的函数(生成日期集并以笨拙的输出格式格式化它们):
CREATE FUNCTION dbo.fn_GetRunDatesShort_2
(
@StartDate DATE, @Weeks TINYINT
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE @output VARCHAR(MAX);
;WITH cte1(m,d) AS
(
SELECT DATENAME(MONTH, wd), CONVERT(VARCHAR(2), DAY(wd)) FROM
(
SELECT TOP (@Weeks) DATEADD(WEEK, ROW_NUMBER() OVER
(ORDER BY [object_id]), @StartDate)
FROM sys.all_objects ORDER BY [object_id]
) AS sq(wd)
),
cte2(ds) AS
(
SELECT DISTINCT m + STUFF((SELECT ', ' + d FROM cte1 AS cte1_a
WHERE cte1_a.m = cte1.m
FOR XML PATH(''), TYPE).value('.[1]','varchar(max)'),1,1,'')
FROM cte1
)
SELECT @output = STUFF((SELECT ' | ' + ds FROM cte2
FOR XML PATH(''),TYPE).value('.[1]','varchar(max)'),1,3,'');
RETURN (@output);
END
GO
(如果您有一个Numbers表 - which you should - 您可以针对Numbers表将sys.all_objects
的查询替换为类似的表,然后您可以将WITH SCHEMABINDING
添加到该函数中 - 在这种情况下可能没有帮助,但它是something I try to do consistently whenever possible。)
用法:
SELECT dbo.fn_GetRunDatesShort_2(GETDATE(), 4);
结果:
August 21, 28 | September 4, 11
如果您的@weeks
接近52,问题就出现了 - 如果您有今年8月的日期和的日期是从明年8月开始,您会期望什么输出?
编辑2014-05-19
为了确保在正确的方向排序(去年八月工作正常,但现在似乎在五月改变了订单),您可以对功能进行此更改:
CREATE FUNCTION dbo.fn_GetRunDatesShort_2
(
@StartDate DATE, @Weeks TINYINT
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE @output VARCHAR(MAX);
;WITH cte1(mn,m,d) AS
(
SELECT
DATEPART(YEAR, wd) * 100 + DATEPART(MONTH, wd),
DATENAME(MONTH, wd), CONVERT(VARCHAR(2), DATEPART(DAY,wd))
FROM
(
SELECT TOP (@Weeks) DATEADD(WEEK, ROW_NUMBER() OVER
(ORDER BY [object_id]), @StartDate)
FROM sys.all_objects ORDER BY [object_id]
) AS sq(wd)
),
cte2(mn,ds) AS
(
SELECT DISTINCT mn, m + STUFF((SELECT ', ' + d FROM cte1 AS cte1_a
WHERE cte1_a.m = cte1.m ORDER BY mn
FOR XML PATH(''), TYPE).value('.[1]','varchar(max)'),1,1,'')
FROM cte1
)
SELECT @output = STUFF((SELECT ' | ' + ds FROM cte2 ORDER BY mn
FOR XML PATH(''),TYPE).value('.[1]','varchar(max)'),1,3,'');
RETURN (@output);
END
GO
答案 1 :(得分:1)
为了避免通过FOR XML引擎,我可以建议一些case语句结构吗?
CREATE FUNCTION dbo.fn_GetRunDatesShort_2
(
@StartDate DATE, @Weeks TINYINT
)
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE @output VARCHAR(MAX)
SET @output = ''
SELECT @output = @output + CASE @output WHEN '' THEN M ELSE CASE R WHEN 1 THEN ' | ' + M ELSE '' END END + ' ' + CASE R WHEN 1 THEN '' ELSE ', ' END + D
FROM (
SELECT year(DT) as Y, datename(month,DT) as M, CAST(datepart(day,DT) AS VARCHAR) AS D
, row_number()over(partition by month(DT), year(dt) order by day(DT)) AS R
, row_number()over(order by dt) AS R2
FROM (
SELECT dateadd(ww,row_number()over(order by object_id), @StartDate) AS DT
FROM master.sys.objects
) z
) y
WHERE R2 <= @weeks
ORDER BY R2
RETURN @output
END
GO
SELECT dbo.fn_GetRunDatesShort_2(GETDATE(),5)
May 26 | June 2 , 9 , 16 , 23
如果日期对输出很重要,也可以编辑。