繁体   English   中英

如何迭代填充临时表并将结果填充()到列中?

[英]How do I fill a temp table iteratively and stuff() the result into columns?

如果我想知道每个用户在某一天在内联网上花了多少时间,我可以使用自定义功能 - 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

我想为dates表中的每个date运行自定义函数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

这需要我在循环中调用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行创建多个列来自#usertime 但我不明白如何使用这些功能。 任何人都可以帮助我吗?

不需要任何循环(在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

如果您需要转动这些结果,那么您也可以这样做。

由于临时表范围的限制,使用永久表作为动态表结构更容易。 如果由于某种原因必须使用#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

正如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)

这看起来像你需要一个游标来调用你的函数与从[日期]表中读取的值。 你可以从:

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

在此之后,如果您想要它旋转,您可以在该表上添加一个数据透视表

希望,我理解你的问题吧!

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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM