<?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
WHERE some_col LIKE '%удалить строки содержащие эту фразу%'
LIMIT 0, 5
а MySQL глотает LIMIT 5 но не LIMIT 0, 5
Выходит что я не смогу решить задачу с помощью построителя запросов?
Как тогда обойти его, но преобразовать {my_table} чтобы не утратить переносимости?
Комментарии
А где генерится такой запрос?
Когда выполняется ->execute() вылетает ошибка pdo exception там отображается этот запрос.
Он имеет вид как указал - после LIMIT два числа. Скопировал его и выполнил в phpMyAdmin - та же ошибка.
Убрал 0 и запрос прошел. Отсюда и вывод что проблема глубокая.
Обычный db_delete возвращает объект у которого нет range() или чего-то похожего чтобы задать число строк.
Вот как-то так вот. Гуглил - но ничего внятного не нашел.
Это, типа, нормальное поведение
Гуглю на заказ. Долго. Дорого. Первый результат.
Это до меня дошло что нормальное поведение.
И на результат этот натыкался.
Вот не пойму куда этот патч приложить... попробую найти такой код.
В ядро лезть не хочется - ибо что это за модуль который требует хаков ядра.
Вот я к чему.
Не, не пойму куда ложить этот патч.
Кстати третий результат по гуглу - этот пост
Неужели никто не удаляет с LIMIT?
Мне это нужно для постепенного вычищения таблицы.
Последующий код выбирает очищенные строки. Вернее делает join и юзает то что чисто.
Вот и идея пришла - через UPDATE пойду. Поставлю нолики где надо.Там тоже синтаксис не позволяет...
Но вообще все странно это. Может у хостера кривой FreeBSD и PHP 5.2 - в этом может дело?
А никуда, туплю я.
Подобная тема поднималась, но, как видишь, ее загнули.
Очевидным решением будет использовать
db_query
Будет типа того:
<?php
$query = db_query('
DELETE FROM {my_table}
WHERE some_col LIKE :t LIMIT :limit',
array(':t'=>'%удалить строки содержащие эту фразу%', ':limit' => 5))->execute();
?>
Видимо просто придется разобраться с ошибкой
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 дня битвы много узнал зато.
Как ни хотел я юзать подзапросы, а по другому никак.
Решение вот такое.
<?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 и хотел я огорода такого избежать.
Извините а чем вас не устраивают подзапросы? В стандартном SQL скорее всего нельзя использовать LIMIT на delete-запросах.
Не раз видел конструкции типа delete t.* from t join t2 using(key) where ..., вот нафига такое писать, когда есть подзапросы? аллергия на них у некоторых чтоли...
В самом стандартном не знаю, может и нельзя.
Наверно алергия, вы правы. Подзапрос создает последовательность символов - тупое перечисление (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.
Да, похоже я погорячился с предыдущими высказываниями.
Решние - подзапросы.
Сначала выберите нужные идентификаторы с помощью db_select(), потом вставьте их в условие db_delete().
Зачем вам понадобилось удалять с LIMIT?
Одна задача высвобождает место в таблице по определенному условию.
Вторая задача выбирает свободные строки (путем join они вычисляются)
и уже работает с освободившимися.
Типа конвеера.
хы, хы, хы - воспользовался :), помоему здорово, спасибо, тоже пригодилось.
Это всё херня, разница будет в миллисекундах, если вообще будет. Выборка по первичному ключу происходит очень (очень) быстро, будь там хоть миллион в IN.
И, кстати, зачем вам эта копеечная оптимизация в delete-запросе? Он что, выполняется очень часто?
Просто маразм выполняется он не часто.
Просто личные соображения - выше я про циклы написал.
Может и в 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', 0, 3, // $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))
Все красиво, но не пашет.
Дык видимо в качестве аргумента передается пусто, не? Делай проверку на результат выборки, и не выполняй удаление, если пусто.
Видимо да, но получится уже 2 запроса. Но да фиг с ними.
Но а как же когда не пустой? ведь не пашет и не пустой.
Или надо пыхом бежать по результатам и самому составлять строку из цифр?
Вот тут и пойдет возрастание времени в тысячи раз и может получиться очень длинная строка,
на которую мускуль может ругнуться что превышена длина запроса.
И на кой фиг по всему тырнету валяются красивые образцы
$query->condition('id', $subquery, 'IN');
Я думаю что фишка
PDOдинамических запросов не в том чтобы составлять простейшие запросы, которые проще и без него составить. Разве оно для пыли в глаза сделано?Это не подзапрос а 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)
Я не понял ни о чем речь, ни для чего там delete, но, если вы будете удалять по несколько строк (не тысячи), то нужно, как я сказал "сначала выбирать нужные идентификаторы с помощью db_select(), потом вставлять их в условие db_delete()" - т.е. использовать два запроса, и не извращаться с оптимизацией.