SQL根据date from DateTime字段而不是时间选择日期的第一个唯一条目

SQL根据date from DateTime字段而不是时间选择日期的第一个唯一条目,sql,datetime,unique,Sql,Datetime,Unique,我有一个处理学生打卡的数据库。所以每个出现在数据库中的学生每天应该只有一个条目。。。但是人们都很愚蠢,所以我需要反驳。我正在使用PHP从SQLServer2005数据库中提取查询 表:TAData UserID,DateTime,DeviceID 11111,2014/01/23 07:39:03 AM,1 22222,2014/01/23 07:45:23 AM,2 33333,2014/01/23 07:51:03 AM,1 22222,2014/01/23 08:58:53 AM,3 1

我有一个处理学生打卡的数据库。所以每个出现在数据库中的学生每天应该只有一个条目。。。但是人们都很愚蠢,所以我需要反驳。我正在使用PHP从SQLServer2005数据库中提取查询

表:TAData

UserID,DateTime,DeviceID

11111,2014/01/23 07:39:03 AM,1
22222,2014/01/23 07:45:23 AM,2
33333,2014/01/23 07:51:03 AM,1
22222,2014/01/23 08:58:53 AM,3
11111,2014/01/23 09:22:34 AM,1
11111,2014/01/23 11:15:00 AM,5

11111,2014/01/24 07:49:03 AM,1
22222,2014/01/24 07:55:23 AM,2
33333,2014/01/24 08:01:03 AM,1
22222,2014/01/24 08:58:53 AM,3
11111,2014/01/24 09:22:34 AM,1
11111,2014/01/24 11:15:00 AM,5
如何计算学生迟到的天数,但只计算当天的第一个条目,以便得到:

11111-1 因为它会忽略第一天之后的第1天的所有条目,第一天的条目很早,第2天的条目很晚。 22222 - 2 两天的第一次入境都晚了 33333 - 2 两天的第一次入境都晚了

    SELECT 
        COUNT(*)
    FROM 
        TAData
    WHERE
        UserID = '" . $row["UserID"] . "'
        AND DateTime > '2014-01-15'
        AND CONVERT(varchar,DateTime,108) > '07:45'
这就是我到目前为止所做的,它计算了33333这样的学生迟到的次数,但它为每天多次打卡的人增加了额外的条目。所以11111的计数是5而不是1

我试过:

    SELECT 
        CONVERT(varchar,DateTime,103), COUNT(*)
    FROM 
        TAData
    WHERE
        UserID = '" . $row["UserID"] . "'
        AND DateTime > '2014-01-15'
        AND CONVERT(varchar,DateTime,108) BETWEEN '07:45' AND '09:45'
    GROUP BY CONVERT(varchar,DateTime,103)
但它对我来说却毫无意义(“数组([0]=>1[]=>16/01/2014[1]=>16/01/2014)”

我也尝试过使用DISTINCT,但放弃了,因为它显然不是我想要使用的


有什么建议吗?

我想你想要的是这个,假设你想要一个每用户计数(否则,从最后的select和
GROUP BY
子句中删除
UserID

declare @TAData table (UserID int not null,[DateTime] datetime not null,
                       DeviceID int not null)
insert into @TAData(UserID,[DateTime],DeviceID) values
(11111,'2010-11-04T07:39:03',1),(22222,'2010-11-04T07:45:23',2),
(33333,'2010-11-04T07:51:03',1),(22222,'2010-11-04T08:58:53',3),
(11111,'2010-11-04T09:22:34',1),(11111,'2010-11-04T14:15:00',5),
(11111,'2010-11-05T07:49:03',1),(22222,'2010-11-05T07:55:23',2),
(33333,'2010-11-05T08:01:03',1),(22222,'2010-11-05T08:58:53',3),
(11111,'2010-11-05T09:22:34',1),(11111,'2010-11-05T14:15:00',5)

;With Ordered as (
    select *,
        ROW_NUMBER() OVER (
            PARTITION BY UserID,
                DATEADD(day,DATEDIFF(day,0,[DateTime]),0)
            ORDER BY [DateTime]) as rn
    from @TAData
), Earliest as (
    select *,
        DATEADD(day,DATEDIFF(day,[DateTime],'19000101'),[DateTime]) as FixedDate
    from Ordered where rn = 1
)
select
    UserID,
    SUM(CASE WHEN FixedDate > '1900-01-01T07:45:00' THEN 1 ELSE 0 END) as Late
from
    Earliest
group by
    UserID
希望您能看到我用来创建两个
CTE
s的逻辑。
DATEADD
/
DATEDIFF
用于第一个CTE(
Ordered
)将
[DateTime]
值标准化到各自的一天午夜。我更喜欢在您的尝试中使用这种方式而不是任何形式的字符串转换

最早的
中,我们再次使用
DATEADD
/
DATEDIFF
对,这一次将值标准化为在一天的同一时间但在固定的一天(1900年1月1日)。这允许最后的
CASE
表达式与单个固定值进行比较

结果是:

UserID      Late
----------- -----------
11111       1
22222       2
33333       2
(我还必须通过将第二组六条记录的日期调整为不同的日期来修复您的样本数据。我还将时间解释为24小时,尽管存在
AM
,因为我不知道
14:15 AM
将是什么)


更多说明:

这是订购的
产生的:

UserID      DateTime                DeviceID    rn
----------- ----------------------- ----------- --------------------
11111       2010-11-04 07:39:03.000 1           1
11111       2010-11-04 09:22:34.000 1           2
11111       2010-11-04 14:15:00.000 5           3
11111       2010-11-05 07:49:03.000 1           1
11111       2010-11-05 09:22:34.000 1           2
11111       2010-11-05 14:15:00.000 5           3
22222       2010-11-04 07:45:23.000 2           1
22222       2010-11-04 08:58:53.000 3           2
22222       2010-11-05 07:55:23.000 2           1
22222       2010-11-05 08:58:53.000 3           2
33333       2010-11-04 07:51:03.000 1           1
33333       2010-11-05 08:01:03.000 1           1
UserID      DateTime                DeviceID    rn         FixedDate
----------- ----------------------- ----------- ---------- -----------------------
11111       2010-11-04 07:39:03.000 1           1          1900-01-01 07:39:03.000
11111       2010-11-05 07:49:03.000 1           1          1900-01-01 07:49:03.000
22222       2010-11-04 07:45:23.000 2           1          1900-01-01 07:45:23.000
22222       2010-11-05 07:55:23.000 2           1          1900-01-01 07:55:23.000
33333       2010-11-04 07:51:03.000 1           1          1900-01-01 07:51:03.000
33333       2010-11-05 08:01:03.000 1           1          1900-01-01 08:01:03.000
也就是说,对于每个用户,对于每一天,它为每一行分配一个distinct,其中第1行分配给当天最早的事件

这是
最早的
产生的:

UserID      DateTime                DeviceID    rn
----------- ----------------------- ----------- --------------------
11111       2010-11-04 07:39:03.000 1           1
11111       2010-11-04 09:22:34.000 1           2
11111       2010-11-04 14:15:00.000 5           3
11111       2010-11-05 07:49:03.000 1           1
11111       2010-11-05 09:22:34.000 1           2
11111       2010-11-05 14:15:00.000 5           3
22222       2010-11-04 07:45:23.000 2           1
22222       2010-11-04 08:58:53.000 3           2
22222       2010-11-05 07:55:23.000 2           1
22222       2010-11-05 08:58:53.000 3           2
33333       2010-11-04 07:51:03.000 1           1
33333       2010-11-05 08:01:03.000 1           1
UserID      DateTime                DeviceID    rn         FixedDate
----------- ----------------------- ----------- ---------- -----------------------
11111       2010-11-04 07:39:03.000 1           1          1900-01-01 07:39:03.000
11111       2010-11-05 07:49:03.000 1           1          1900-01-01 07:49:03.000
22222       2010-11-04 07:45:23.000 2           1          1900-01-01 07:45:23.000
22222       2010-11-05 07:55:23.000 2           1          1900-01-01 07:55:23.000
33333       2010-11-04 07:51:03.000 1           1          1900-01-01 07:51:03.000
33333       2010-11-05 08:01:03.000 1           1          1900-01-01 08:01:03.000
也就是说,我们从
Ordered
中选取行,其中
rn
等于1,即每个用户每天最早发生的事件。正如我前面所说,我们在
fixedate
中使用了一个技巧并生成一个值,该值具有与
DateTime
相同的时间成分,但都发生在
01/01/1900
。这将在以下位置进行最终比较:

CASE WHEN FixedDate > '1900-01-01T07:45:00' THEN 1 ELSE 0 END

易于编写-如果我们不这样做,那么我们仍然需要在特定的一天计算时间是在
07:45

之前还是之后,您可以使用您得到的数据。您在结果数组中的索引0处得到了1的计数。但它应该是7…而不是1。因此它不能正确计数。它是我的查询中的所有条目实际上只计算1…当你说SQLExpress时,你是指SQL Server Express,还是其他数据库产品?MS SQL Server 2005,抱歉是的…我的一个朋友也向我指出…我真的没想到编码会这么复杂。做这么简单的事情看起来太多了。去我需要花一些时间来弄清楚您做了什么,因为我对SQL查询不太熟悉yet@Demonwolf-我这样做的原因之一是作为一个完整的查询,使用CTEs,您可以将其放在查询窗口中并检查各个部分。例如,您可以从rdered
从最早的开始选择*
以查看它们是如何工作的。@Demonwolf-我已经尝试在我的答案底部添加更多的解释。如果仍然不清楚,请随意指出哪些位比较混乱,我将尝试添加更多。该解释帮助很大。谢谢。我现在看到了所有这些都是如何组合在一起的。Still看起来很复杂,很长,但什么有效,什么有效。谢谢你的帮助。@Demonwolf-如果你在2008年,它可能会更简单、更明显,因为我们可以将
DateTime
列在适当的时间点转换为
date
time
,而不必使用
DATEADD
tr伊克斯。