Я застрял в эффективном извлечении данных из базы данных MySQL.MySQL: проблемы, ограничивающие набор результатов перед предложением HAVING
У меня есть стол с большим количеством предметов. Каждый элемент может иметь статус 1, 2 или 3. Я хочу выбрать элемент с определенными статусами, в зависимости от ссылок на эти записи в других таблицах.
Пример:
- Таблица
items
с полями: идентификатор, имя, статус, ... - Таблица
bought
с полями: Itemid, идентификатор пользователя - Таблица
rented
с полями: Itemid, идентификатор пользователя
Скажите, что я хочу это для USERID 123
:
- Все записи, которые покупаются, имеющие статус 1
- Все записи, которые сдаются в аренду, имеющие статус 1 или 2
- Все записи, которые не купили или арендовали, имеющие статус 3
Мой запрос на выборку теперь выглядит примерно так:
SELECT
i.id,
i.name,
i.status,
// bought by this user?
SUM(IF(b.userId = 123, 1, 0)) AS bought,
// rented by this user?
SUM(IF(r.userId = 123, 1, 0)) AS rented,
FROM items i
LEFT JOIN bought b
ON b.itemId = i.id
LEFT JOIN rented r
ON r.itemId = r.id
GROUP BY i.id
HAVING
// bought and status 1
(bought > 0 AND status = 1)
// rented and status 1 or 2
OR (rented > 0 AND status IN (1, 2)
// not bought or rented and status 3
OR (bougth = 0 AND rented = 0 AND status = 3)
ORDER BY i.name ASC
вопрос 1
Является ли частью SUM в предложении SELECT хорошим способом определить, есть ли записи в другой таблице, связанной с элементом? Предполагая, что на пользователя есть только одна запись, сумма будет 1 или 0, предоставив мне необходимую мне информацию. Но это просто кажется странным.
Вопрос 2
Несмотря на то, что это работает, есть одна большая проблема: он в основном получает все элементы, а затем фильтрует их с помощью предложения HAVING
. Поскольку записей достаточно много, запросы слишком медленны таким образом. Я пытаюсь понять, как это исправить.
Я сначала попробовал статью WHERE
.. но как?
...
WHERE
// only items with status 1 if bought or rented
(t.status = 1 AND (bought > 0 OR rented > 0))
// only items with status 2 if rented
OR (t.status = 2 AND rented > 0)
// only items with status 3 if not bought or rented
OR (t.status = 3 AND bought = 0 AND rented = 0)
...
Но вы не можете использовать переменные из пункта SELECT
. И поскольку в таблице элементов нет столбца rented
или bought
, это не сработает.
Я также попытался с помощью определяемых пользователем переменных, но это не сработало:
SELECT
...
@bought := SUM(IF(b.userId = 123, 1, 0)) AS bought,
...
WHERE @bought = ... // does not work
Тогда я попробовал подзапрос, но я не могу заставить его использовать основной идентификатор элемента запросов :
...
WHERE
...
// only items with status 2 if rented
OR (
t.status = 2
AND (
SELECT COUNT(r2.userId)
FROM rented r2
WHERE r2.userId = 123
AND r2.itemId = i.itemId // it doesn't recognize i.itemId
) > 0
)
...
Любые идеи? Я также хотел бы сохранить все в одном запросе. Это просто урезанный пример, но фактический довольно большой.Я уверен, что я могу разделить все и использовать различные запросы, чтобы собирать все по отдельности, но это просто добавит много кода и не упрощает его ремонтопригодность.
Являются '' bought' и rented' столбцы, необходимые для окончательного вывода Resultset, или они там только для использования псевдонима столбца? –
Они также необходимы, чтобы дифференцировать элементы позже. – Alec