Не могу выполнить запрос удаления с LIMIT

Главные вкладки

Аватар пользователя serega386 serega386 24 сентября 2013 в 10:41
<?php
$query 
db_query_range('
DELETE FROM {my_table}
WHERE some_col LIKE :t'
0$limit
array(
':t'=>'%удалить строки содержащие эту фразу%'))->execute();
?>

Насколько я понял это косяк PDO - генерит запрос вида

DELETE FROM my_table
WHERE some_col LIKE '%удалить строки содержащие эту фразу%'
LIMIT 0, 5

а MySQL глотает LIMIT 5 но не LIMIT 0, 5

Выходит что я не смогу решить задачу с помощью построителя запросов?
Как тогда обойти его, но преобразовать {my_table} чтобы не утратить переносимости?

Комментарии

Аватар пользователя serega386 serega386 24 сентября 2013 в 13:38

Когда выполняется ->execute() вылетает ошибка pdo exception там отображается этот запрос.
Он имеет вид как указал - после LIMIT два числа. Скопировал его и выполнил в phpMyAdmin - та же ошибка.
Убрал 0 и запрос прошел. Отсюда и вывод что проблема глубокая.
Обычный db_delete возвращает объект у которого нет range() или чего-то похожего чтобы задать число строк.
Вот как-то так вот. Гуглил - но ничего внятного не нашел.

Аватар пользователя validoll validoll 24 сентября 2013 в 14:10

"serega386" wrote:
Скопировал его и выполнил в phpMyAdmin - та же ошибка.
Убрал 0 и запрос прошел. Отсюда и вывод что проблема глубокая.

Это, типа, нормальное поведение
"serega386" wrote:
Гуглил - но ничего внятного не нашел.

Гуглю на заказ. Долго. Дорого. Первый результат.

Аватар пользователя serega386 serega386 24 сентября 2013 в 14:38

Это до меня дошло что нормальное поведение.
И на результат этот натыкался.
Вот не пойму куда этот патч приложить... попробую найти такой код.
В ядро лезть не хочется - ибо что это за модуль который требует хаков ядра.
Вот я к чему.

Аватар пользователя serega386 serega386 24 сентября 2013 в 15:03

Не, не пойму куда ложить этот патч.
Кстати третий результат по гуглу - этот пост Smile
Неужели никто не удаляет с LIMIT? Smile
Мне это нужно для постепенного вычищения таблицы.
Последующий код выбирает очищенные строки. Вернее делает join и юзает то что чисто.
Вот и идея пришла - через UPDATE пойду. Поставлю нолики где надо.
Там тоже синтаксис не позволяет...
Но вообще все странно это. Может у хостера кривой FreeBSD и PHP 5.2 - в этом может дело?

Аватар пользователя validoll validoll 24 сентября 2013 в 15:33

"serega386" wrote:
Не, не пойму куда ложить этот патч.

А никуда, туплю я.
Подобная тема поднималась, но, как видишь, ее загнули.
Очевидным решением будет использовать db_query
Будет типа того:

<?php
$query 
db_query('
DELETE FROM {my_table}
WHERE some_col LIKE :t LIMIT :limit'

array(
':t'=>'%удалить строки содержащие эту фразу%'':limit' => 5))->execute();
?>
Аватар пользователя serega386 serega386 24 сентября 2013 в 16:36

Видимо просто придется разобраться с ошибкой

PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''15'' at line 1: DELETE FROM {my_table} WHERE chresult LIKE :t LIMIT :limit; Array ( [:t] => %Ошибка: Ваш% [:limit] => 15 ) в функции mymodule_check() (строка 239 в файле /.../www/sites/all/modules/mymodule/mymodule.module).

Я просто поленился и подумал динамические вопросы решат проблему, но похоже с ними еще круче.
Но за 2 дня битвы много узнал зато.

Аватар пользователя serega386 serega386 24 сентября 2013 в 17:17

Как ни хотел я юзать подзапросы, а по другому никак.
Решение вот такое.

<?php
$subquery 
db_query_range('SELECT id FROM {my_table}
WHERE chresult LIKE :t'
0$limit,
array(
':t'=>'%Ошибка: Ваш%'));

$query db_delete('my_table');
$query->condition('id'$subquery'IN');
$query->execute();
?>

Просто там могут быть тысячи этих id и хотел я огорода такого избежать.

Аватар пользователя deb deb 25 сентября 2013 в 5:13

Извините а чем вас не устраивают подзапросы? В стандартном SQL скорее всего нельзя использовать LIMIT на delete-запросах.

Не раз видел конструкции типа delete t.* from t join t2 using(key) where ..., вот нафига такое писать, когда есть подзапросы? аллергия на них у некоторых чтоли...

Аватар пользователя serega386 serega386 25 сентября 2013 в 8:45

В самом стандартном не знаю, может и нельзя.

Наверно алергия, вы правы. Подзапрос создает последовательность символов - тупое перечисление (1, 2, 3, 4, ...). Я боюсь что при тысячах таких перечислений будет превышена длина запроса - это во первых. А во вторых как программист знаю - сначала создать строку, а потом заново ее интерпретировать долго. Т.е. двойная работа по конвертации - в разы замедляет запросы, если не в десятки. Хотя на счет строки я тут преувеличил наверно. Работа подзапроса происходит в сервере и там последовательность в скомпилированом виде. Но то что сервер создает перечень никаким образом не связанных собой id это точно, а значит потом бежит по этому перечню, а значит запрос будет долгим.

Вот пример на псевдоязыке как я вижу работу сервера

$subquery = ..... ;
for ( i = 0; i < count_lines($table); i++) {
for ( j = 0; j < count($subquery); j++) {
if ($table[i]['mycol'] == $subquery[j] ) { do_delete(); }
}
}

Два цикла вместо одного (прохода по строкам таблицы).
Т.е. скорость запроса падает пропорционально длине списка полученного подзапросом.
А если подзапрос возвращает тысячи id или десятки тысяч, то скорость падает в такое кол-во раз.
Вот наверно мысли об этом и вызывают отторжение использовать IN.

Аватар пользователя tonyk tonyk 25 сентября 2013 в 15:04

"serega386" wrote:
Не, не пойму куда ложить этот патч.
Правильный ответ такой: если вы используете pdo, т.е. пишете код, который должен выполняться на разных диалектах SQL, то вы для удаления должны использовать db_delete() или DeleteQuery со всеми вытекающими, как то: невозможность использовать "LIMIT".
Сначала выберите нужные идентификаторы с помощью db_select(), потом вставьте их в условие db_delete().

Зачем вам понадобилось удалять с LIMIT?

Аватар пользователя serega386 serega386 25 сентября 2013 в 15:17

Одна задача высвобождает место в таблице по определенному условию.
Вторая задача выбирает свободные строки (путем join они вычисляются)
и уже работает с освободившимися.
Типа конвеера.

Аватар пользователя serega386 serega386 25 сентября 2013 в 16:16

"deb" wrote:
Не раз видел конструкции типа delete t.* from t join t2 using(key) where ..., вот нафига такое писать, когда есть подзапросы? аллергия на них у некоторых чтоли...

хы, хы, хы - воспользовался :), помоему здорово, спасибо, тоже пригодилось.

Аватар пользователя deb deb 26 сентября 2013 в 6:02

"serega386" wrote:

Это всё херня, разница будет в миллисекундах, если вообще будет. Выборка по первичному ключу происходит очень (очень) быстро, будь там хоть миллион в IN.

И, кстати, зачем вам эта копеечная оптимизация в delete-запросе? Он что, выполняется очень часто?

Аватар пользователя serega386 serega386 26 сентября 2013 в 9:43

Просто маразм Smile выполняется он не часто.
Просто личные соображения - выше я про циклы написал.
Может и в mysql-сервере все будет быстро, а вот если на php такой код реализовать,
то будет очень медленно - отсюда мысли тянутся.
А от статических запросов ушел поскольку мне кажется хоть он и статический и теоретически должен выполняться всегда, но что-то встроили в db_query и там идут какие-то проверки, и даже вышеприведенные примеры, которые вроде должны работать, не работают. Беда толи в LIKE толи в LIMIT. Не пропускает их механизм database api.

Но кстати даже с подзапросами вопрос не решился.
Я рано обрадовался. Поскольку получил вывод командами

<?php
$msg 
$query->__toString();
drupal_set_message($msg);
?>

Видимо неправильно я юзаю подзапросы...

и обрадовался раньше времени. Там то красивый запрос вышел с сотней подстановочных токенов или как там они правильно называются.

В рабочем коде 2 варианта исхода событий и оба с ошибками.
Первый когда подзапрос возвращает путой перечень:

PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '))' at line 1: DELETE FROM {my_table} WHERE (id IN ()) ; Array ( ) в функции mymodule_check() (строка 259 в файле /.../sites/all/modules/mymodule/mymodule.module).

Второй исход когда перечень не пустой

Recoverable fatal error: Object of class stdClass could not be converted to string в функции DatabaseStatementBase->execute() (строка 2168 в файле /.../includes/database/database.inc).

Код с подзапросом вот такой

<?php
$subquery 
db_query_range('SELECT id FROM {my_table}
WHERE chresult LIKE :t'
03// $limit - $free
array(':t'=>'%Ошибка: Ваш%'));

$query db_delete('my_table');
$query->condition('id'$subquery'IN');
$query->execute();?>

Перед выполнением ->execute() вот такой код

<?php
$msg 
$query->__toString();
drupal_set_message($msg);
?>

выдает такой результат

DELETE FROM {my_table} WHERE (id IN (:db_condition_placeholder_0, :db_condition_placeholder_1, :db_condition_placeholder_2))

Все красиво, но не пашет.

Аватар пользователя validoll validoll 26 сентября 2013 в 9:54

"serega386" wrote:
PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '))' at line 1: DELETE FROM {my_table} WHERE (id IN ()) ; Array ( ) в функции mymodule_check() (строка 259 в файле /.../sites/all/modules/mymodule/mymodule.module).

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

Аватар пользователя serega386 serega386 26 сентября 2013 в 10:04

Видимо да, но получится уже 2 запроса. Но да фиг с ними.
Но а как же когда не пустой? ведь не пашет и не пустой.
Или надо пыхом бежать по результатам и самому составлять строку из цифр?
Вот тут и пойдет возрастание времени в тысячи раз и может получиться очень длинная строка,
на которую мускуль может ругнуться что превышена длина запроса.
И на кой фиг по всему тырнету валяются красивые образцы
$query->condition('id', $subquery, 'IN');

Я думаю что фишка PDO динамических запросов не в том чтобы составлять простейшие запросы, которые проще и без него составить. Разве оно для пыли в глаза сделано?

Аватар пользователя deb deb 26 сентября 2013 в 10:40

Это не подзапрос а 2 разных запроса.

Подзапрос выглядит так: select * from t where t.id in (select id from t2 where ...)

В вашем случае будет что-то вроде delete from t where id in (select * from (select id from t where ... limit 3 offset 0) temp)

Аватар пользователя tonyk tonyk 27 сентября 2013 в 12:52

"serega386" wrote:
Одна задача высвобождает место в таблице по определенному условию.
Вторая задача выбирает свободные строки (путем join они вычисляются)
и уже работает с освободившимися.
Типа конвеера.

Я не понял ни о чем речь, ни для чего там delete, но, если вы будете удалять по несколько строк (не тысячи), то нужно, как я сказал "сначала выбирать нужные идентификаторы с помощью db_select(), потом вставлять их в условие db_delete()" - т.е. использовать два запроса, и не извращаться с оптимизацией.