Mysql Select Query 以查找在两个给定日期之间也部分空闲的项目

Posted

技术标签:

【中文标题】Mysql Select Query 以查找在两个给定日期之间也部分空闲的项目【英文标题】:Mysql Select Query to find items also partially free between two given dates 【发布时间】:2021-09-30 12:17:29 【问题描述】:

我需要列出两个给定日期之间的免费物品。 我在 A 表中有物品,在 B 表中有它们的入住情况。 当我搜索两个日期之间的免费项目时,我还需要列出部分免费的项目。

Tabel A:
| ID       | ITEM_NAME      |
| -------- | -------------- |
| 1        | Item1          |
| 2        | Item2          |
| 3        | Item3          |

Table B:
|id_item     |occupancy_start_date     |occupancy_end_date|
|---------------------------------------------------------|
|1           |2021-07-24               |2021-08-06        |
|2           |2021-07-24               |2021-07-31        |
|3           |2021-07-29               |2021-08-03        |

当我在 2021-07-24 和 2021-08-06 之间搜索免费物品时,我必须得到 Item2 和 Item3。

Item2 is free from 2021-08-01 till 2021-08-06
Item3 is free from 2021-07-24 till 2021-07-29
Item3 is free from 2021-08-04 till 2021-08-06

(实际上我必须在用户给定的两个给定日期之间找到空闲的日期时段)

你们能帮帮我吗?谢谢。

【问题讨论】:

请向我们展示您的尝试。看看***.com/help/minimal-reproducible-example。此外,我们需要知道您是如何定义范围开始和结束的。 两个日期之间的范围。 【参考方案1】:

无窗口功能:

有一种方法是把提取分成三部分:“From start date”、“Till end date”和“Others”,并按每个查询过滤行,如下所示:

SET @sdate = DATE'2021-07-24';
SET @edate = DATE'2021-08-06';

-- From start date
SELECT
    b.id_item id_item,
    @sdate from_date,
    DATE_ADD(MIN(b.occupancy_start_date), INTERVAL -1 DAY) to_date
FROM TableB b
GROUP BY b.id_item
HAVING from_date <= to_date
-- Till end date
UNION ALL
SELECT
    b.id_item id_item,
    DATE_ADD(MAX(b.occupancy_end_date), INTERVAL 1 DAY) from_date,
    @edate to_date
FROM TableB b
GROUP BY b.id_item
HAVING from_date <= to_date
-- Others
UNION ALL
SELECT
    b1.id_item,
    DATE_ADD(b1.occupancy_end_date, INTERVAL 1 DAY),
    DATE_ADD(b2.occupancy_start_date, INTERVAL -1 DAY)
FROM TableB b1 INNER JOIN TableB b2
    ON b1.id_item=b2.id_item AND
    b1.occupancy_end_date < b2.occupancy_start_date AND
    -- Exclude inappropriate rows
    NOT EXISTS (SELECT 1 FROM TableB b WHERE
        b1.id_item = b.id_item AND (
        ( b1.occupancy_end_date < b.occupancy_start_date AND
          b.occupancy_start_date < b2.occupancy_start_date) OR
        ( b1.occupancy_end_date < b.occupancy_end_date AND
          b.occupancy_end_date < b2.occupancy_start_date) ) )
ORDER BY 1,2
;

DB Fiddle

带窗口功能:

如果是 mysql8,可以使用 LAG(或 LEAD)函数,像这样:

SET @sdate = DATE'2021-07-24';
SET @edate = DATE'2021-08-06';

SELECT
    a.ITEM_NAME,
    w.id_item,
    w.from_date,
    w.to_date
FROM (
  -- Non-free period(s) exist
  SELECT * FROM (
    SELECT
      id_item,
      DATE_ADD(LAG(occupancy_end_date, 1)
        OVER (PARTITION BY id_item ORDER BY occupancy_end_date),
        INTERVAL 1 DAY) from_date,
      DATE_ADD(occupancy_start_date, INTERVAL -1 DAY) to_date
    FROM TableB
    WHERE @sdate < occupancy_end_date AND occupancy_start_date < @edate
  ) t
  WHERE from_date <= to_date
  -- From start date
  UNION ALL
  SELECT
      id_item,
      @sdate from_date,
      DATE_ADD(MIN(occupancy_start_date), INTERVAL -1 DAY) to_date
  FROM TableB
  GROUP BY id_item
  HAVING from_date <= to_date
  -- Till end date
  UNION ALL
  SELECT
      id_item,
      DATE_ADD(MAX(occupancy_end_date), INTERVAL 1 DAY) from_date,
      @edate to_date
  FROM TableB
  GROUP BY id_item
  HAVING from_date <= to_date
  -- No occupancy
  UNION ALL
  SELECT
      ID,
      @sdate from_date,
      @edate to_date
  FROM TableA a
  WHERE NOT EXISTS (SELECT 1 FROM TableB b WHERE a.ID=b.id_item)
) w
INNER JOIN TableA a ON w.id_item=a.ID
ORDER BY
    w.id_item,
    w.from_date
;

DB Fiddle

【讨论】:

谢谢。这似乎是一个很好的操作,但是对于很多行,这变得非常非常慢! 我改为过滤每个查询。我认为这将比以前的查询提高性能。我添加了带有窗口​​功能的案例。 您好 Etsuhisa,我已将 Mysql 升级到 Versione del 服务器:10.4.20-MariaDB。现在您的功能运行良好。我需要这个查询的另外两个细节:1)tableA 中的项目名称(那么,如何在这个查询中加入两个表)2)在给定时间跨度内完全免费的项目。非常感谢您的专业回答。 我重写了“带窗口函数”的查询。 1)“tableA”中的项目名称可以通过使用INNER JOIN进行查询。 2) 将“包括免费”、“从开始”和“直到结束”分开,并在查询中添加“无占用”。 我在回答中更新了“带窗口功能”的查询。并查看 DB Fiddle "db-fiddle.com/f/bj8hYcSwFmUyZS82bYyTB6/3" 的链接。【参考方案2】:

这是另一个起点(使用其他人慷慨提供的小提琴 - 推送到更新的版本)......如果没有人击败我,我将在下周发布更完整的答案......

WITH RECURSIVE cte AS (
   SELECT '2021-07-24' dt
    UNION ALL
   SELECT dt + INTERVAL  1 DAY
     FROM cte
    WHERE dt < '2021-08-06')
   SELECT x.dt
        , a.*
     FROM cte x 
     JOIN TableA a
     LEFT
     JOIN TableB b
       ON b.id_item = a.id
      AND x.dt BETWEEN b.occupancy_start_date AND b.occupancy_end_date
    WHERE b.id_item IS NULL

【讨论】:

这很简短,似乎很好。我无法验证它,因为 phpmyadmin 产生错误“Sono stati trovati 1 errori durante l'analisi。Tipo statement non riconosciuto. (near "WITH" at position 0)"。错误号 #1064。 +-------------------------+--------------- ------------------+ |变量名 |价值 | +-------------------------+------------------------ ----------+ | innodb_version | 5.6.41-84.1 | |协议版本 | 10 | |版本 | 10.1.37-MariaDB | +-------------------------+------------------------ ----------+ Etsuhisa 抛出以下错误:#1064 - Errore di sintassi nella query SQL vicino a 'OVER ( PARTITION BY id_item ORDER BY ' linea 9 我会努力更新的!并让你知道。谢谢。 升级后就可以了。但问题并没有解决!这很好。 db-fiddle.com/f/bj8hYcSwFmUyZS82bYyTB6/0 但我需要在给定日期范围内完全免费的项目。

以上是关于Mysql Select Query 以查找在两个给定日期之间也部分空闲的项目的主要内容,如果未能解决你的问题,请参考以下文章

SET 和 Select Query 结合 Run in a Single MySql Query 将结果传递到 pentaho

php mysql增删查改

如何在 MySQL 中搜索多个列?

如何在 DB::select(query) 上使用分页 - Laravel

使用codeIgniter在MySQL中查找表是不是存在?

golang操作mysql,模糊查找like,%报错