2010-11-11 3 views
0

Я застрял в эффективном извлечении данных из базы данных MySQL.MySQL: проблемы, ограничивающие набор результатов перед предложением HAVING

У меня есть стол с большим количеством предметов. Каждый элемент может иметь статус 1, 2 или 3. Я хочу выбрать элемент с определенными статусами, в зависимости от ссылок на эти записи в других таблицах.

Пример:

  • Таблица items с полями: идентификатор, имя, статус, ...
  • Таблица bought с полями: Itemid, идентификатор пользователя
  • Таблица rented с полями: Itemid, идентификатор пользователя

Скажите, что я хочу это для USERID 123:

  1. Все записи, которые покупаются, имеющие статус 1
  2. Все записи, которые сдаются в аренду, имеющие статус 1 или 2
  3. Все записи, которые не купили или арендовали, имеющие статус 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 
) 
    ...

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

+0

Являются '' bought' и rented' столбцы, необходимые для окончательного вывода Resultset, или они там только для использования псевдонима столбца? –

+0

Они также необходимы, чтобы дифференцировать элементы позже. – Alec

ответ

1

Используйте два подзапроса (один для купленного и один для арендованного) и оставите их в таблице пользователя в основном запросе.

EDIT: простите мой MySQL, это было какое-то время, но я имел в виду что-то вроде:

select i.id, i.name, i.status, ifnull(b.TotalBought,0) AS ItemsBought, ifnull(r.TotalRented,0) AS ItemsRented 
FROM items i 
LEFT JOIN (select itemid, COUNT(*) AS TotalBought FROM bought WHERE userid=123 GROUP BY itemid) AS b ON b.itemid=i.itemid 
LEFT JOIN (select itemid, COUNT(*) AS TotalRented FROM rented WHERE userid=123 GROUP BY itemid) AS r ON r.itemid=i.itemid 
WHERE (i.status=1 AND ifnull(b.TotalBought,0)>0) 
OR (ifnull(r.TotalRented,0) >0 AND i.status in(1,2) 
OR (ifnull(b.TotalBought,0)=0 AND ifnull(r.TotalRented,0) =0 AND i.status=3) 
ORDER BY i.name ASC 
+0

Где эти подзапросы идут? И в запросе нет пользовательской таблицы, а просто ссылка на определенный идентификатор пользователя. И как LEFT JOIN ограничивает набор результатов? – Alec

+0

А, проверьте! Я понимаю что ты имеешь ввиду. Играли с ним какое-то время, и, похоже, это сработало! Спасибо. – Alec

Смежные вопросы