2016-04-08 53 views
6

如果我想知道每個用戶,他們花了多少時間在Intranet的某一天,我可以使用自定義功能 - 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表運行自定義功能usertime每個date和結果存儲在一個臨時表中,如下:

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

可能出現[SQL Server動態PIVOT查詢?]的重複(http://stackoverflow.com/questions/10404348/sql-server-dynamic-pivot-query)。這已被問及答覆數百次和數百次。 –

+0

[dbo]。[usertime]('2016-04-08')必須收到userid。不是嗎? –

+0

@RuslanK。 - 不,它不能。唯一的輸入是日期。該函數檢索當天首先登錄的用戶的不同列表,然後從另一個表中檢索在該網站上花費的總時間:) – Pr0no

回答

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 

如果您需要旋轉這些結果,那麼您也可以這樣做。

+0

不錯!然而,根據帖子,'日期'是一個永久性表格,而不是臨時表格。 –

1

這看起來像你需要一個遊標來調用你的函數與從[日期]表中讀取的值。 你可以開始:

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,'''[email protected]+''',totaltime from [dbo].[usertime]('''[email protected]+''')' 
    --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 

在這之後,你可以,如果你認爲表中添加一個支點想要它樞軸轉動

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 
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 ('[email protected]+') 
) 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) 
2

當湯姆^ 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)