看图像。我想用sql生成完整的结果。现在我正在生成带有大量前端代码的UI。
一个人给了我整个sql,它通过SQL给我正确的输出。这是完整的sql。
DECLARE @FirstOfMonth DATE={ts'2011-02-01 00:00:00'};
WITH DateBorders AS
(
SELECT @FirstOfMonth AS FirstOfMonth
,DATEADD(DAY,-1,DATEADD(MONTH,1,@FirstOfMonth)) AS LastOfMonth
)
,ThirtyOneNumbers(N) AS
(
SELECT N FROM(VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
,(11),(12),(13),(14),(15),(16),(17),(18),(19),(20)
,(21),(22),(23),(24),(25),(26),(27),(28),(29),(30)
,(31))t(N)
)
,RunningDates AS
(
SELECT N AS DayNumber
,DATEADD(DAY,N-1,DateBorders.FirstOfMonth) DayDate
FROM ThirtyOneNumbers,DateBorders
WHERE ThirtyOneNumbers.N<=DATEDIFF(DAY,DateBorders.FirstOfMonth,DateBorders.LastOfMonth) + 1
)
,RunningDatesExt AS
(
SELECT RunningDates.*
,wd.WeekDayInx
FROM RunningDates
CROSS APPLY(SELECT DATEPART(WEEKDAY,DayDate)) AS wd(WeekDayInx)
)
,HourSheetSum AS
(
SELECT hs.SpecialistID
,hs.EntryDate
,SUM(hs.HoursData) AS SumHoursData
FROM HourSheet AS hs
GROUP BY hs.SpecialistID,hs.EntryDate
)
,DataToPivot AS
(
SELECT s.SpecialistID,
s.Name,
rde.DayNumber,
CASE WHEN h.SumHoursData IS NULL THEN CASE WHEN rde.WeekDayInx IN(6,7) THEN 'S'
ELSE '8.00'
END
ELSE CAST(h.SumHoursData AS VARCHAR(100)) END AS HoursData
FROM RunningDatesExt AS rde
CROSS JOIN Specialists AS s
LEFT JOIN HourSheetSum AS h ON h.SpecialistID=s.SpecialistID AND rde.DayDate=h.EntryDate
)
SELECT pvt.*
FROM DataToPivot AS d
PIVOT
( MIN(HoursData)
FOR DayNumber IN
( [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11],
[12], [13], [14], [15], [16], [17], [18], [19], [20], [21],
[22], [23], [24], [25], [26], [27], [28], [29], [30], [31]
)
) AS pvt
ORDER BY pvt.SpecialistID;
现在的问题是上面的sql工作正常,但变得非常大。我怎样才能缩短它。
此CTE值中的是硬编码的
ThirtyOneNumbers(N) AS
(
SELECT N FROM(VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)
,(11),(12),(13),(14),(15),(16),(17),(18),(19),(20)
,(21),(22),(23),(24),(25),(26),(27),(28),(29),(30)
,(31))t(N)
)
但是我的下面的sql会在几个月内返回.....它可能是31或30或28等
DECLARE @Days AS INT
DECLARE @DateInput AS VARCHAR(10)
SET @DateInput = '01/01/2011'
SELECT @Days = DAY(DATEADD(DD,-1,DATEADD(MM,DATEDIFF(MM,-1,@DateInput),0)))
PRINT @Days
;WITH TotalDaysInMoth(MonthNumber) AS
(
SELECT 1
UNION ALL
SELECT MonthNumber+1
FROM TotalDaysInMoth
WHERE MonthNumber < @Days
)
select * from TotalDaysInMoth;
现在我的问题是如何使这个区域动态而不是硬编码值
SELECT pvt.*
FROM DataToPivot AS d
PIVOT
( MIN(HoursData)
FOR DayNumber IN
( [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11],
[12], [13], [14], [15], [16], [17], [18], [19], [20], [21],
[22], [23], [24], [25], [26], [27], [28], [29], [30], [31]
)
) AS pvt
ORDER BY pvt.SpecialistID;
我正在寻找建议。在FOR DayNumber IN(1,2,3 ...... 31)中的是硬编码的,我不想要。我希望它应该显示没有几个月的日子。如果不使这个区域充满活力,我怎样才能实现自己的目标并缩短目标。
寻找建议。感谢
答案 0 :(得分:4)
为什么你想要它是动态的?这并没有缩短它,它使它变得更加复杂。在SQL中,你应该在你的选择/结果集中有一个固定数量的列,而不是根据参数给出的日期而改变。
使动态数据库动态化的唯一方法是创建包含该月实际天数的动态SQL - 这意味着在varchar变量中创建所有SQL。
此外,使用递归CTE创建31天的方式也毫无意义,因为RunningDates已经有了这个限制(N&lt; = DATEDIFF(DAY,DateBorders.FirstOfMonth,DateBorders.LastOfMonth)+ 1)
编辑:忘记提及,显示或隐藏列29-31应该在GUI中完成,或者更准确地说,你甚至不应该有枢轴,你应该返回行中的数据并进行转置进入客户端的列。
答案 1 :(得分:0)
您可以使用以下方法将第一个四个CTE替换为:
DECLARE @FirstOfMonth DATE={ts'2011-02-01 00:00:00'};
SELECT DAY(DayNumber) AS DayNumber
,CAST(DayNumber AS Date) DayDate
,DATEPART(WEEKDAY,DayNumber) AS WeekDayIndx
FROM(
SELECT TOP (CASE WHEN YEAR(@FirstOfMonth) % 4 = 0 THEN 366 ELSE 365 END)
DATEADD(DAY
,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) -1
, CAST(YEAR(@FirstOfMonth) AS VARCHAR(4)) + '0101' ) DayNumber
From master..spt_values
)x
WHERE DayNumber > DATEADD(SECOND,-1,DATEADD(MONTH, DATEDIFF(MONTH,0,@FirstOfMonth),0))
AND DayNumber < DATEADD(SECOND,-1,DATEADD(MONTH, DATEDIFF(MONTH,0,@FirstOfMonth)+1,0))