Zend Framework расширенный запрос WHERE IN

Итак, я придумал этот запрос, который отлично работает при выполнении. Однако, поскольку я хочу использовать разбиение на страницы из Zend, запрос должен быть через адаптер.

Это запрос, который нужно преобразовать: (я вырезал запрос, поэтому здесь только важная часть. Я знаю, что вы не можете выполнить этот запрос, LOL)

//snip
WHERE
    city IN (
        SELECT 
            Plaatsnaam
        FROM
            plaatsnamen
        WHERE
            Latitude 
                BETWEEN 
                    ?
                AND 
                    ?
        AND
            Longitude
                BETWEEN
                    ?
                AND
                    ?       
    )
//snip

Работает отлично! Однако... Как видите, я использую "расширенный" вариант WHERE IN. Почему-то я не могу преобразовать это, и я застрял.

Вот во что его нужно преобразовать:

$db = Zend_Db_Table::getDefaultAdapter();                
$select = $db->select('gethousecity.id, city')
                                ->from('gethousecity')
                                    ->join('houses_props', 'houses_props.id = gethousecity.id')
                                ->where('city IN ()') // HOW TO CONVERT THE UPPER QUERY TO FIT IT HERE?
                                ->where('is_online = 1')
                                ->where('houses_props.prop_name = ?', 'something')
                                ->where('houses_props.prop_value BETWEEN ? AND ?', array(1,2));

Я вообще не нашел никакой документации о том, как преобразовать запрос в какой-либо формат, который подходит здесь. У кого-нибудь есть идеи? В основном потому, что мне нужно вставить какие-то значения, а ZF переписывает там какое-то странное дерьмо.

Я немного застрял сейчас, любая помощь была бы потрясающей!


person ChrisH    schedule 14.05.2012    source источник


Ответы (4)


Подзапросы работают нормально.

$select1->cols('id')->where('date > NOW()');
$select2->where('id IN (?)', $select1);

Если проблема сложная, то вы можете использовать именованные параметры.

$select1 = $db->select();
$select2 = $db->select();
$select1->from('plaatsnamen', array('Plaatsnaam'))
        ->where('Latitude BETWEEN :latmin AND :latmax')
        ->where('Longitude BETWEEN :longmin AND :longmax');
$select2->from('gethousecity')
        ->join('houses_props', 'houses_props.id = gethousecity.id')
        ->where('city IN ?', $select1) // HOW TO CONVERT THE UPPER QUERY TO FIT IT HERE?
        ->where('is_online = 1')
        ->where('houses_props.prop_name = ?', 'something')
        ->where('houses_props.prop_value BETWEEN :propsmin AND :propsmax');
die($select2);

Возвращает:

SELECT `gethousecity`.*, `houses_props`.*
FROM `gethousecity`
INNER JOIN `houses_props` ON houses_props.id = gethousecity.id
WHERE 
    (city IN (
        SELECT `plaatsnamen`.`Plaatsnaam`
        FROM `plaatsnamen`
        WHERE 
        (Latitude BETWEEN :latmin AND :latmax) AND
        (Longitude BETWEEN :longmin AND :longmax)
    )) AND
    (is_online = 1) AND
    (houses_props.prop_name = 'something') AND
    (houses_props.prop_value BETWEEN :propsmin AND :propsmax)

Что должно работать с именованными параметрами, как показано в вручную

$bind = array(
    ':latmin' => 10,
    ':latmax' => 3,
    '...' => '...',
);
$db->fetchAll($select2, $bind);
person Tomáš Fejfar    schedule 14.05.2012
comment
Да, для этих простых вещей это делает. Не тогда, когда у вас есть подзапрос. - person ChrisH; 14.05.2012
comment
Да, это тоже работает! Это странно, это работает для именованных параметров. Я поддерживаю ваш ответ, так как вы используете $db. В конце концов, оба наших ответа верны! - person ChrisH; 14.05.2012
comment
Но главной проблемой были именованные параметры? Вы можете легко создать свой собственный адаптер Db и переопределить выбор. Мы используем его для добавления языковых суффиксов в столбцы таблицы, т.е. меняем каждое {что-нибудь}_en на {что-нибудь}_de для пользователя с немецким языком. - person Tomáš Fejfar; 14.05.2012
comment
Вы также можете привязать именованные параметры во время создания select: $select->bind(array(':latin' => 10, ':latex' => 3)); - person Bolebor; 27.07.2016

В конце концов, в качестве быстрого обходного пути я придумал следующее: $db->цитировать.

$select = $db->select('gethousecity.id, city')
                                    ->from('gethousecity')
                                        ->join('houses_props', 'houses_props.id = gethousecity.id')
                                    ->where('city IN (  
                                                    SELECT Plaatsnaam
                                                    FROM plaatsnamen
                                                    WHERE Latitude BETWEEN '.$latlon_search[0].' AND '.$latlon_search[1].'
                                                        AND Longitude BETWEEN '.$latlon_search[2].' AND '.$latlon_search[3].'
                                    )') 
                                    ->where('is_online = 1')
                                    ->where('houses_props.prop_name = ?', 'vraagprijs[vraagprijs]')
                                    ->where('houses_props.prop_value BETWEEN '.$db->quote($price_start).' AND '.$db->quote($price_end));

Для меня это был единственный способ заставить эту работу работать. Каждый раз, когда я делал:

->where('statement ? AND ?', array($val1, $val2)

Результат был следующим:

statement val1,val2 AND val1,val2
person ChrisH    schedule 14.05.2012

Насколько я знаю, ZF DB не имеет встроенной поддержки подзапросов.

Вы можете попробовать сделать это:

// snip
->where("city IN (
    SELECT Plaatsnaam
    FROM plaatsnamen
    WHERE Latitude BETWEEN $lowLat AND $highLat
      AND Longitude BETWEEN $lowLon AND $highLon
)")
// snip

EDIT: Интерполяция значений непосредственно в строку условия, это безопасно только в том случае, если переменные являются доверенными (или если вы получили их от пользователя, убедитесь, что вы преобразовали их в числа с плавающей запятой, прежде чем делать это).

В качестве альтернативы вы можете попытаться избежать подзапроса и вместо этого выполнить соединение, но это может повлиять на производительность (возможно, в лучшую или худшую сторону).

person Ezequiel Muns    schedule 14.05.2012
comment
Ну, я тоже об этом подумал. Однако это не работает. ZF переписывает запрос таким странным образом: pastebin.com/veCSzj6A - person ChrisH; 14.05.2012
comment
@SiteSafeNL Можете ли вы опубликовать полученный запрос? - person Ezequiel Muns; 14.05.2012
comment
4 значения вставляются сразу, а не по одному. Для меня это не имеет никакого смысла - person ChrisH; 14.05.2012
comment
Я только что просмотрел код Zend_Db_Select, и функция where() принимает только один параметр, то есть каждый ? в предложении цитируется с тем же значением. Другой вариант — заключать значения в кавычки непосредственно в предложении. Обновляю свой ответ, чтобы отразить это. - person Ezequiel Muns; 14.05.2012
comment
Действительно, и это ошибка дизайна, по моему честному мнению :/ - person ChrisH; 14.05.2012
comment
Это выбор дизайна, я думаю, это помогает, когда у вас есть куча идентификаторов, которые вы хотите включить, и вы можете просто сделать ->where('id IN (?)', $arrayOfIds), но это может быть умнее. Еще одна вещь, которую вы могли бы сделать, это использовать именованные параметры. - person Ezequiel Muns; 14.05.2012
comment
Именованные параметры также являются хорошим решением. Небольшое примечание к вашему редактированию; Я очень хорошо проверил свои параметры. Обычно вы должны избегать любых данных, которые вы вводите в свой запрос, чтобы избежать SQL-инъекций. Так, на всякий случай ;) - person ChrisH; 14.05.2012

Попробуй это

 $db = Zend_Db_Table::getDefaultAdapter();  
    $selectInner = $db->select()->from('plaatsnamen','Plaatsnaam')
                   ->where("Lattitude BETWEEN $lowLat AND $highLat")
                   ->where("Longitude BETWEEN $lowLon AND $highLon")
                   ->__toString();



    $select = $db->select('gethousecity.id, city')
                                    ->from('gethousecity')
                                        ->join('houses_props', 'houses_props.id = gethousecity.id')
                                    ->where("city IN ($selectInner)"); 
person Mr Coder    schedule 14.05.2012
comment
Вы должны определенно указывать vars по соображениям безопасности. - person ChrisH; 14.05.2012