过滤mysql表中重叠的行

时间:2015-09-01 19:20:23

标签: php mysql

我有一张这样的桌子......

id  areaId  date        fromHour tohour
 1     485  31-08-2015  10       16
 2     485  31-08-2015  17       22
 3     485  31-08-2015  13       16
 4     460  31-08-2015  15       19

我使用了这个查询:

select t.* from tableName t
 join (select areaId, date,  count(*) as NumDuplicates
 from tableName group by areaId, date having NumDuplicates > 1 ) tsum
 on t.areaId = tsum.areaId and t.date = tsum.date

...我现在收到这样的结果集......

id areaId date       fromHour tohour
 1    485 31-08-2015 10       16
 2    485 31-08-2015 17       22
 3    485 31-08-2015 13       16

但是,我希望收到像这样的结果集......

id areaId date       fromHour tohour
 1    485 31-08-2015 10       16
 3    485 31-08-2015 13       16

我现在要解释选择第1行和第3行的标准,但第2行和第4行不是......

选择第1行和第3行是因为:
它们具有相同的areaId和相同的Date,然后来自第3行的fromHour与第1行中的小时重叠。
从第3行开始,从第10行至第10行至第16行之间

未选择第2行和第4行,因为:
第2行与17到22之间的任何其他行没有重叠小时,其他行(第1行和第3行)在此时间之前。
第4行不在同一区域中,即使它具有相同的日期。

这是一个图表:

id  areaId   08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 1     485          |-----------------|
 2     485                               |--------------|
 3     485                   |--------|
 4     460                         |-----------|

结论:
我想选择具有相同areaId和date的行,只要它们有重叠的小时数(fromHour:toHour)。

3 个答案:

答案 0 :(得分:0)

考虑以下内容......

DROP TABLE IF EXISTS my_table;

CREATE TABLE my_table
(id  INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,area_id  INT NOT NULL NULL
,date DATE NOT NULL
,from_hour INT NOT NULL
,to_hour INT NOT NULL
);

INSERT INTO my_table VALUES
(1     ,485  ,'2015-08-31'  ,10      , 16),
(2     ,485  ,'2015-08-31'  ,17      , 22),
(3     ,485  ,'2015-08-31'  ,13      , 16),
(4     ,460  ,'2015-08-31'  ,15      , 19);

SELECT DISTINCT x.* 
           FROM my_table x 
           JOIN my_table y 
             ON y.area_id = x.area_id 
            AND y.date = x.date 
            AND y.from_hour < x.to_hour 
            AND y.to_hour > x.from_hour 
            AND y.id <> x.id;
+----+---------+------------+-----------+---------+
| id | area_id | date       | from_hour | to_hour |
+----+---------+------------+-----------+---------+
|  3 |     485 | 2015-08-31 |        13 |      16 |
|  1 |     485 | 2015-08-31 |        10 |      16 |
+----+---------+------------+-----------+---------+

请注意,您应该使用日期数据类型存储日期,并认真考虑存储datetime_start, datetime_end而不是date, from_hour, to_hour

答案 1 :(得分:0)

我不满意这种方法尽可能高效,但它至少可以帮助讨论。我采用的具体方法需要一个整数表(我只使用0到24),用于确定这个子查询中各行的“共同”小时:

select
    areaId, `date`, n.n
from tablename t
inner join tblNums n on n.n between t.fromHour and t.tohour
group by
    areaId, `date`, n.n
having count(*) > 1
;

该子查询的结果(来自问题中的示例)是:

| areaId |                     date |  n |
|--------|--------------------------|----|
|    485 | August, 31 2015 00:00:00 | 13 |
|    485 | August, 31 2015 00:00:00 | 14 |
|    485 | August, 31 2015 00:00:00 | 15 |
|    485 | August, 31 2015 00:00:00 | 16 |

该结果可以连接回源行(并且通过不同以避免重复)将列出共享重叠周期的行

select distinct
     t.*
from tableName t
inner join (
        select
            areaId, `date`, n.n
        from tablename t
        inner join tblNums n on n.n between t.fromHour and t.tohour
        group by
            areaId, `date`, n.n
        having count(*) > 1
       ) o on t.areaId = o.areaId and t.date = o.date
           and t.fromHour = o.n or t.tohour = o.n
 ;

| id | areaId |                     date | fromHour | tohour |
|----|--------|--------------------------|----------|--------|
|  3 |    485 | August, 31 2015 00:00:00 |       13 |     16 |
|  1 |    485 | August, 31 2015 00:00:00 |       10 |     16 |

数据:

CREATE TABLE tableName 
    (`id` int, `areaId` int, `date` datetime, `fromHour` int, `tohour` int)
;

INSERT INTO tableName 
    (`id`, `areaId`, `date`, `fromHour`, `tohour`)
VALUES
    (1, 485, '2015-08-31 00:00:00', 10, 16),
    (2, 485, '2015-08-31 00:00:00', 17, 22),
    (3, 485, '2015-08-31 00:00:00', 13, 16),
    (4, 460, '2015-08-31 00:00:00', 15, 19)
;


CREATE TABLE tblNums
    (`n` int)
;

INSERT INTO tblNums
    (`n`)
VALUES
    (0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),
    (12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23), (24);

请参阅:http://sqlfiddle.com/#!9/30cd1/1

答案 2 :(得分:0)

我过去得到我想要的最终查询,感谢@Strawberry

SELECT x.* 
FROM my_table x 
JOIN my_table y 
ON y.area_id = x.area_id 
AND y.date = x.date 
AND y.from_hour <= x.to_hour 
AND y.to_hour >= x.from_hour 
AND y.id <> x.id;

我刚刚添加等于大于和小于,o避免在to_hour和from_hour等于时重叠小时。
再次感谢你。希望这有助于其他人。