Сведения о вопросе

Fedya

21:15, 8th August, 2020

Теги

SQL    

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

Просмотров: 247   Ответов: 8

Приветствую всех! Есть небольшой вопрос к понимающим 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, голова пока не думает, взываю к вашей помощи…
Реально ли вообще выкрутиться в такой ситуации одним запросом?

В принципе, если не получится сделать достаточно просто и производительно, есть идея создавать в памяти таблицу, хранящую в себе для каждой компании количество актуальных товаров и обновлять эту таблицу по большим праздникам типа поступления новых товаров и по расписанию каждый день.



  Сведения об ответе

crush

04:20, 20th August, 2020

Так будет работать:
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


  Сведения об ответе

dump

01:09, 20th August, 2020

Если я правильно понял, достаточно добавить в WHERE:

OR p.data IS NULL


  Сведения об ответе

piter

23:40, 9th August, 2020

where (to_days(now() — to_days(p.data)) <= 10 OR (to_days(now() — to_days(p.data)) IS NULL


  Сведения об ответе

fo_I_K

03:21, 16th August, 2020

А так не работает?
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 


  Сведения об ответе

FAriza

19:05, 9th August, 2020

Неплохо было бы СУБД указать, в принципе относительно универсальным должно быть нечто такое:

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


  Сведения об ответе

repe

14:48, 23rd August, 2020

тут два приемлемых варианта один через сложный запрос

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

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

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

далее формируем из этих данных требуемый вывод.


  Сведения об ответе

VCe znayu

05:33, 2nd August, 2020

Ответы от VolCh, xdenser и ArtemS похоже подходят, спасибо. Но проблема с производительностью остаётся, видимо придётся сделать, как планировал вначале…


  Сведения об ответе

LIZA

15:39, 28th August, 2020

Вот так можно, скорее всего будет работать быстрее чем через обычный 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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться