Sql 如何在不同的时间范围内循环运行自定义函数和查询?

Sql 如何在不同的时间范围内循环运行自定义函数和查询?,sql,sql-server,Sql,Sql Server,我正在编写一个函数来计算用户在我的网站上在线的总秒数。之后,我将秒数转换为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) +

我正在编写一个函数来计算用户在我的网站上在线的总秒数。之后,我将秒数转换为
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
。我现在想看到的是我在网站上花费的总时间,不是一个范围,而是一系列范围,例如:

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(或任何更好的解决方案),我将不胜感激。

请将您当前的代码包装到带有参数的过程中,例如:

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”存储过程,并将结果转储到temp表中。有两种方法可以做到这一点,使用

INSERT #Results
 execute dbo.CalcTime  'Period', 'Jan 1, 2016', 'Jan 15, 2016'
或者,在调用过程中定义了临时表之后,您可以在标准的
INSERT。。。选择…
语句

同样在循环中,构建一个逗号分隔的字符串,列出所有句号标签,例如

SET @AllPeriodLabels = isnull(@AllPeriodLabels + ',', '') + @ThisPeriodLabel
或者

使用此命令针对临时表构建动态SQL pivot语句,就完成了。正如在评论中提到的,有很多关于如何实现这一点的SO帖子,这里有两个链接:讨论如何构建动态透视语句,以及对unpivot语句使用类似的策略


*避免在对象名称中嵌入空格,它们只会给你带来痛苦

**好的,有时您必须这样做。

两个伪表:

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数,我建议您从非动态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)

祝你好运

这是一个有效的测试代码。根据您的需要进行调整

设置:

-- 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 
——创建测试表
创建表用户
( 
UserId INT,
用户名NVARCHAR(最大值)
) 
创建表访问
( 
UserId INT,
开始时间日期时间2,
结束时间日期时间2
) 
创建表格期间
( 
名称NVARCHAR(最大值),
开始时间日期时间2,
结束时间日期时间2
) 
去
--函数设置时间格式
创建函数ToTime(@SECONDS BIGINT)
返回NVARCHAR(最大值)
作为
开始
返回转换(VARCHAR(8),@SECONDS/3600)+':'
+右('00'+转换(VARCHAR(2),@SECONDS%3600/60),2)
+ ':' 
+右('00'+转换(VARCHAR(2),@SECONDS%60),2)
结束
去
--填充值
插入到用户中
值(1,'安东'),
(2,“伯特”)
声明@I INT=100
声明@D1 DATETIME2
声明@D2 DATETIME2
而(@I>0)
开始
SET@D1=Dateadd(第二个,Rand()*8640000,Getdate())
SET@D2=Dateadd(秒,Rand()*1000,@D1)
插入访问
数值(下限(Rand()*2+1)、@D1、@D2);
设置@I=@I-1
结束
设置@I=1
SET@D1=Getdate()
而(@I<6)
开始
SET@D2=Dateadd(第15天,第1天)
插入句号
值(Concat('Period_u',@I),
@D1,
@D2);
设置为@D1=@D2
设置@I=@I+1
结束
去
工作代码:

-- 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) 
——获取值
声明@COLS NVARCHAR(最大值)
SET@COLS=Stuff((选择“,”+Quotename(名称)
从时期
按名称分组
点名
对于xml路径(“”),键入.value(“”、'nvarchar(max)')、1、1、'
) 
声明@SQL NVARCHAR(最大值)
设置@SQL=N'SELECT*FROM
( 
选择u.UserName,
p、 以句号命名,
dbo.Totime(Sum(Datediff(SECOND,a.StartTime,a.EndTime))作为[时间]
从访问a
内部连接用户u
在a.UserId=u.UserId上
内连接周期

在p.starttimehmm上--您愿意使用逗号分隔的列表吗--这更简单,而且您不需要动态sql。您的范围是如何定义的?这听起来像是一个动态的轴心。在这里已经被询问和回答了数百次。请根据这些数据附上示例数据和所需的输出。@Pr0no,这是您的结果r函数不足以提供答案。请为该表提供用户联机信息的DDL。需要该表中的日期将联机花费的时间划分为日期范围。如果有更多示例数据,我(或其他人)可以提供更详细的答案。
-- 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)