2012-10-01 37 views
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

我已经加入表的结构和记录here或如下:

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; 

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

+0

是日期值中的一个(或几个)应该来自于数据库中的列?最好为产品表提供一个最小模式,并为其提供一些示例行。我打算建议OVERLAP操作符,但Google建议它不提供Oracle记录(尽管它存在),所以也许你不能(不应该)使用它。你可能想看看[确定两个日期范围是否重叠](http://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap/328558#328558)。 –

+0

@JonathanLeffler用[sqlfiddle](http://sqlfiddle.com/#!4/03d61/1)更新了我的问题谢谢 – user75ponic

回答

2

请参阅Determine whether two date ranges overlap

您需要评估以下,或者用它来代替<=<上一个小的变体,或许:

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; 

的不相等条件确保你没有得到与自己配对(虽然<条件也保证了创纪录的,但如果你使用<=,在不相等的条件将是一个好主意。

这将产生两行用于每个对产品(一行与产品A为p1和产品B作为p2,其他与产品B作为p1和产品A为p2)。为防止发生这种情况,请将!=更改为<>


而且,仔细查看示例数据,可能会发现在注册编号匹配和日期重叠的行中您非常有趣。在这种情况下,您可以忽略我关于!=<>的诡计,并最终用=代替条件。

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小提琴(未保存的)表明,该工程:

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)); 
+0

请参阅我的附录;对于你的示例数据,用'='替换'!='。 –

+0

当我使用真实数据进行测试时,您的查询完美无缺。我有一个问题,但我怎么修改这个接收参数,以便我可以在函数中使用此查询来检查任何重叠的记录,当用户在存储在表中之前在应用程序中输入日期。感谢和赞赏。 – user75ponic

+1

有多种选择。在某些方面,最简单的方法是创建第二个表(可能是临时表),将其称为'prospective_registration',与“产品”相同的三列。将新值插入该表中。更改查询,使'JOIN产品p2'成为'JOIN prospective_registration p2',并保持其他所有内容不变。否则,您需要用相应的参数替换每个出现的'p2.product_reg_no','p2.start_date'和'p2.end_date',并删除'JOIN products p2'行。可能还有其他一些简化可用。 –

2

这是一个奇怪的查询。您检查2001年8月14日是2001年8月27日至2001年8月30日之间是否始终是虚假的,或2001年8月31日是2001年8月27日至2001年8月30日期间是否也是虚假的。所以你的where条款将永远是错误的。

编辑:感谢澄清

SQL Fiddle Demo

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行会发生什么情况。

+0

用[sqlfiddle]更新了我的问题(http://sqlfiddle.com/#!4/03d61/1)谢谢 – user75ponic