1

我似乎对 SQL 毫无用处,我正试图弄清楚要使用的正确查询是什么。

我有一个表 Items 和一个表 Reservations。一个项目可以有多个预订,并且预订是在两个日期之间进行的。

我正在尝试创建一个搜索查询,它将返回在两个用户输入的日期之间没有保留的所有项目。

我现在的 SQL 看起来像:

SELECT `Item`.`id`, `Item`.`user_id`, `Item`.`name`, `Item`.`description`, `User`.`id`, `User`.`username`, `User`.`password`, `User`.`email` 
FROM `database`.`items` AS `Item` 
LEFT JOIN `database`.`reservations` AS `ReservationJoin` ON (`ReservationJoin`.`item_id` = `Item`.`id` AND `ReservationJoin`.`start` >= '2013-07-17' and `ReservationJoin`.`finnish` <= '2013-07-20') 
LEFT JOIN `database`.`users` AS `User` ON (`Item`.`user_id` = `User`.`id`) 
WHERE ((`Item`.`name` LIKE '%projector%') OR (`Item`.`description` LIKE '%projector%') 
AND `ReservationJoin`.`id` IS NULL 
LIMIT 10

我在 cakephp 2.3 中这样做,所以代码如下所示:

$this->paginate = array(
      "conditions" => array(
        "or" => array(
          "Item.name LIKE" => '%'.$projector.'%',
          "Item.description LIKE" => '%'.$projector.'%',
        ),
        "ReservationJoin.id" => null,
      ),
      "joins" => array(
        array(
          "table" => "reservations",
          "alias" => "ReservationJoin",
          "type" => "LEFT",
          "conditions" => array(
            "ReservationJoin.item_id = Item.id",
            "ReservationJoin.checkin >= '{$start}' and ReservationJoin.checkout <= '{$finnish}'",
          )
        )
      ),
      "limit"=>10
    );
    $data = $this->paginate('Item');

这不起作用,我认为这与加入不正确排除保留有关。但是我无法弄清楚正确的mysql是什么。善良的灵魂可以告诉我应该使用什么吗?

谢谢

4

3 回答 3

2

如果某事在两个日期之间有保留,则以下情况之一为真:

  1. 它在日期之间有一个开始日期
  2. 它在日期之间有一个结束日期
  3. 它的开始日期在较早日期之前,结束日期在第二个日期之后

以下查询在having子句中使用此逻辑。方法是在item级别进行聚合,并确保以上三个条件为真:

SELECT i.`id`, i.`user_id`, i.`name`, i.`description`
FROM `database`.`items`i LEFT JOIN
     `database`.`reservations` r
     ON r.`item_id` = i.`id`
WHERE ((i.`name` LIKE '%projector%') OR (i.`description` LIKE '%projector%')
group by i.id
having max(start between '2013-07-17' and '2013-07-20') = 0 and
       max(finish between '2013-07-17' and '2013-07-20') = 0 and
       max(start < '2013-07-17' and finished > '2013-07-20') = 0
LIMIT 10;

start请注意,不返回任何匹配项,因为条件在和ned为 NULL时被视为假。

于 2013-07-16T13:24:55.203 回答
0

你可以试试这个。

SELECT `Item`.`id`, `Item`.`user_id`, `Item`.`name`, `Item`.`description`, `User`.`id`, `User`.`username`, `User`.`password`, `User`.`email` 
FROM `database`.`items` AS `Item` 
LEFT JOIN `database`.`reservations` AS `ReservationJoin` ON (`ReservationJoin`.`item_id` = `Item`.`id`) 
LEFT JOIN `database`.`users` AS `User` ON (`Item`.`user_id` = `User`.`id`) 
WHERE ((`Item`.`name` LIKE '%projector%') OR (`Item`.`description` LIKE '%projector%')) 
AND `ReservationJoin`.`start` >= '2013-07-17' 
AND `ReservationJoin`.`finnish` <= '2013-07-20'
AND `ReservationJoin`.`id` IS NULL 
LIMIT 10

即只是将日期子句移到 WHERE 子句中,而不是在 JOIN CLAUSE 中

于 2013-07-16T14:29:32.667 回答
0

我认为在 CakePHP 中将所有条件放在 WHERE 子句中可能更容易。(这也可以通过一些 OUTER JOIN 来完成,但可能很难转录成 CakePHP)

    SELECT id, user_id, name, description
    FROM items
    WHERE ((name LIKE '%projector%') OR (description LIKE '%projector%'))
    AND NOT EXISTS(
    SELECT *
    FROM reservations
    WHERE items.id = reservations.item_id
    AND (
    '2013-07-17' BETWEEN start and finnish
    OR
    '2013-07-20' BETWEEN start and finnish
    OR 
    start BETWEEN '2013-07-17' AND '2013-07-20'));

或者,使用相同的逻辑,可以将 WHERE 子句清理为

    SELECT id, user_id, name, description
    FROM items
    WHERE ((name LIKE '%projector%') OR (description LIKE '%projector%'))
    AND NOT EXISTS(
    SELECT *
    FROM reservations
    WHERE items.id = reservations.item_id
    AND NOT(
    '2013-07-17' > finnish
    OR
    '2013-07-20' < start
    ));
于 2013-07-16T14:33:09.307 回答