1

我有一个 sql 查询来检查表 PRODUCTS 中产品记录的重叠。在大多数情况下,查询工作正常,但以下情况除外。

select * from products where 
product_reg_no = 'AL-NAPT' 
and (to_date('14-Aug-2001') BETWEEN to_date('27-Aug-2001') AND to_date('30-Aug-2001')
or to_date('31-Aug-2001') BETWEEN to_date('27-Aug-2001') AND to_date('30-Aug-2001'))

如何使此查询捕获所有记录部分或完全重叠?

如果需要,我可以提供带有示例记录的表结构。

谢谢

更新 1

我在此处或如下添加了表结构和记录:

create table products
(product_reg_no varchar2(32),
 start_date date,
 end_date date);


Insert into products
   (product_reg_no, START_DATE, END_DATE)
 Values
   ('AL-NAPT', TO_DATE('08/14/2012', 'MM/DD/YYYY'), TO_DATE('08/31/2012', 'MM/DD/YYYY'));
Insert into products
   (product_reg_no, START_DATE, END_DATE)
 Values
   ('AL-NAPT', TO_DATE('08/27/2012', 'MM/DD/YYYY'), TO_DATE('08/30/2012', 'MM/DD/YYYY'));
COMMIT;

从2012 年 8月 14 日到 2012 年8月 31 日的第一个记录与从 2012 年 8月 27 日到 2012 年8月 30 日的第二个记录重叠。那么如何修改我的查询以获得重叠?

4

2 回答 2

2

这是一个奇怪的查询。您检查 2001 年 8 月 14 日是否介于 2001 年 8 月 27 日和 2001 年 8 月 30 日之间,这总是错误的,或者 2001 年 8 月 31 日在 2001 年 8 月 27 日和 2001 年 8 月 30 日之间,这也总是错误的。所以你的where条款永远是错误的。

编辑:感谢您的澄清

SQL 小提琴演示

select   p1.product_reg_no
       , p1.start_date p1s
       , p1.end_date   p1e
       , p2.start_date p2s
       , p2.end_date   p2e
from products p1, products p2
where p1.product_reg_no = p2.product_reg_no
  and not (    p1.end_date   < p2.start_date
           and p1.start_date > p2.end_date   );

您想要的是以下场景(1 代表第一行 2 代表第二行)

1    1
 2  2

 1  1
2    2

1    1
2    2

1   1
 2   2

 1   1
2   2

你也可以转身说你不想要这个:

1 1
   2 2

   1 1
2 2

我以为你也想要这个

1 1
  2 2

  1 1
2 2

WHERE条款也可以写成不同的

not (    p1.end_date   < p2.start_date and p1.start_date > p2.end_date   )

是相同的

        p1.end_date   >= p2.start_date or p1.start_date <= p2.end_date   

我想当我很久以前在学校时,它被称为德摩根定律。

您可能必须考虑如果您有超过 2 行会发生什么。

于 2012-10-01T05:19:02.010 回答
2

请参阅确定两个日期范围是否重叠

您可能需要使用<=而不是评估以下内容或次要变体<

Start1 < End2 AND Start2 < End1

由于您使用的是单个表,因此您需要有一个自联接:

SELECT p1.*, p2.*
  FROM products p1
  JOIN products p2
    ON p1.product_reg_no != p2.product_reg_no
   AND p1.start < p2.end
   AND p2.start < p1.end;

不相等条件确保您不会获得与其自身配对的记录(尽管<条件也确保了这一点,但如果您使用<=,则不相等条件将是一个好主意。

这将为每对产品生成两行(一行 ProductA asp1和 ProductB as p2,另一行 ProductB asp1和 ProductA as p2)。为防止这种情况发生,请将 更改!=<>


而且,更仔细地查看示例数据,您可能对注册号匹配且日期重叠的行非常感兴趣。在这种情况下,您可以忽略我对!=and <or的喋喋不休,毕竟>将条件替换为。=

SELECT p1.*, p2.*
  FROM products p1
  JOIN products p2
    ON p1.product_reg_no = p2.product_reg_no
   AND p1.start < p2.end
   AND p2.start < p1.end;

SQL Fiddle(未保存)表明这有效:

SELECT p1.product_reg_no p1_reg, p1.start_date p1_start, p1.end_date p1_end,
       p2.product_reg_no p2_reg, p2.start_date p2_start, p2.end_date p2_end
  FROM products p1
  JOIN products p2
    ON p1.product_reg_no = p2.product_reg_no
   AND p1.start_date < p2.end_date
   AND p2.start_date < p1.end_date
 WHERE (p1.start_date != p2.start_date OR p1.end_date != p2.end_date);

WHERE 子句消除了连接到自身的行。删除 SELECT 列表中的重复列名后,您可以看到所有数据。我加了一行:

INSERT INTO products (product_reg_no, start_date, end_date)
VALUES ('AL-NAPT', TO_DATE('08/27/2011', 'MM/DD/YYYY'), TO_DATE('08/30/2011', 'MM/DD/YYYY'));

这没有被选中——表明它确实拒绝了不重叠的条目。

如果要消除双行,则必须添加另一个花哨的标准:

SELECT p1.product_reg_no p1_reg, p1.start_date p1_start, p1.end_date p1_end,
       p2.product_reg_no p2_reg, p2.start_date p2_start, p2.end_date p2_end
  FROM products p1
  JOIN products p2
    ON p1.product_reg_no = p2.product_reg_no
   AND p1.start_date < p2.end_date
   AND p2.start_date < p1.end_date
 WHERE (p1.start_date != p2.start_date OR p1.end_date != p2.end_date)
   AND (p1.start_date < p2.start_date OR
       (p1.start_date = p2.start_date AND p1.end_date < p2.end_date));
于 2012-10-01T06:16:49.563 回答