繁体   English   中英

每个特定时间选择一行

[英]Select one row per specific time

我有一个看起来像这样的表:

ID  UserID  DateTime             TypeID

1     1     1/1/2010 10:00:00      1
2     2     1/1/2010 10:01:50      1
3     1     1/1/2010 10:02:50      1
4     1     1/1/2010 10:03:50      1
5     1     1/1/2010 11:00:00      1
6     2     1/1/2010 11:00:50      1

我需要查询其typeID为1的所有用户,但每15分钟只有一行

例如,结果应该是:

1     1     1/1/2010 10:00:00      1
2     2     1/1/2010 10:01:50      1
5     1     1/1/2010 11:00:00      1
6     2     1/1/2010 11:00:50      1

ID 3和4未显示,因为自特定用户ID的最后一条记录以来尚未传递15分钟。

显示ID 1和5,因为此特定用户ID已经过了15分钟,与ID 2和6相同。

我该怎么做?

谢谢

尝试这个:

select * from 
(
      select ID, UserID, 
      Max(DateTime) as UpperBound, 
      Min(DateTime) as LowerBound, 
      TypeID 
      from the_table
      where TypeID=1
      group by ID,UserID,TypeID
) t 
where datediff(mi,LowerBound,UpperBound)>=15

编辑:由于我的上述尝试错误,我正在使用不需要递归的Sql表值函数添加另一种方法,因为可以理解,这是一个很大的问题。

步骤1:创建一个表类型如下(LoginDate是Shay示例中的DateTime列 - DateTime名称与SQL数据类型冲突,我认为避免这些冲突是明智的)

CREATE TYPE [dbo].[TVP] AS TABLE(
    [ID] [int] NOT NULL,
    [UserID] [int] NOT NULL,
    [LoginDate] [datetime] NOT NULL,
    [TypeID] [int] NOT NULL
)
GO

第2步:创建以下功能:

CREATE FUNCTION [dbo].[fnGetLoginFreq] 
(
    -- notice: TVP is the type (declared above)
    @TVP TVP readonly
)
RETURNS 
@Table_Var TABLE 
(
    -- This will be our result set
    ID int, 
    UserId int,
    LoginTime datetime,
    TypeID int,
    RowNumber int
)
AS
BEGIN
    --We will insert records in this table as we go through the rows in the
    --table passed in as parameter and decide that we should add an entry because
    --15' had elapsed between logins 
    DECLARE @temp  table
    (
        ID int,
        UserId int, 
        LoginTime datetime,
        TypeID int
    )
    -- seems silly, but is not because we need to add a row_number column to help
    -- in our iteration and table-valued paramters cannot be modified inside the function
    insert into @Table_var
    select ID,UserID,Logindate,TypeID,row_number() OVER(ORDER BY UserID,LoginDate) AS [RowNumber] 
    from @TVP order by UserID asc,LoginDate desc

    declare @Index int,@End int,@CurrentLoginTime datetime, @NextLoginTime datetime, @CurrentUserID int , @NextUserID int

    select @Index=1,@End=count(*) from @Table_var

    while(@Index<=@End)
    begin        
            select @CurrentLoginTime=LoginTime,@CurrentUserID=UserID from @Table_var where RowNumber=@Index
            select @NextLoginTime=LoginTime,@NextUserID=UserID from @Table_var where RowNumber=(@Index+1)

            if(@CurrentUserID=@NextUserID)
            begin
                if( abs(DateDiff(mi,@CurrentLoginTime,@NextLoginTime))>=15)
                begin   
                    insert into @temp
                    select ID,UserID,LoginTime,TypeID
                    from @Table_var
                    where RowNumber=@Index
                end     
            END
            else 
            bEGIN
                    insert into @temp
                    select ID,UserID,LoginTime,TypeID
                    from @Table_var
                    where RowNumber=@Index and UserID=@CurrentUserID 
            END

            if(@Index=@End)--last element?
            begin
                insert into @temp
                select ID,UserID,LoginTime,TypeID
                from @Table_var
                where RowNumber=@Index and not 
                abs((select datediff(mi,@CurrentLoginTime,max(LoginTime)) from @temp where UserID=@CurrentUserID))<=14
            end

            select @Index=@Index+1
    end 

    delete  from @Table_var

    insert into @Table_var
    select ID, UserID ,LoginTime ,TypeID ,row_number() OVER(ORDER BY UserID,LoginTime) AS 'RowNumber' 
    from @temp

    return 

END

第3步:旋转一下

declare @TVP TVP

INSERT INTO @TVP
select ID,UserId,[DateType],TypeID from Shays_table where TypeID=1 --AND any other date restriction you want to add 

select * from fnGetLoginFreq(@TVP) order by LoginTime asc

我的测试返回了这个:

ID  UserId  LoginTime               TypeID  RowNumber
2   2       2010-01-01 10:01:50.000 1       3
4   1       2010-01-01 10:03:50.000 1       1
5   1       2010-01-01 11:00:00.000 1       2
6   2       2010-01-01 11:00:50.000 1       4

怎么样,这是相当直接的,并为您提供所需的结果:

SELECT ID, UserID, [DateTime], TypeID
FROM Users
WHERE Users.TypeID = 1
  AND NOT EXISTS (
    SELECT TOP 1 1 
    FROM Users AS U2 
    WHERE U2.ID <> Users.ID 
      AND U2.UserID = Users.UserID 
      AND U2.[DateTime] BETWEEN DATEADD(MI, -15, Users.[DateTime]) AND Users.[DateTime] 
      AND U2.TypeID = 1)

NOT EXISTS限制仅显示在它们之前15分钟内没有记录的记录,因此您将看到块中的第一条记录而不是每15分钟一条记录。

编辑:因为您希望每15分钟看一次,所以不使用递归:

SELECT Users.ID, Users.UserID, Users.[DateTime], Users.TypeID 
FROM
  (
    SELECT MIN(ID) AS ID, UserID, 
      DATEADD(minute, DATEDIFF(minute,0,[DateTime]) / 15 * 15, 0) AS [DateTime]
    FROM Users
    GROUP BY UserID, DATEADD(minute, DATEDIFF(minute,0,[DateTime]) / 15 * 15, 0)
  ) AS Dates
  INNER JOIN Users AS Users ON Users.ID = Dates.ID
WHERE Users.TypeID = 1
  AND NOT EXISTS (
    SELECT TOP 1 1
    FROM
      (
        SELECT MIN(ID) AS ID, UserID, 
          DATEADD(minute, DATEDIFF(minute,0,[DateTime]) / 15 * 15, 0) AS [DateTime]
        FROM Users
        GROUP BY UserID, DATEADD(minute, DATEDIFF(minute,0,[DateTime]) / 15 * 15, 0)
      ) AS Dates2
      INNER JOIN Users AS U2 ON U2.ID = Dates2.ID
    WHERE U2.ID <> Users.ID 
      AND U2.UserID = Users.UserID 
      AND U2.[DateTime] BETWEEN DATEADD(MI, -15, Users.[DateTime]) AND Users.[DateTime] 
      AND U2.TypeID = 1
  )
ORDER BY Users.DateTime

如果这不起作用,请发布更多样本数据,以便我可以看到缺少的内容。

Edit2与上面的内容相同,但现在仅使用CTE来提高可读性并帮助提高可维护性,我还将其改进为突出显示,您还可以通过限制主查询的任何DateTime范围来限制Dates表:

WITH Dates(ID, UserID, [DateTime])
AS
(
  SELECT MIN(ID) AS ID, UserID, 
    DATEADD(minute, DATEDIFF(minute,0,[DateTime]) / 15 * 15, 0) AS [DateTime]
  FROM Users
  WHERE Users.TypeID = 1 
  --AND Users.[DateTime] BETWEEN @StartDateTime AND @EndDateTime
  GROUP BY UserID, DATEADD(minute, DATEDIFF(minute,0,[DateTime]) / 15 * 15, 0)
)

SELECT Users.ID, Users.UserID, Users.[DateTime], Users.TypeID 
FROM Dates
  INNER JOIN Users ON Users.ID = Dates.ID
WHERE Users.TypeID = 1 
  --AND Users.[DateTime] BETWEEN @StartDateTime AND @EndDateTime
  AND NOT EXISTS (
    SELECT TOP 1 1
    FROM Dates AS Dates2
      INNER JOIN Users AS U2 ON U2.ID = Dates2.ID
    WHERE U2.ID <> Users.ID 
      AND U2.UserID = Users.UserID 
      AND U2.[DateTime] BETWEEN DATEADD(MI, -15, Users.[DateTime]) AND Users.[DateTime] 
      AND U2.TypeID = 1
  )
ORDER BY Users.DateTime

另外作为性能说明,每当处理可能最终会递归的事情(可能是其他答案)时,您应该立即考虑是否能够将主查询限制在日期范围内,即使它是一整年或更长的范围

您可以使用递归CTE,但如果结果集非常大,我也会评估游标,因为它可能会更有效。

我在答案中遗漏了ID栏。 如果你真的需要它,就可以添加它。 它只是使递归CTE的锚点部分更加笨拙。

DECLARE @T TABLE
(
ID INT PRIMARY KEY,
UserID INT,
[DateTime] DateTime,
TypeID INT
)
INSERT INTO @T
SELECT 1,1,'20100101 10:00:00', 1 union all
SELECT 2,2,'20100101 10:01:50', 1 union all
SELECT 3,1,'20100101 10:02:50', 1 union all
SELECT 4,1,'20100101 10:03:50', 1 union all
SELECT 5,1,'20100101 11:00:00', 1 union all
SELECT 6,2,'20100101 11:00:50', 1;


WITH RecursiveCTE
     AS (SELECT UserID,
                MIN([DateTime]) As [DateTime],
                1               AS TypeID
         FROM   @T
         WHERE  TypeID = 1
         GROUP  BY UserID
         UNION ALL
         SELECT UserID,
                [DateTime],
                TypeID
         FROM   (
                --Can't use TOP directly
                SELECT T.*,
                       rn = ROW_NUMBER() OVER (PARTITION BY T.UserID ORDER BY
                            T.[DateTime])
                 FROM   @T T
                        JOIN RecursiveCTE R
                          ON R.UserID = T.UserID
                             AND T.[DateTime] >=
                                 DATEADD(MINUTE, 15, R.[DateTime])) R
         WHERE  R.rn = 1)

暂无
暂无

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

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