我正在编写一个函数来计算用户在我网站上的总秒数。之后,我将秒数转换为hh:mm:ss
:
select * into #temp from [MyFunction](timestamp1, timestamp2);
select u.Name,
convert(varchar(8), t.Seconds / 3600) + ':'
+ right('0', convert(varchar(2) t.Seconds % 3600/60), 2) + ':'
+ right('0', convert(varchar(2) t.Seconds % 60), 2)
as [Total Time]
from #temp t left join Users u
on t.UserID = u.UserID;
示例时间戳为2016-04-01 00:00:00.000
的位置。我现在想要的是看到我在网站上花费的总时间,而不是1个范围,而是一系列范围,例如:
2016-01-01 to 2016-01-15
2016-01-16 to 2016-01-31
2016-02-01 to 2016-02-15
是否可以将我的代码置于动态查询中,以便每次都运行相同的代码来计算所有这些范围?
上面代码的输出是:
Name [Total Time]
--------------------
Anton 6:34:55
Bert 5:22:14
我想要的是输出,例如
Name [Period_1] [Period_2] [Period_3] [Period_4]
---------------------------------------------------
Anton 6:34:55 5:00:22 null 10:44:32
Bert 5:22:14 null null 9:22:53
因此,每个范围或代码循环都应该是一列。
我相信pivot()
会在这里帮助我,但是我会非常感谢任何用动态SQL(或任何更好的解决方案)启动我的帮助。
答案 0 :(得分:0)
将当前代码包装到带参数的过程中,例如:
CREATE PROCEUDRE dbo.CalcTime
@Period varchar(100) -- Name of the period
,@PeriodStart datetime -- Period starts
,@PeriodEnd datetime -- Period ends
并使用适当的数据类型。
接下来,创建第二个过程。在这一个中,定义另一个临时表,如
CREATE TABLE #Results
(
Name varchar(100) not null -- Or however long it might get
,Period varchar(100) not null -- Ditto
,TotalTime int null -- *
)
遍历您希望为其定义数据的每个时段。对于每个时期,请调用" CalcTime"存储过程,并将结果转储到临时表中。有两种方法,使用
INSERT #Results
execute dbo.CalcTime 'Period', 'Jan 1, 2016', 'Jan 15, 2016'
或者,在调用过程中定义了临时表后,您可以在标准INSERT... SELECT...
语句中的被调用过程中引用它。
同样在循环内,构建一个逗号分隔的字符串,列出所有句点标签,例如
SET @AllPeriodLabels = isnull(@AllPeriodLabels + ',', '') + @ThisPeriodLabel
,或者
SET @AllPeriodLabels = isnull(@AllPeriodLabels + ',', '') + '[' + @ThisPeriodLabel + ']' -- **
使用它来针对临时表构建动态SQL pivot语句,您就完成了。正如评论中所提到的,有关于如何做到这一点的SO帖子,这里有两个链接:first讨论了构建动态数据透视表语句,second使用了类似的策略一个不透明的声明。
*避免在对象名称中嵌入空格,它们只会给你带来痛苦。
**好的,有时你必须这样做。
答案 1 :(得分:0)
两个伪表:
persons:
personId int
lastname nvarchar(50)
visits:
personid int
created datetime
duration int -- (store things like duration in seconds)
首先列出列,这里我使用了创建的列并将其转换为月份。结果如下:[201501],[201502],[201503],....
declare @cols nvarchar(max)
set @cols = STUFF((select ',' + quotename(convert(VARCHAR(6), created, 112))
from visits
group by convert(VARCHAR(6), created, 112)
order by convert(VARCHAR(6), created, 112)
for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '')
我需要动态SQL来填充可变数量的COL,我建议你从NON动态SQL开始,让它动态应该是最后一步。
declare @sql nvarchar(max)
set @sql = N'
select *
-- lazy create a temp so you don't have to bother about the column definitions
-- into #temp
from (
select p.lastname, convert(VARCHAR(6), created, 112) as period
-- this is optional to get a Grand Row total
-- ,(select sum(duration) from visits v where v.personId = p.personId) as total
from visits v
inner join persons p on v.personId = p.personId
) src
pivot (
sum(duration) for period in (' + @cols + ')
) pvt;
'
您可以将其打印出来进行验证或运行...
exec sp_executesql @sql
您可以通过将结果转储到临时表(即时创建)来进行扭曲。这创造了为输出添加额外列的机会,例如组织等等。
alter table #temp add organization nvarchar(100)
祝你好运!
答案 2 :(得分:0)
这是一个有效的测试代码。根据需要调整它。
设定:
-- create test tables
CREATE TABLE Users
(
UserId INT,
UserName NVARCHAR(max)
)
CREATE TABLE Access
(
UserId INT,
StartTime DATETIME2,
EndTime DATETIME2
)
CREATE TABLE Periods
(
NAME NVARCHAR(max),
StartTime DATETIME2,
EndTime DATETIME2
)
go
-- function to format the time
CREATE FUNCTION ToTime(@SECONDS BIGINT)
returns NVARCHAR(max)
AS
BEGIN
RETURN CONVERT(VARCHAR(8), @SECONDS / 3600) + ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), @SECONDS % 3600/60), 2)
+ ':'
+ RIGHT('00'+CONVERT(VARCHAR(2), @SECONDS % 60), 2)
END
go
-- populate values
INSERT INTO Users
VALUES (1, 'Anton'),
(2,'Bert')
DECLARE @I INT=100
DECLARE @D1 DATETIME2
DECLARE @D2 DATETIME2
WHILE ( @I > 0 )
BEGIN
SET @D1=Dateadd(second, Rand() * 8640000, Getdate())
SET @D2=Dateadd(second, Rand() * 1000, @D1)
INSERT INTO Access
VALUES (Floor(Rand() * 2 + 1), @D1, @D2);
SET @I=@I - 1
END
SET @I=1
SET @D1=Getdate()
WHILE ( @I < 6 )
BEGIN
SET @D2=Dateadd(day, 15, @D1)
INSERT INTO Periods
VALUES (Concat('Period_', @I),
@D1,
@D2);
SET @D1=@D2
SET @I=@I + 1
END
go
工作代码:
-- Getting the values
DECLARE @COLS NVARCHAR(max)
SET @COLS = Stuff((SELECT ',' + Quotename(NAME)
FROM Periods
GROUP BY NAME
ORDER BY NAME
FOR xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, ''
)
DECLARE @SQL NVARCHAR(max)
SET @SQL = N'SELECT * FROM
(
SELECT u.UserName,
p.Name AS Period,
dbo.Totime(Sum(Datediff(SECOND,a.StartTime,a.EndTime))) AS [Time]
FROM Access a
INNER JOIN Users u
ON a.UserId=u.UserId
INNER JOIN Periods p
ON p.StartTime<=a.StartTime
AND a.StartTime<p.EndTime
GROUP BY u.UserName,
p.Name ) x PIVOT ( Max([Time]) FOR Period IN (' + @COLS +')
) p;'
--PRINT @SQL
EXECUTE(@SQL)