Подсобите с запросом
От: Grog13 Финляндия  
Дата: 31.07.09 13:23
Оценка:
База MySQL

Три таблицы:

orders { OrderID — PK, ... }
items { OID — FK }
states { OID — FK }

Есть разные статусы заказов, которые хранятся в табличке states.
В табличке items — содержимое заказа.

Возникли трудности с запросом.
Нужно выбрать все заказы:
— у которых нет статусов { CLOSED, SENT }
— в содержании которых есть товары с определенной категорией (например category_id = 4) и для этих заказов нет статуса { PERIODIC }

Трудности в том, как мне анализировать содержимое заказа.
Вот пример.
Есть заказ. У него есть статус SENT. У него в содержании помимо прочего есть товары с категорией 4. У заказа нет статуса { PERIODIC } — его нужно выбрать.
Другой заказ. У него есть статус PERIODIC, есть содержимое с разными категориями (в том числе и 4). Но нет статусов { CLOSED, SENT } — надо выбрать
Третий заказ. У него только товары из категории 4. И нет статуса { PERIODIC } — надо выбрать.
Еще заказ. Есть товары где нет категории 4. И нет статусов CLOSED или SENT — тоже надо выбрать.

Вот. Если не очень понятно объяснил — попробую еще раз
Re: Подсобите с запросом
От: Donz Россия http://donz-ru.livejournal.com
Дата: 03.08.09 22:31
Оценка:
Здравствуйте, Grog13, Вы писали:

G>Три таблицы:


G>orders { OrderID — PK, ... }

G>items { OID — FK }
G>states { OID — FK }

Не хватает как минимум еще одной таблицы, связывающей многие-ко-многим товары (items) и заказы (orders). Пусть будет orderitems( int order, int item )

G>Есть разные статусы заказов, которые хранятся в табличке states.

G>В табличке items — содержимое заказа.

G>Возникли трудности с запросом.

G>Нужно выбрать все заказы:
G>- у которых нет статусов { CLOSED, SENT }
G>- в содержании которых есть товары с определенной категорией (например category_id = 4) и для этих заказов нет статуса { PERIODIC }

Как-то так, наверное:
select o.* from orders o 
  inner join orderitems oi on oi.order = o.order 
  inner join items i on i.item = oi.item 
  where ( o.status <> CLOSED and o.status <> SENT ) or 
  ( o.status <> PERIODIC and 4 not in 
     ( select i2.category_id from items i2 inner join orderitems oi2 on oi2.item = i2.item inner join orders o2 on o2.order = oi2.order where o2.order = o.order ) )

Возможно, не совсем прав насчет использования o.order из внешнего запроса во внутреннем, но вроде должно работать.
Ну и надо учитывать, что этот запрос очень неоптимальный.
Первое, что приходит в голову по поводу оптимизации, это добавление избыточной колонки с консолидированной информацией о продуктах заказа. Если исключения по статусам касаются только категорий, то как раз подходит. Можно взять битовую строку, где одним битом обозначить наличие в заказе хотя бы одного товара с категорией, которая соответствует этому биту. Так в запросе надо будет немного пошаманить с битовыми операциями, и никаких вложенных селектов.
Re: Подсобите с запросом
От: niteshade123  
Дата: 04.08.09 03:53
Оценка:
Здравствуйте, Grog13, Вы писали:

G>База MySQL


G>Три таблицы:


G>orders { OrderID — PK, ... }

G>items { OID — FK }
G>states { OID — FK }

G>Есть разные статусы заказов, которые хранятся в табличке states.

G>В табличке items — содержимое заказа.

G>Возникли трудности с запросом.

G>Нужно выбрать все заказы:
G>- у которых нет статусов { CLOSED, SENT }
G>- в содержании которых есть товары с определенной категорией (например category_id = 4) и для этих заказов нет статуса { PERIODIC }
select 
from orders o
where not exists (select null
                 from states s2
                 where s2.oid = o.OrderID
                       and s2.status in ('CLOSED', 'SENT')
                 )
      or (exists (select null
                 from items i2
                 where i2.oid = o.OrderID
                       and i2.category = 4
                 )
         and not exists (select null
                        from states s2
                        where s2.oid = o.OrderID
                              and s2.status = 'PERIODIC'
                        )
         )
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.