Самый умный способ написать запрос «у элемента есть свойства A, B, C, но не X, Y, Z» - PullRequest
0 голосов
/ 08 октября 2009

Хорошо, допустим, у меня есть две таблицы:

  • элементы с идентификаторами столбцов, вещи
  • item_properties со столбцами item_id, prop_id

Теперь я хочу выполнить запрос, подобный

SELECT stuff FROM items WHERE
      EXISTS(SELECT * FROM item_properties WHERE prop_id = 123 AND item_id = items.id)
  AND EXISTS(SELECT * FROM item_properties WHERE prop_id = 456 AND item_id = items.id)
  AND NOT EXISTS(SELECT * FROM item_properties WHERE prop_id = 789 AND item_id = items.id)
  AND NOT EXISTS(SELECT * FROM item_properties WHERE prop_id = 101 AND item_id = items.id)

Что работает, но выглядит некрасиво и медленно. Кто-нибудь может придумать более разумный способ сделать это? При необходимости я также могу получить списки 123 456 и 789 101 через подзапрос из третьей таблицы. Я открыт для предложений по изменению дизайна моего стола.

Количество идентификаторов свойств, с которыми мне нужно проверить свойства элемента, может варьироваться.

Спасибо!

Ответы [ 4 ]

2 голосов
/ 08 октября 2009

, если у вас есть таблица, содержащая prop_id для включения и исключения itemsIU (item_id, prop_id, include)

select distinct stuff 
  from items i
  join item_properties ip on i.id = ip.item_id
  join itemsIU iiu on ip.prop_id = iiu.prop_id
group by i.id
having sum(include) = (select count(1) 
                         from itemsIU iiu2 
                        where i.id = iiu2.item_id
                          and iiu2.include = 1)

для вашего конкретного примера вы можете использовать:

select distinct stuff 
  from items i
  join item_properties ip on i.id = ip.item_id
  join (          select 123 prop_id, 1 include
        union all select 456, 1
        union all select 789, 0
        union all select 101, 0) iiu on ip.prop_id = iiu.prop_id
group by i.id
having sum(include) = 2
1 голос
/ 08 октября 2009
SELECT stuff 
FROM Items as i
INNER JOIN item_properties as ip ON i.id = ip.item_id
WHERE ip.prop_id IN (123, 456) AND ip.prop_id NOT IN (789, 101)

Это НЕ ПРОВЕРЕНО, но я так часто делаю такие вещи.

Если я правильно понял вашу проблему, она должна работать.

0 голосов
/ 08 октября 2009

поздравляю, вы наткнулись на одну из действительно веских причин не использовать EAV. :)

лучшее, что вы можете сделать, это:

SELECT *
  FROM items
 WHERE id IN (SELECT id FROM item_properties WHERE prop_id in (123, 456))
   AND id NOT IN (SELECT id FROM item_properties WHERE prop_id in (789, 101))

когда mysql получает материализацию подзапроса, произойдет некоторая оптимизация (например, преобразование одного из подзапросов в JOIN), но сейчас он просто сгенерирует производные таблицы, которые не будут масштабироваться.

0 голосов
/ 08 октября 2009
SELECT 
  i.*
FROM 
  Items as i
  INNER JOIN item_properties as ip ON i.id = ip.item_id
WHERE 
   ip.prop_id IN (123, 456) 
   AND ip.prop_id NOT IN (789, 101)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...