Здравствуйте. Есть таблица в базе с колонками dt (дата создания записи) типа datetime и updated_dt (on update CURRENT_TIMESTAMP). При выборке записей их необходимо отсортировать так: 1) сначала идут записи за сегодняшний день с датой добавления (dt) по убыванию; 2) записи добавленные вчера и позднее соритруются по дате добавления и обновления по убыванию (т.е. мы например, можем обновить запись, и тогда она поднимается наверх, но всё равно они идут после объявлений, добавленных сегодня - см. условие 1) ). Мой запрос выглядит так (комментарии оставил для ясности - оставляю запрос полностью, так как подозреваю, что просто не вижу в нём очевидного решения проблемы): Код (Text): SELECT * FROM ads_table /* определённый тип (рубрика), актуальность объявления */ WHERE type=1 AND ( DATE_ADD( publish_dt, INTERVAL public_interval MONTH ) > NOW() ) /* is_spec - флаг для специальных объявлений, а вот дальше условие, которым я пытаюсь решить задачу, т.е. добавлено сегодня, то используем дату добавления, если раньше, то берём большее из добавления-обновления */ ORDER BY is_spec DESC, IF( dt >= '2014-04-21 00:00:01',dt ,GREATEST(dt, updated_dt)) DESC, id DESC LIMIT 25 Но всё равно объявления, обновлённые сегодня (но добавленные в прошлом), проскакивают наверх, рядом со свежими. Как это можно исправить? Если я заменяю в ORDER BY Код (Text): IF( dt >= '2014-04-21 00:00:01',dt ,GREATEST(dt, updated_dt)) DESC на Код (Text): dt DESC то получаю, то что мне нужно, но тогда дата обновления перестаёт влиять на выборку.
а если склеить два запроса, "сегодня" и "всё остальное", через UNION ALL ? помоему условный ORDER BY это совсем хардкор. трудно для восприятия, особенно в понедельник.
Получился вот такой запрос Код (Text): (SELECT * FROM metr_ads WHERE TYPE =1 AND (DATE_ADD( publish_dt, INTERVAL public_interval MONTH ) > NOW( )) AND dt >= '2014-04-21 00:00:01') UNION (SELECT * FROM metr_ads WHERE TYPE =1 AND (DATE_ADD( publish_dt, INTERVAL public_interval MONTH ) > NOW( )) AND dt < '2014-04-21 00:00:01') ORDER BY is_spec DESC , GREATEST( dt, updated_dt ) DESC , id DESC LIMIT 25 Но это всё равно не то, так как не получается вынести LIMIT 25 в конец. Приходится выносить всё с ORDER BY, что по-моему нивелирует результат использования UNION.
1. UNION ALL, чтобы MySQL не пытался выкинуть дубликаты из полученного объединения 2. почему не получится LIMIT ? все отлично получится. почему у вас в первом SELECT отсутствует ORDER BY ? и почему во втором SELECT по прежнему выражение в ORDER BY? вы нифига не поняли вся идея с разбиением на два запроса как раз и состоит в упрощении сотировки. вы сможете использовать быстрые выборки. а главное — запрос станет понятным и доступным для модификаций. Добавлено спустя 9 минут 11 секунд: упрощённый "до немогу" пример: есть таблица с числами. надо вывести четыре наибольших числа в обратном порядке, затем числа с 2 до 5 (но не более трех строк) в прямом порядке. http://sqlfiddle.com/#!2/9d1323/3 Код (PHP): ( SELECT `n` FROM `numbers` ORDER BY `n` DESC LIMIT 4 ) UNION ALL ( SELECT `n` FROM `numbers` WHERE `n` BETWEEN 2 AND 5 ORDER BY `n` ASC LIMIT 3 ) всё работает просто отлично. присутствует индивидуальная сортировка и LIMIT. если кликните по ссылке "View Execution Plan", то увидите, что запрос использует индекс.
Я не могу написать запрос, похожий на то, что вы написали потому, что в нём используется LIMIT и OFFSET для постраничной навигации. Т.е. я не могу указать точное количество записей для каждого из подзапросов. В этой связи я и пытался вынести LIMIT в его конец. И в любом случае, второй подзапрос мне нужно сортировать по условию GREATEST( dt, updated_dt ) DESC, чтобы обновлённые объявления поднимались вверх.
вы ведь зашли в тупик, верно? значит надо пробовать то, к чему не были готовы. не надо приспосабливать новое решение под старые рамки, потому что решение при этом исчезает. нужен limit на все результаты в-целом для пагинации? оберните это двойное выражение в еще один SELECT и в нем сделайте LIMIT с оффсетом.
это прекрасно! значит и не надо limit во внутренних запросах ) ограничивайте только в WHERE по условию даты-времени. снаружи оборачиваете в SELECT с LIMIT справитесь? Добавлено спустя 2 минуты 25 секунд: Код (Text): SELECT o.* FROM ( (select…) UNION ALL (select…)) AS o LIMIT n, m Добавлено спустя 3 минуты 55 секунд: http://sqlfiddle.com/#!2/9d1323/4 поиграйте с LIMIT самостоятельно
В итоге у меня получается примерно такой запрос: Код (Text): SELECT o . * FROM ( (SELECT *, DATE_FORMAT(DATE_ADD( publish_dt, INTERVAL public_interval MONTH ), '%d.%m.%Y') as public_end_dt, (DATE_ADD( publish_dt, INTERVAL public_interval MONTH ) <= NOW()) as is_expired FROM my_table WHERE type=1 AND dt >= '2014-04-22 00:00:01' ORDER BY is_spec DESC , dt DESC , id DESC) UNION ALL (SELECT *, DATE_FORMAT(DATE_ADD( publish_dt, INTERVAL public_interval MONTH ), '%d.%m.%Y') as public_end_dt, (DATE_ADD( publish_dt, INTERVAL public_interval MONTH ) <= NOW()) as is_expired FROM my_table WHERE type=1 AND ( DATE_ADD( publish_dt, INTERVAL public_interval MONTH ) > NOW() ) AND dt < '2014-04-22 00:00:01' ORDER BY is_spec DESC , GREATEST( dt, updated_dt ) DESC , id DESC) ) AS o LIMIT 25 Если я выполняю первый селект из вложенных, то получаю, то что мне надо. Выполняю второй - тоже прекрасный результат. Но вот когда выполняю весь запрос в целом, то первая часть (где все сегодняшние объявления) идёт ОК, а вот во второй полная каша. Т.е. в ней записи не отсортированы по этому условию ORDER BY is_spec DESC , GREATEST( dt, updated_dt ) DESC , id DESC В Википедии по поводу UNION написано: Вот тут-то и проблема: мне нужно, чтобы сегодняшние записи всегда были сверху, а если я буду использовать общий ORDER BY, тогда обновлённые записи из второго запроса будут вылезать наверх.
жесть... есть солюшн: забей на свою могучую сортировку, и сделай некое универсальное правило. Например: 10 новостей за сегодня (одним запросом) и потом все остальные новости, за исключением тех 10ти (другим запросом).
renta, упрощайте, упрощайте и еще раз упрощайте. каша конечно. вы нагромоздили черте чего! сама идея, что надо сортировать в разные стороны уже сомнительна. трудно оценить результат когда сортировка туда-сюда. теперь вы сделали два запроса, по идее можно было бы упростить сортировку в каждом из двух случаев. так нет! вы сделали а) простую сортировку б) сортировку с вычислением. как они соотносятся друг с другом? этого никто не поймет. условие WHERE по начальной идее должно рассекать два запроса так, чтобы они в совокупности покрывали всё множество. то есть (x >= D) + (x < D) а что вы делаете? опять несопоставимые выражения. зачем у вас дата изменяется во фразе SELECT, чтобы скрыть от себя epic fail? для форматирования результатов больше приспособлен PHP куда вы всё равно затянете результат. упрощайте! Добавлено спустя 18 минут 37 секунд: согласен с igordata. вы "приплыли" — делайте всё совсем отдельными запросами, тогда возможно поймете где что происходит.