выбор нескольких таблиц с использованием многих предложений where, есть ли лучший способ, чем самостоятельное соединение? - PullRequest
0 голосов
/ 18 июня 2009

У меня есть несколько таблиц, к которым я присоединяюсь, используя уникальный идентификатор элемента. Большинство условий предложения where будут построены программно из формы, в которую входит пользователь (окно поиска), и несколько условий часто будут проверяться на одной и той же таблице, в данном случае теги элементов.

Мой опыт работы с SQL минимален, но я понимаю основы. Я хочу найти идентификаторы активных (status = 1) предметов, которые были помечены тегом определенного типа, со значениями «коты» и «котята». Теги хранятся как (id, product_id, tag_type_id, value), причем id является единственным столбцом, требующим уникального значения. Моя первая попытка была

   select 
      distinct p2c.product_id 
   from '.TABLE_PRODUCT_TO_CATEGORY.' p2c
      inner join '.TABLE_PRODUCT.' p on p2c.product_id = p.id 
      inner join '.TABLE_PRODUCT_TAG.' pt on p.id = pt.product_id
      inner join '.TABLE_TAG_TYPE.' tt on pt.tag_type_id = tt.id
   where 
      tt.id = '.PRODUCT_TAG_TYPE_FREE_TAG.'
      and p.status = 1
      and lower(pt.value) = "cats"
      and lower(pt.value) = "kittens"

но это ничего не вернуло. Я понял, что проблема заключалась в конечном условии AND, поэтому вместо этого попытался использовать самосоединение;

   select 
      distinct p2c.product_id 
   from '.TABLE_PRODUCT_TO_CATEGORY.' p2c
      inner join '.TABLE_PRODUCT.' p on p2c.product_id = p.id 
      inner join '.TABLE_PRODUCT_TAG.' pt on p.id = pt.product_id
      inner join '.TABLE_PRODUCT_TAG.' pt2 on p.id = pt2.product_id
      inner join '.TABLE_TAG_TYPE.' tt on pt.tag_type_id = tt.id
   where 
      tt.id = '.PRODUCT_TAG_TYPE_FREE_TAG.'
      and p.status = 1
      and lower(pt.value) = "cats"
      and lower(pt2.value) = "kittens"

Теперь все работает как положено, и набор результатов правильный. Так что я хочу знать? Повторяю, результаты, которые мне нужны, - это идентификаторы активных (status = 1) элементов, которые были помечены тегом определенного типа со значениями «cats» И «kittens» ...

  1. Являются ли самостоятельные объединения лучшим способом достижения этих результатов?
  2. Этот запрос потенциально может быть огромным (я пропустил условие категории, которого может быть ~ 300), так хорошо ли масштабируется этот подход к самостоятельному объединению? Если нет, есть ли альтернатива?
  3. Будет ли подход с самостоятельным объединением лучшим способом продвижения вперед (при условии, что есть альтернатива), если я позволю пользователям определять сложные поиски по тегам? то есть "кошки" и ("котята" или "собаки") не "попугаи".

Ответы [ 7 ]

4 голосов
/ 18 июня 2009

не сработает ли это в вашем первом запросе?

вместо

and lower(pt.value) = "cats"
and lower(pt.value) = "kittens"

сделать это

and lower(pt.value) in ("cats","kittens")
3 голосов
/ 18 июня 2009
select 
  distinct p2c.product_id
from '.TABLE_PRODUCT_TO_CATEGORY.' p2c
  inner join '.TABLE_PRODUCT.' p on p2c.product_id = p.id
  inner join '.TABLE_PRODUCT_TAG.' pt on p.id = pt.product_id
  inner join '.TABLE_TAG_TYPE.' tt on pt.tag_type_id = tt.id   
where 
  tt.id = '.PRODUCT_TAG_TYPE_FREE_TAG.'
  and p.status = 1  
  and (lower(pt.value) = "cats" or lower(pt.value) = "kittens")
0 голосов
/ 19 июня 2009

КОД ВОЗДУХА

select 
  distinct p2c.product_id 
from '.TABLE_PRODUCT_TO_CATEGORY.' p2c
  inner join '.TABLE_PRODUCT.'     p  on p2c.product_id = p.id 
where 
  and p.status = 1
  and 2 = (
      SELECT  COUNT(1)
      FROM '.TABLE_PRODUCT_TAG.' pt
        INNER JOIN  '.TABLE_TAG_TYPE.' tt ON pt.tag_type_id = tt.id
      WHERE tt.id = '.PRODUCT_TAG_TYPE_FREE_TAG.'
      AND pt.product_id = p.id /* edit */
      lower(pt.value) IN( "cats", "kittens" )
)
0 голосов
/ 18 июня 2009

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

Вы пытаетесь показать все товары, которые имеют два разных специальных тега («коты» и «котята»), но теги хранятся в таблице «1 ко многим».

Двойное соединение работает, но есть другая альтернатива:

SELECT ...
FROM P
WHERE p.status = 1
  AND p.ProductID IN (SELECT Product_ID FROM tags WHERE value = "cats")
  AND p.ProductID IN (SELECT Product_ID FROM tags WHERE value = "kittens")

Просто добавьте дополнительные операторы AND в зависимости от параметров, выбранных пользователем.

Оптимизатор SQL на самом деле должен относиться к этому так же, как к объединению, поэтому я не думаю, что производительность будет масштабироваться на хуже , чем ваша версия. Тем не менее, стоит удостовериться в своем наборе данных, чтобы убедиться.

0 голосов
/ 18 июня 2009

Вы создаете еще одну модель данных сущности-атрибута-значения. Так как вы спросили о масштабируемости, вот вам предупреждение: модели EAV обычно не масштабируются и не работают поверх RDBMS. В конечном итоге эта «гибкая» модель данных приводит к тому, что оптимизатор сгущается, и вы будете сканировать миллионы и миллионы строк, чтобы найти ваших нескольких собак и котят. В Википедии есть тема , охватывающая эту модель и некоторые недостатки . Не знаю, какова ваша целевая БД, например, В SQL Server CAT опубликован технический документ с распространенными проблемами в модели EAV.

0 голосов
/ 18 июня 2009

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

0 голосов
/ 18 июня 2009

Проблема с первоначальным запросом заключалась в следующем:

  and lower(pt.value) = "cats"
  and lower(pt.value) = "kittens"

Не существует тега, для которого значение равно "cats" и "kittens", поэтому никакие записи не будут возвращены. Использование предложения IN, как предлагает SQLMenace, было бы решением - таким образом, вы говорите: «верните мне любой активный элемент, помеченный как« коты »или« котята »».

Но если вам нужен какой-либо активный элемент с ОБА тегами - вам нужно выполнить что-то вроде второго запроса. Из твоего вопроса не совсем понятно, что ты ищешь.

Что-то вроде вашего Вопроса № 3:

«кошки» и («котята» или «собаки») не «попугаи».

вы бы хотели pt1, pt2 и (в подзапросе) pt3 и что-то вроде этого:

and lower(pt1.value) = "cats"
and lower(pt2.value) in ("kittens", "dogs")
and not exists (select * from '.TABLE_PRODUCT_TAG.' pt3 where pt3.product_id = p.id and lower(pt3.value) = "parrots")

Широко распространенный случай может стать довольно грязным ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...