【问题标题】:Query to get count of Rows within a date range stored in records查询以获取存储在记录中的日期范围内的行数
【发布时间】:2021-07-30 01:20:09
【问题描述】:

我正在尝试获取具有相同 FKID 并且在相同日期范围内(在开始和结束之间)的记录数 例如: FK ID 有 2 行在给定的日期范围内:ID 210 介于 10:15-11:32 之间,ID 213 介于 10:30 和 11:47 之间(它们也在同一天)。我需要一个查询来告诉我哪些 ID 具有相同的 FKID 并且在相同的范围内。我已经为此工作了一段时间,但无法弄清楚!谢谢!

ID FKID Start End TotalTime
208 40 2021-07-29 09:30:00.000 2021-07-29 09:30:00.000 0.9
209 37 2021-07-29 10:00:00.000 2021-07-29 10:00:00.000 0.9
210 39 2021-07-29 10:15:00.000 2021-07-29 11:32:00.000 77.76
211 40 2021-07-29 13:00:00.000 2021-07-29 14:17:00.000 77.76
212 34 2021-07-29 13:00:00.000 2021-07-29 14:28:00.000 88.8685714285714
213 39 2021-07-29 10:30:00.000 2021-07-29 11:47:00.000 77.76

【问题讨论】:

  • 赞成只是因为在您的第一个问题上获得正确的表格格式,而不是使用图像。

标签: sql sql-server


【解决方案1】:

我们首先需要做一个自连接来获取表中所有具有相同FKID的行:

SELECT  *
FROM    T AS t1
        INNER JOIN T AS t2
            ON t2.FKID = t1.FKID -- SAME FKID
            AND t2.ID <> t1.ID -- Different ID;

条件t2.ID &lt;&gt; t1.ID 将确保您不会将同一行连接到自身,而是在同一个表中查找其他行。这将返回所有行组合,例如

ID1 ID2
210 213
213 210

如果您只想要每个元组一次,您可以更改为 t2 &gt; t1.ID,它只会返回

ID1 ID2
210 213

那么对于重叠范围,逻辑的详细版本是:

  • 第 2 行的开始日期在第 1 行的开始之后和第 1 行的结束日期之前和结束日期
  • 第 2 行的结束日期在第 1 行的开始之后和第 1 行的结束日期之前

所以用 SQL 术语来说,这就变成了:

CREATE TABLE #T (ID INT, FKID INT, Start DATETIME, [End] DATETIME, TotalTime INT);

INSERT INTO #T(ID, FKID, Start, [End], TotalTime)
VALUES
    (208, 40, '2021-07-29 09:30:00', '2021-07-29 09:30:00', 0.9),
    (209, 37, '2021-07-29 10:00:00', '2021-07-29 10:00:00', 0.9),
    (210, 39, '2021-07-29 10:15:00', '2021-07-29 11:32:00', 77.76),
    (211, 40, '2021-07-29 13:00:00', '2021-07-29 14:17:00', 77.76),
    (212, 34, '2021-07-29 13:00:00', '2021-07-29 14:28:00', 88.8685714285714),
    (213, 39, '2021-07-29 10:30:00', '2021-07-29 11:47:00', 77.76);

SELECT  t1.*
FROM    #T AS t1
        INNER JOIN #T AS t2
            ON t2.FKID = t1.FKID -- SAME FKID
            AND t2.ID <> t1.ID -- Different ID 
WHERE   (t2.Start > t1.Start AND t2.Start < t1.[End])
OR      (t2.[End] > t1.Start AND t2.[End] < t1.[End]);

不过,我们可以使用 De Morgan's laws 将其简化为:

SELECT  t1.*
FROM    #T AS t1
        INNER JOIN #T AS t2
            ON t2.FKID = t1.FKID -- SAME FKID
            AND t2.ID <> t1.ID -- Different ID
WHERE   t2.Start < t1.[End]
AND     t2.[End] > t1.Start;

在这两种情况下,输出都是:

ID FKID Start End TotalTime
210 39 2021-07-29 10:15:00.000 2021-07-29 11:32:00.000 77.76
213 39 2021-07-29 10:30:00.000 2021-07-29 11:47:00.000 77.76

Example on db<>fiddle


如果您想包含开始日期等于前一行结束日期的行,只需将&lt; 更改为&lt;= 并将&gt; 更改为&gt;=

SELECT  t1.*
FROM    #T AS t1
        INNER JOIN #T AS t2
            ON t2.FKID = t1.FKID -- SAME FKID
            AND t2.ID <> t1.ID -- Different ID
WHERE   t2.Start <= t1.[End]
AND     t2.[End] >= t1.Start;

Example on db<>fiddle 记下 ID 为 214 和 215 的行

【讨论】:

  • 我删除的唯一一行是“AND t2.ID t1.ID -- 不同的 ID”,这对我有用 =)
  • 其实我才意识到这并没有考虑到时间同时开始的情况。我以为这是一个简单的改变,但事实并非如此。正在调查并将分享详细信息,但如果您知道如何,请分享 =)
  • 应该只是将&gt;更改为&gt;=&lt;更改为&lt;=的情况。我在 [this dbfiddle](*dbfiddle.uk/…) 中添加了几行额外的行(ID 214 和 215,其中 215 开始同时 214 结束)并且随着更改它们也被返回
  • 我有一个错字。这做到了! =) 谢谢!
【解决方案2】:

您可以将表与自身 JOIN,查找范围重叠 (StartA = StartB) 的相同 FKID 的记录

这会返回重叠的 ID:

select A.ID as ID1, B.ID as ID2
from MyTable as A
     inner join MyTable as B on A.FKID = B.FKID and 
                                A.Start <= B.End and 
                                A.End >= B.Start

虽然此查询只返回重叠行数。

select count(*) as Overlaps
from MyTable as A
     inner join MyTable as B on A.FKID = B.FKID and 
                                A.Start <= B.End and 
                                A.End >= B.Start

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-02
    • 2017-11-21
    • 2014-08-22
    相关资源
    最近更新 更多