如果我想知道每个用户在某一天他们在内联网上花了多少时间,我可以使用自定义功能 - 2个例子:
select * from [dbo].[usertime]('2016-04-08')
userid totaltime
-----------------
1 4430
2 11043
5 13045
select * from [dbo].[usertime]('2016-04-09')
userid totaltime
-----------------
1 345
3 12066
9 15344
我无法控制该功能,只能使用其输出。 totaltime
只需几秒钟。
从另一张表中,我可以选择一年中的日期:
select * from dates;
date
----------
2016-01-01
...
2016-04-08
2016-04-09
我想为usertime
表中的每个date
运行自定义函数dates
,并将结果存储在临时表中,如下所示:
userid 2016-01-01 .. 2016-04-08 2016-04-09
----------------------------------------------
1 .. 4430 345
2 .. 11043 0
3 .. 0 12066
5 .. 13045 0
9 .. 0 15344
这需要我在循环中调用usertime
,伪:
create table #usertime
(
userid int
date date
seconds int
)
select * into #dates from dates;
foreach (#dates as _date)
update #usertime with [dbo].[usertime](_date)
select * from #usertime
userid 2016-01-01 .. 2016-04-08 2016-04-09
----------------------------------------------
1 .. 4430 345
2 .. 11043 0
3 .. 0 12066
5 .. 13045 0
9 .. 0 15344
我知道我需要动态SQL来每次循环使用不同的日期并stuff()
从结果集中的行创建多个列来自#usertime
。但我不明白如何使用这些功能。有人可以帮助我吗?
答案 0 :(得分:13)
不需要任何循环(在SQL中几乎总是应该避免的)。
SELECT
T.userid,
D._date,
T.totaltime
FROM
#dates D -- Probably no need for a temporary table either...
CROSS APPLY dbo.usertime(D._date) T
如果您需要转动这些结果,那么您也可以这样做。
答案 1 :(得分:3)
由于临时表范围的限制,使用永久表作为动态表结构更容易。如果由于某种原因必须使用#usertime临时表,则需要嵌套动态SQL,这非常难看。
以下是如何动态地将结果从行转移到列的示例。
SET NOCOUNT ON;
IF OBJECT_ID(N'dbo.TempUserTime', 'U') IS NOT NULL
DROP TABLE dbo.TempUserTime;
IF OBJECT_ID(N'tempdb..#UnpivitedUserTime', 'U') IS NOT NULL
DROP TABLE #UnpivitedUserTime;
--load temp table with unpivoted data
SELECT date, userid, totaltime
INTO #UnpivitedUserTime
FROM dates
CROSS APPLY dbo.userTime(date)
WHERE date BETWEEN '2016-01-01' AND '2016-04-09';
--create pivot table structure with userid and one column per date
DECLARE @SQL nvarchar(MAX) = 'CREATE TABLE dbo.TempUserTime(userid int NOT NULL';
SELECT @SQL += ',' + QUOTENAME(CONVERT(char(10), date, 121)) + ' int NULL'
FROM dates
WHERE date BETWEEN '2016-01-01' AND '2016-04-09';
SELECT @SQL += ');'
EXEC(@SQL);
--insert a row into pivot table for each user
INSERT INTO dbo.TempUserTime (userid)
SELECT DISTINCT userid FROM #UnpivitedUserTime;
--generate an update statement for each date to update all users
SET @SQL = N'';
SELECT @SQL += N'UPDATE dbo.TempUserTime
SET ' + QUOTENAME(CONVERT(char(10), date, 121)) + N' = (
SELECT totaltime
FROM #UnpivitedUserTime AS u
WHERE
u.date = ''' + + CONVERT(char(10), date, 121) + + N'''
AND u.userid = TempUserTime.userid
);
'
FROM dates
CROSS APPLY dbo.userTime(date)
WHERE date BETWEEN '2016-01-01' AND '2016-04-09';
--execute update batch
EXEC(@SQL);
--return results
SELECT *
FROM dbo.TempUserTime
ORDER BY userid;
IF OBJECT_ID(N'dbo.TempUserTime', 'U') IS NOT NULL
DROP TABLE dbo.TempUserTime;
IF OBJECT_ID(N'tempdb..#UnpivitedUserTime', 'U') IS NOT NULL
DROP TABLE #UnpivitedUserTime;
GO
答案 2 :(得分:2)
正如Tom H所说,你应该避免循环,你应该能够通过交叉申请来做到这一点。动态SQL是根据日期表中的内容构建列。
DECLARE @SearchList varchar(1000)
DECLARE @sql varchar(MAX)
SELECT @SearchList = COALESCE(@SearchList, '') + ',[' + CAST([date] AS VARCHAR(100)) + ']' FROM dates
select @SearchList
SET @sql = 'SELECT userid' + @SearchList +'
FROM
(SELECT d.[date], U.userid, U.totaltime FROM dates d
CROSS APPLY [dbo].[usertime](d.[date]) U) AS t
PIVOT
(
SUM(seconds)
FOR [date] IN (' + RIGHT(@SearchList, LEN(@SearchList)-1) + ')
) AS pvt'
EXEC(@sql)
答案 3 :(得分:1)
这看起来你需要一个游标来调用你的函数,其中包含从[dates]表中读取的值。 你可以从:
开始CREATE TABLE #usertime
(
userid int
,date date
,seconds int
)
DECLARE @date nvarchar(16)
DECLARE @sql nvarchar(max)
DECLARE curs CURSOR FOR SELECT * FROM dates
OPEN curs
FETCH NEXT FROM curs INTO @date
WHILE @@FETCH_STATUS = 0
BEGIN
SET @sql = 'INSERT INTO #usertime SELECT userid,'''+@date+''',totaltime from [dbo].[usertime]('''+@date+''')'
--print @sql
exec (@sql)
FETCH NEXT FROM curs INTO @date
END
CLOSE curs
DEALLOCATE curs
SELECT * FROM #usertime
这应该返回(除非我的表名有语法错误)结果如:
userid date seconds
----------------------------------------------
1 2016-04-08 4430
1 2016-04-09 345
2 2016-04-08 11043
3 2016-04-09 12066
在此之后,如果您想要旋转
,可以在该表上添加一个数据透视表答案 4 :(得分:1)
希望,我理解你的问题吧!
DECLARE @userstring AS nvarchar(max),
@sql AS nvarchar(max)
CREATE TABLE #usertime (
userid int,
totaltime int,
dateof date
)
INSERT INTO #usertime VALUES
(1, 4430, '2016-04-08'),
(2, 11043, '2016-04-08'),
(5, 13045, '2016-04-08'),
(1, 345, '2016-04-09'),
(3, 12066, '2016-04-09'),
(9, 15344, '2016-04-09')
SELECT @userstring = stuff((select distinct ',' + quotename(dateof) from #usertime for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '');
SELECT @sql = '
select *
from (select userid,
totaltime,
dateof
from #usertime) src
pivot (SUM(totaltime) for [dateof] in ('+@userstring+')
) pvt'
EXECUTE(@sql)
DROP TABLE #usertime
输出:
userid 2016-04-08 2016-04-09
----------- ----------- -----------
1 4430 345
2 11043 NULL
3 NULL 12066
5 13045 NULL
9 NULL 15344
(5 row(s) affected)