MySQL - 如何 select 'DISTINCT' 重叠期间(日期或数字范围)

MySQL - How to select 'DISTINCT' overlapping periods (dates or number ranges)

简而言之,如果查询告诉我 A 与 B 重叠,那么我不需要它也告诉我 B 也与 A 重叠,因为它们彼此重叠。

所以我尝试在 sql 中使用自连接到 select 只是 'DISTINCT' 重叠。

为了说明,这是我写的一个简单的 SQL fiddle 来显示包容性重叠 selection (http://sqlfiddle.com/#!9/7af84f/1)

详细...

假设我有一个 table 名称 (char), d1 (int), d2 (int) ,其架构如下。这里 d1 和 d2 代表某个区间的开始和结束,这些区间可能与同一 table,.

中的另一个区间重叠
CREATE TABLE test (
  letter char ,
  d1 int ,
  d2 int  
) ;

鉴于此 table 我用一些值填充它

INSERT INTO test (letter,d1,d2)
VALUES
   ('A',  2, 10),    -- overlaps C and D
   ('B', 12, 20),    -- overlaps E
   ('C',  5, 10),    -- overlaps A and D
   ('D',  1,  8),    -- overlaps A and C 
   ('E', 13, 15),    -- overlaps B
   ('F', 25, 30);    -- doesn't overlap anything

和 运行 以下查询使用自联接正确查找其中一行中的 d1 和 d2 与其他行中的 d1 和 d2 具有包含性重叠的行。

-- selects all records that overlap in the range d1 - d2 inclusive
-- (excluding the implicit overlap between a record and itself)
-- The results are sorted by letter followed by d1

SELECT
  basetable.letter as test_letter,
  basetable.d1,
  basetable.d2,
  overlaptable.letter as overlap_letter,
  overlaptable.d1 as overlap_d1,
  overlaptable.d2 as overlap_d2

FROM
  test as basetable, 
  test as overlaptable
WHERE
  -- there is an inclusive overlap
  basetable.d1 <= overlaptable.d2 and basetable.d2 >= overlaptable.d1
AND
  -- the row being checked is not itsself
    basetable.letter <> overlaptable.letter
    AND
    basetable.d1 <> overlaptable.d1
    AND 
    basetable.d2 <> overlaptable.d2
ORDER BY 
  basetable.letter,
  basetable.d1

这正确地给出了以下内容,显示了重叠的所有 6 个版本,例如左手列表示 A 与 C 重叠,另一行显示 C 与 A 重叠(请注意 sqlfiddle 不似乎不理解字段别名,所以我的专栏 headers 不同)

test_letter     d1     d2   overlap_letter  overlap_d1  overlap_d2
  A              2     10         D              1         8
  B             12     20         E             13        15
  C              5     10         D              1         8
  D              1      8         A              2        10
  D              1      8         C              5        10
  E             13     15         B             12        20

我的问题是:

如何更改 sql 以仅获得四行 'DISTINCT' 或 'one way' 重叠?

即这个结果...

test_letter  d1     d2  overlap_letter  overlap_d1  overlap_d2 
    A         2     10        D            1           8
    A         2     10        C            5          10
    B        12     20        E           13          15
    C         5     10        D            1           8

例如:
根据以下推理

,结果仅在左侧列中显示 A、B 和 C 的记录

你可以改成不等式。而且,你还应该使用 JOIN:

SELECT basetable.letter as test_letter, basetable.d1, basetable.d2,
       overlaptable.letter as overlap_letter, overlaptable.d1 as overlap_d1, overlaptable.d2 as overlap_d2
FROM test basetable JOIN
     test overlaptable
     ON basetable.d1 <= overlaptable.d2 AND
        basetable.d2 >= overlaptable.d1
WHERE basetable.letter < overlaptable.letter  -- This is the change
ORDER BY basetable.letter, basetable.d1;

这可以像已经建议的 PK 顺序一样简单。或者,您可能希望引入某种词典顺序。

CREATE TABLE test (
  letter char ,
  d1 int ,
  d2 int  
) ;

INSERT INTO test (letter,d1,d2)
VALUES
   ('A',  2, 10),    -- overlaps C and D
   ('B', 12, 20),    -- overlaps E
   ('C',  5, 10),    -- overlaps A and D
   ('D',  1,  8),    -- overlaps A and C 
   ('E', 13, 15),    -- overlaps B
   ('F', 25, 30),    -- doesn't overlap anything
   ('G', 50, 60),    -- a set of equal intervals
   ('H', 50, 60),
   ('I', 50, 60)


SELECT
  basetable.letter as test_letter,
  basetable.d1,
  basetable.d2,
  overlaptable.letter as overlap_letter,
  overlaptable.d1 as overlap_d1,
  overlaptable.d2 as overlap_d2

FROM
  test as basetable, 
  test as overlaptable
WHERE
  -- there is an inclusive overlap
  basetable.d1 <= overlaptable.d2 and basetable.d2 >= overlaptable.d1
AND
  -- require lexicographic order: basetable starts later / finishes earlier / its letter is less then overlaptable
  basetable.d1 > overlaptable.d1 OR (basetable.d1 = overlaptable.d1 
                                     AND (basetable.d2 < overlaptable.d2 OR (basetable.d2 = overlaptable.d2 
                                                                             AND basetable.letter < overlaptable.letter)))
ORDER BY 
  overlaptable.d1, 
  basetable.d2,
  basetable.letter