MySQL лучший способ построить этот запрос? - PullRequest
0 голосов
/ 06 декабря 2008

Я унаследовал базу данных, которая имеет структуру с таблицей продуктов, таблицей, состоящей из некоторых атрибутов продукта, и другой таблицей для построения взаимосвязи между этими атрибутами и данным продуктом.

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

Запрос в настоящее время выглядит так (не мой код):

SELECT DISTINCT p.* FROM products AS p 
INNER JOIN attributes a ON p.product_id=a.property_id 
WHERE a.attribute_id IN (1,3,7) 
GROUP BY p.property_id 
HAVING COUNT(DISTINCT a.attribute_id) = 3 

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

Я создал код php для создания специального запроса, когда выбраны два «специальных» атрибута (3 и 7):

SELECT DISTINCT p.* FROM products AS p 
INNER JOIN attributes a ON p.product_id=a.property_id 
WHERE a.attribute_id IN (1,3) OR a.attribute_id IN (1,7) 
GROUP BY p.property_id 
HAVING COUNT(DISTINCT a.attribute_id) = 2

Однако это по-прежнему не работает должным образом - любые продукты, которые имеют оба этих атрибута, не возвращаются в результате (это, очевидно, связано с предложением HAVING COUNT, но я не знаю, как мне это исправить. Для ясности, проблема в том, что если у 10 товаров есть только атрибут 3, а у еще пяти есть атрибуты 3 и 7, приведенный выше запрос вернет только 10 записей.

Возможно ли использовать какой-то подзапрос или какие есть альтернативы?

Ответы [ 5 ]

2 голосов
/ 06 декабря 2008

Запрос выглядит нормально, за исключением того, что вы, возможно, можете удалить модификатор DISTINCT, так как вы уже группируете по id. Что касается нового требования, разве вы не можете решить его в своем коде до того, как оно достигнет SQL-запроса?

Редактировать: альтернативой может быть создание запроса с одним внутренним объединением для каждого необходимого атрибута, но это, вероятно, будет намного медленнее

1 голос
/ 06 декабря 2008

Это лучший способ сделать оригинальный запрос:

SELECT ... FROM products AS p 
INNER JOIN attributes a1 ON p.product_id=a1.property_id AND a1.attribute_id=1
INNER JOIN attributes a2 ON p.product_id=a2.property_id AND a2.attribute_id=3
INNER JOIN attributes a3 ON p.product_id=a3.property_id AND a3.attribute_id=7

И, учитывая, что если вам нужен attribute_id 3, вы хотите ИЛИ, что с attribute_id 7, и при условии, что вы также хотите attribute_id 1, который не является одним из этих специальных атрибутов:

SELECT ... FROM products AS p 
INNER JOIN attributes a1 ON p.product_id=a1.property_id AND a1.attribute_id=1
LEFT OUTER JOIN attributes a2 ON p.product_id=a2.property_id AND a2.attribute_id=3
LEFT OUTER JOIN attributes a3 ON p.product_id=a3.property_id AND a3.attribute_id=7
WHERE a2.attribute_id IS NOT NULL OR a3.attribute_id IS NOT NULL

Я подозреваю, что любой из них будет намного быстрее, чем оригиналы с отдельной / имеющей / группируемой по совокупности операциями. Таблица атрибутов должна иметь многоколонный уникальный индекс либо (property_id, attribute_id), либо (attribute_id, property_id), хотя я предполагаю, что property_id более избирателен и поэтому должен быть самым левым столбцом в индексе.

1 голос
/ 06 декабря 2008

Я думаю, это выглядит уже неплохо. Помимо обязательного упоминания "не делать выбор *", мне кажется, это нормально.

Мой совет: если это работает и не вызывает проблем с производительностью, оставьте это и тратьте свое время на что-то другое. Пересмотрите его в будущем, если возникнет проблема.

0 голосов
/ 12 декабря 2008

как вы можете выбрать стр. * И сгруппировать только по 1 столбцу? Или это работает с первичным ключом?

ГДЕ a1.attribute_id IN (1,3) ИЛИ a1.attribute_id IN (1,7)

совпадает с

ГДЕ a1.attribute_id IN (1,3,7)

SELECT p.* FROM products  
INNER JOIN (
    SELECT a1.property_id  
    FROM attributes a1 
    WHERE a1.attribute_id IN (1,3,7)
    GROUP BY a1.property_id 
    HAVING COUNT(DISTINCT a1.attribute_id) = 2
) as a ON p.product_id=a.property_id
0 голосов
/ 06 декабря 2008

При правильных (и очевидных) индексах это будет очень эффективно в MySQL.

ВЫБРАТЬ ...

ИЗ ПРОДУКТОВ AS p

Атрибуты INNER JOIN a1 ON p.product_id = a1.property_id AND a1.attribute_id = 1
Атрибуты левого соединения a2 ON p.product_id = a2.property_id AND a2.attribute_id = 3
Атрибуты LEFT JOIN a3 ON p.product_id = a3.property_id AND a3.attribute_id = 7

ГДЕ (
Случай, когда a1.product_attribute_id равен NULL THEN 0 ELSE 1 END
+ СЛУЧАЙ, КОГДА a1.product_attribute_id НУЛЯЕТСЯ ТОЛЬКО 0, В противном случае 1 КОНЕЦ
)> 0

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