Выборка из множественных SQL таблиц одинаковой структуры для одной сущности
Начну несколько издалека. Предположим, у нас есть таблица (допустим mySQL) следующего вида:
CREATE TABLE `table` (
`id` int(11) unsigned NOT NULL,
`lang` tinyint(3) unsigned NOT NULL,
`data` text NOT NULL,
PRIMARY KEY (`id`,`lang`)
) ENGINE=InnoDB
В таблице хранятся данных для объекта с ID = `id` на нескольких языках, поэтому PRIMARY KEY = (id, lang). Типичный use case для этой таблицы: необходимо получить данные data для объекта с id = 1 желательно на языке 1, если его нет — на языке 2, иначе пофигу на каком, лишь бы был.
Такая задача решается достаточно просто:
SELECT * FROM `table` WHERE `id` = 1
ORDER BY
CASE WHEN `lang` = 1 THEN 1
WHEN `lang` = 2 THEN 2
ELSE 3
END ASC
LIMIT 1
Выборка получается достаточно быстрая и используя только PRIMARY ключ и сортировку.
Вопрос №1: как сделать такую выборку не для одного, а для N объектов сразу, не прибегая к subquery:
SELECT (подзапрос для одного объекта) FROM (запрос, выбирающий ID объектов)
Теперь представим ситуацию, что объектов этих — 1 000 000, плюс языков к каждой из них — ещё по 20. Берем и разбиваем одну таблицу на несколько с точно такими же структурами. Объекты распределятся по таблицам с помощью примитивной хэш-функции вида (id % N == 0).
Вопрос №2: Задача всё та же. Как теперь получить такую выборку? Думаю, что ответ на этот вопрос будет прямо вытекать из вопроса №1 :)
Дополнительные вопросы, которые, наверное, даже «главнее» указанных ранее: Насколько вообще оптимален такой путь решения поставленной задачи? Может быть есть более элегантные пути решения?
Что-то мне подсказывает, что должны быть более элегантные способы решения такой задачи :/
Заранее всем спасибо за предложенные варианты и обсуждения! ;)
Необходимо понять, каким образом задаются эти самые N объектов.
Если о характере выборки ID для этих объектов ничего нельзя сказать, то на вопрос 1 кроме предложенного варианта с подзапросом вряд ли что-то можно придумать.
Если я правильно понимаю задачу, результат первого вопроса будет выглядеть примерно так:
select *
from `table`
where id in (
<подзапрос или список для выбора id>
)
order by
case when lang = 1 then 1
when lang = 2 then 2
else 3
end asc
А вот по поводу второго… Если есть подобного рода запросы, может быть, оптимальнее разбивать не по id, а по языкам?
Если по языкам, то ответ на второй вопрос будет выглядеть так: select * from
(
select *
from `table1`
where id in (
<подзапрос или список для выбора id>
)
and lang in (1, 2, 4)
union all
select *
from `table2`
where id in (
<подзапрос или список для выбора id>
)
and lang in (3)
)
order by
case when lang = 1 then 1
when lang = 2 then 2
else 3
end asc
В общем случае набор ID — случайная выборка 100%-тно существующих в таблице (таблицах) объектов.
К сожалению, ваш вариант ответа на 1 вопрос работает не совсем верно, так как вернёт не одну строку на каждый объект, а сразу несколько. На ум приходит использование GROUP BY id, но насколько я знаю, GROUP BY не гарантирует какую строку из набора группируемых он выберет для отображения. А как тут прикрутить HAVING не очень понятно… Разве что как-то реализовать в нём HAVING(MIN( колонка, полученная CASE'ом ))?..
Второй ответ тоже интересен, но проблема та же — возвращаются несколько строк на каждый объект :/