在cakephp中查询所需的mySql建议

时间:2013-07-08 22:26:15

标签: php mysql cakephp

我有两种型号,清单和预订。列表有很多预订。我正在尝试编写一个查询来返回在特定时间段内没有预订的所有房源。

我已经到目前为止了,但我仍然是cakephp的新手,我的sql也不是很好。在我的控制器方法中我有

$conditions = array(
                            "OR" => array(
                                "Listing.address_one LIKE" => "%".$area."%",
                                "Listing.address_two LIKE" => "%".$area."%",
                                "Listing.city LIKE" => "%".$area."%",
                                "Listing.postcode LIKE" => "%".$area."%",
                                "Listing.title LIKE" => "%".$area."%",
                            )

                        );


    $this->paginate = array(
                'conditions'=> $conditions,
                'joins'=>array(
                          array(
                           'table'=>'bookings',
                           'alias'=>'Booking',
                           'type' =>'inner',
                           'conditions' =>array('Booking.listing_id = Listing.id', 'Booking.checkin NOT BETWEEN ? AND ?' => array($to, $from))
                          )
                        ),
                'limit' => 10,
                );

    $data = $this->paginate();

这只是获得实际上与他们相关的预订的所有列表。不仅如此,它也不会忽略在给定时期内预订的任何预订。

它创建的SQL是:

SELECT 
    ...
FROM 
    `database`.`listings` AS `Listing` 
INNER JOIN 
    `database`.`bookings` AS `Booking` ON (
        `Booking`.`listing_id` = `Listing`.`id` AND
        `Booking`.`checkin` NOT BETWEEN '18-07-2013' AND '10-07-2013'
     ) 
LEFT JOIN 
    `database`.`users` AS `User` ON (
        `Listing`.`user_id` = `User`.`id`
     ) 
WHERE (
    (`Listing`.`address_one` LIKE '%belfast%') OR 
    (`Listing`.`address_two` LIKE '%belfast%') OR 
    (`Listing`.`city` LIKE '%belfast%') OR 
    (`Listing`.`postcode` LIKE '%belfast%') OR 
    (`Listing`.`title` LIKE '%belfast%')
)
LIMIT 10

哪种看起来有点正确(?)但显然不是。任何想法?谢谢

1 个答案:

答案 0 :(得分:1)

  

返回所有未在特定时间段内预订的房源

生成适当查找的第一步是了解所需的查询类型 - 即从数据库开始。

问题中的查询是查找所有列表(匹配条件)以及超出日期范围的预订。即除BETWEEN '18-07-2013' AND '10-07-2013'之外的任何时间预订贝尔法斯特。这与上面的句子不同。

有多种方法可以实现这一点,这里有两种方法,以便于实施:

使用左连接

SELECT 
    *
FROM 
    `nights2stay`.`listings` AS `Listing` 
LEFT JOIN                                                             # <-
    `nights2stay`.`bookings` AS `Booking` ON (
        `Booking`.`listing_id` = `Listing`.`id` AND
        `Booking`.`checkin` BETWEEN '18-07-2013' AND '10-07-2013'     # <-
     ) 
LEFT JOIN 
    `nights2stay`.`users` AS `User` ON (
        `Listing`.`user_id` = `User`.`id`
     ) 
WHERE 
    (
        (`Listing`.`address_one` LIKE '%belfast%') OR 
        (`Listing`.`address_two` LIKE '%belfast%') OR 
        (`Listing`.`city` LIKE '%belfast%') OR 
        (`Listing`.`postcode` LIKE '%belfast%') OR 
        (`Listing`.`title` LIKE '%belfast%')
    ) AND
    `Booking`.`id` IS NULL                                            # <-
LIMIT 10

即。加入与日期范围匹配的日期的预订,并仅返回预订的记录。

要实现上述查询非常简单:

function index() {
    ... # Existing code

    $this->paginate['joins'][0]['type'] = 'LEFT';
    $this->paginate['conditions']['Booking.id'] = null;
    $data = $this->paginate();

    $this->set('data', $data);
}

使用NOT EXISTS

或者,使用NOT EXISTS

SELECT 
    *
FROM 
    `nights2stay`.`listings` AS `Listing`
                                                                      # <
LEFT JOIN 
    `nights2stay`.`users` AS `User` ON (
        `Listing`.`user_id` = `User`.`id`
     ) 
WHERE 
    (
        (`Listing`.`address_one` LIKE '%belfast%') OR 
        (`Listing`.`address_two` LIKE '%belfast%') OR 
        (`Listing`.`city` LIKE '%belfast%') OR 
        (`Listing`.`postcode` LIKE '%belfast%') OR 
        (`Listing`.`title` LIKE '%belfast%')
    ) AND
    NOT EXISTS (                                                      # <
        SELECT                                                        # <
            *                                                         # <
        FROM                                                          # <
            `nights2stay`.`bookings` AS `Booking`                     # <
        WHERE                                                         # <
            `Booking`.`listing_id` = `Listing`.`id` AND               # <
            `Booking`.`checkin` BETWEEN '18-07-2013' AND '10-07-2013' # <
    )                                                                 # <
LIMIT 10

这种查询更符合逻辑,并且有一个有意义的查询“在没有(......)的地方找到我这些查询”。它可能可能表现更好,但与任何查询一样,最好在选择一个或另一个之前剖析/解释查询。

要实现上述查询is a little more involved in CakePHP,但这并不困难:

function index() {
    ... 

    $db = $this->Listing->getDatasource();
    $subQuery = $db->buildStatement(array(
        'fields'     => "*",
        'table'      => $db->fullTableName('bookings'),
        'alias'      => 'Booking'
        'conditions' => array(
            '`Booking`.`listing_id` = `Listing`.`id`',
            "`Booking`.`checkin` BETWEEN '$to' AND '$from'"
        )
    ));

    $expression = $db->expression('NOT EXISTS (' . $subQuery . ')');

    $this->paginate['conditions'][] = $expression;
    $data = $this->paginate();

    $this->set('data', $data);
}