Задать вопрос

Небольшой вопрос по SQL

Приветствую всех! Есть небольшой вопрос к понимающим SQL:

Предположим, есть у нас в базе компании и продукты. Компаний много, продуктов еще больше.
Каждый продукт сопровождается датой его занесения в базу, причём продукты «скоропортящиеся», т.е. по прошествии N дней продукт уже неактуален.

Требуется получить табличку вида (имя компании, кол-во актуальных продуктов).

Итак, представим, что у нас есть следующие таблички:

companies (id, name);
products (id,name,id_company,data)


Сначала всё работало через два запроса: один брал по очереди каждую компанию, другой проходил по таблице продуктов, находя актуальные и подсчитывая их.
Как известно, выполнение запроса в цикле — худшее из зол, к тому же обе таблички оказались весьма и весьма большими, поэтому такое решение не годится из соображений производительности.

Для повышения быстродействия попробовал использовать левое внешнее соединение таблиц,
дабы иметь в выводе и те компании, у которых есть товары, и те, у которых их нет.

Приблизительно получаем такой запрос:

select c.name, count( p.id ) as cnt
from company c left join products p on c.id = p.id_company
where to_days(now()) - to_days(p.data) <= 10
group by c.id


Запрос работает быстро, но неверно: из-за условия не выводит те компании, у которых продуктов нет вообще.

Дальше, в силу усталости и ограниченности знаний SQL, голова пока не думает, взываю к вашей помощи…
Реально ли вообще выкрутиться в такой ситуации одним запросом?

В принципе, если не получится сделать достаточно просто и производительно, есть идея создавать в памяти таблицу, хранящую в себе для каждой компании количество актуальных товаров и обновлять эту таблицу по большим праздникам типа поступления новых товаров и по расписанию каждый день.
  • Вопрос задан
  • 2517 просмотров
Подписаться 6 Оценить Комментировать
Пригласить эксперта
Ответы на вопрос 8
@ArtemS
Так будет работать:
select c.name, count( p.id ) as cnt
from company c
left join products p on c.id = p.id_company and  to_days(now()) - to_days(p.data) <= 10 
group by c.id
Ответ написан
Комментировать
vadimbelyaev
@vadimbelyaev
Если я правильно понял, достаточно добавить в WHERE:

OR p.data IS NULL
Ответ написан
where (to_days(now() — to_days(p.data)) <= 10 OR (to_days(now() — to_days(p.data)) IS NULL
Ответ написан
@xdenser
А так не работает?
select c.name, 
         (select count(*)  
          from products p
          where p.id_company = c.id
          and to_days(now()) - to_days(p.data) <= 10) as cnt
from company c 
Ответ написан
Комментировать
@lavel
Неплохо было бы СУБД указать, в принципе относительно универсальным должно быть нечто такое:

select r.name,SUM(r.cnt) from
(
	select c.name, count( p.id ) as cnt
	from company c left join products p on c.id = p.id_company 
	where to_days(now()) - to_days(p.data) <= 10
	group by c.name

	union

	select c.name, 0 as cnt
	from company c left join products p on c.id = p.id_company 
	where to_days(now()) - to_days(p.data) > 10
	group by c.name
) as r

group by r.name


Т.е. фактически тут выполняется два запроса с противоположными условиями. Если MSSQL — можно было бы сделать CROSS APPLY
Ответ написан
fStrange
@fStrange
тут два приемлемых варианта один через сложный запрос

второй через 2 сравнительно простых запроса

сначала вытаскиваем данные для таблицы продуктов
из этих данных выбираем список ид_компаний через php(или чем там программируете)

по этому списку одним запросом получаем имена компаний

далее формируем из этих данных требуемый вывод.
Ответ написан
Комментировать
Ambrose
@Ambrose Автор вопроса
Ответы от VolCh, xdenser и ArtemS похоже подходят, спасибо. Но проблема с производительностью остаётся, видимо придётся сделать, как планировал вначале…
Ответ написан
Duke
@Duke
Вот так можно, скорее всего будет работать быстрее чем через обычный left join всей таблицы:
select c.name, count( p.id ) as cnt
from company c
left join (select id, id_company from products where to_days(now()) — to_days(p.data) <= 10) as p on c.id = p.id_company
group by c.id
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы