10

我是这个网站的新手,但请耐心等待。

我正在尝试GROUP BY使用 SQL Server 处理一些数据。

这是数据:

Computer    VisitDate
ComputerA   2012-04-28 09:00:00
ComputerA   2012-04-28 09:05:00
ComputerA   2012-04-28 09:10:00
ComputerB   2012-04-28 09:30:00
ComputerB   2012-04-28 09:32:00
ComputerB   2012-04-28 09:44:00
ComputerB   2012-04-28 09:56:00
ComputerB   2012-04-28 10:25:00
ComputerA   2012-04-28 12:25:00
ComputerC   2012-04-28 12:30:00
ComputerC   2012-04-28 12:35:00
ComputerC   2012-04-28 12:45:00
ComputerC   2012-04-28 12:55:00

我想要实现的是按计算机对数据进行分组,但如果计算机的访问时间超过 1 小时,也可以进行分组。这是我正在尝试做的结果:

Computer     VisitDate
ComputerA    2012-04-28 09:00:00
ComputerB    2012-04-28 09:30:00
ComputerA    2012-04-28 12:25:00
ComputerC    2012-04-28 12:30:00

所以Computer A显示了两次,因为它在 09:10:00 访问,然后在 12:25:00 再次访问,这意味着相差超过 1 小时。

“按计算机分组”很容易,但另一方面,我不知道从哪里开始。对此问题的任何帮助将不胜感激。

4

3 回答 3

5

你不能用简单的GROUP BY. 此运算符仅适用于单个列 - 例如,您可以按计算机名称或其他内容进行分组,但您不能添加其他逻辑,例如时间差必须大于一小时或类似的任何内容到分组中。

您可以做的 - 如果您使用的是 SQL Server 2005或更高版本(您没有在问题中提及该版本),则可以使用 CTE(通用表表达式)。这些提供了一种对数据进行切片的方法。

在这里,我正在做几件事——首先,我将数据“分区”ComputerName并排序,VisitDate并使用ROW_NUMBER()来获取每个分区的序列号。然后第二个 CTE 确定每台计算机的“第一个”条目 - 行号 = 1 的那个 - 第三个最终确定VisitDate每个条目与行号 = 1 的条目相比的差异。从第三个 CTE,我最终选择那些行号 = 1 的条目(每个“分区”的第一个),或者任何在 60 分钟或更多分钟内有差异的条目。

这是代码:

;WITH Computers AS
(
    SELECT
        ComputerName, VisitDate,
        RN = ROW_NUMBER() OVER(PARTITION BY ComputerName ORDER BY VisitDate)
    FROM    
        dbo.YourComputerTable
),
FirstComputers AS
(
    SELECT ComputerName, VisitDate
    FROM Computers
    WHERE RN = 1
),
SelectedComputers AS
(
    SELECT 
        c.ComputerName, c.VisitDate, c.RN,
        DiffToFirst = ABS(DATEDIFF(MINUTE, c.VisitDate, fc.VisitDate))
    FROM Computers c
    INNER JOIN FirstComputers fc ON c.ComputerName = fc.ComputerName
)
SELECT * 
FROM SelectedComputers
WHERE RN = 1 OR DiffToFirst >= 60
于 2012-04-28T14:46:18.100 回答
2

如果您已升级到 SQL Server 2012,则可以为此使用 LAG。

with Lagged as (
  select
    Computer,
    VisitDate,
    LAG(VisitDate,1) over (
      partition by Computer
      order by VisitDate
    ) as LastVisit
  from @Visit
)
  select
    Computer,
    VisitDate
  from Lagged
  where LastVisit is null
  or VisitDate > dateadd(hour,1,LastVisit);

SQL Fiddle在这里

于 2012-04-29T00:24:32.237 回答
0

CTE 显示至少有一次访问的所有计算机,或在间隔 > 60 分钟之前和之后的访问。

create table compVisits (Computer varchar(20), VisitDate datetime)
go
insert into compVisits values
('ComputerA', '2012-04-28 09:00:00')
, ('ComputerA', '2012-04-28 09:05:00')
, ('ComputerA', '2012-04-28 09:10:00')
, ('ComputerB', '2012-04-28 09:30:00')
, ('ComputerB', '2012-04-28 09:32:00')
, ('ComputerB', '2012-04-28 09:44:00')
, ('ComputerB', '2012-04-28 09:56:00')
, ('ComputerB', '2012-04-28 10:25:00')
, ('ComputerA', '2012-04-28 12:25:00')
, ('ComputerC', '2012-04-28 12:30:00')
, ('ComputerC', '2012-04-28 12:35:00')
, ('ComputerC', '2012-04-28 12:45:00')
, ('ComputerC', '2012-04-28 12:55:00')

; WITH a as ( --Initial row count
    select *, r=ROW_NUMBER()OVER(PARTITION BY Computer ORDER BY VisitDate)
    FROM compVisits
)
, b as ( -- gaps >60 minutes
    SELECT a1.Computer, a1.VisitDate
    FROM a a1
    INNER JOIN a a2 ON a1.Computer=a2.Computer AND (a1.r+1)=a2.r
    AND DATEDIFF(MINUTE,a1.VisitDate,a2.VisitDate)>60
    UNION
    SELECT a2.Computer, a2.VisitDate
    FROM a a1
    INNER JOIN a a2 ON a1.Computer=a2.Computer AND (a1.r+1)=a2.r
    AND DATEDIFF(MINUTE,a1.VisitDate,a2.VisitDate)>60
)
-- at least one visit
SELECT a1.Computer, a1.VisitDate
FROM a a1
WHERE r=1
AND NOT EXISTS(SELECT 1 FROM b WHERE Computer=a1.Computer)

UNION

-- gaps >60 minutes
SELECT * FROM b
ORDER BY VisitDate

结果:

在此处输入图像描述

于 2012-04-28T15:28:48.250 回答