Модераторы: skyboy

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> [mysql] Найти все подходящие варианты по критерию, найти по условию все id в одной таблице 
:(
    Опции темы
linuxoid
  Дата 27.10.2012, 22:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 17.4.2005

Репутация: нет
Всего: нет



Здравствуйте, уважаемые коллеги!

Помогите, пожалуйста, вот по какому вопросу:

Есть некая таблица (MySql), к примеру:

Цитата

id  product_id  attribute_id
1  1  2
2  1  3
3  1  7
4  1  11
5  1  15
7  1  25
8  2  1
9  2  8
10  2  12
11  2  15
12  2  17
13  2  20
14  3  4
15  3  1
16  3  5
17  3  10
18  3  16
19  3  18
20  3  24
21  4  3
22  4  2
23  4  4
24  4  6
25  4  11
26  4  13
27  4  17
28  4  26


Как наиболее эффективно вывести список тех product_id, где к примеру (attribute_id = 1 OR attribute_id = 4) AND (attribute_id = 17)

Пояснение: в данном примере по условию выше подходит product_id=2 и product_id=4, 
т.к. у product_id=2 есть attribute_id=1 и attribute_id=17 (т.е. условие, когда должны быть атрибуты ((1 и/или 4) И 17 выполнены).
Так же подходит и product_id=4, где выполнены условия (есть и (1 и/или 4) И есть атрибут с айди 17).

Один (очень медленный) вариант найти эти product_id так:

SELECT DISTINCT article_id FROM article_attributes
WHERE (article_id) IN
(SELECT article_id FROM article_attributes WHERE attribute_id = 1 OR attribute_id = 4)
AND article_id IN (SELECT article_id FROM article_attributes WHERE attribute_id = 17);

Но может быть есть более эффективный и быстрый способ? 

Это сообщение отредактировал(а) linuxoid - 27.10.2012, 23:59
PM MAIL   Вверх
Akina
Дата 28.10.2012, 21:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 45
Всего: 454



Код

select product_id
from article_attributes
group by product_id
having 3=sum(case attribute_id when 1 then 1 when 4 then 1 when 17 then 2 else 0 end);



--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
FINANSIST
Дата 29.10.2012, 08:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Статус: Жив
**


Профиль
Группа: Участник
Сообщений: 526
Регистрация: 11.4.2008
Где: Москва

Репутация: нет
Всего: 23



Код

select a1.product_id
from
(SELECT distinct product_id  FROM article_attributes WHERE attribute_id = 17) a1
inner join
(SELECT distinct product_id  FROM article_attributes WHERE attribute_id in (1,4) a2
on a1.product_id=a2.product_id

Не знаю, по скорости у Акины может лучше будет, но как вариант...

Это сообщение отредактировал(а) FINANSIST - 29.10.2012, 08:39


--------------------
“...Брали корову рыжую одну, отдавать будем корову рыжую одну, чтобы не нарушать отчетности”
Эдуард Успенский, “Каникулы в Простоквашино”
PM MAIL ICQ   Вверх
Akina
Дата 29.10.2012, 08:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 45
Всего: 454



Цитата(FINANSIST @  29.10.2012,  09:36 Найти цитируемый пост)
по скорости у Акины может лучше будет...

При правильном индексе и сравнительно высокой селективности по нему - твой вариант быстрее. Но мой универсальнее.


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
Zloxa
Дата 29.10.2012, 09:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 53
Всего: 161



Цитата(Akina @  29.10.2012,  09:40 Найти цитируемый пост)
При правильном индексе и сравнительно высокой селективности по нему - твой вариант быстрее.

При этих условиях и авторский вариант весьма не плох вроде как. 
Единственно, я бы попробовал один из двух in, с наибольшей селективностью заменить на кореллированный exists.


А так вобще... тема - прекрасный пример, показывающий истинную цену кажущейся простоты EAV, UDA

Это сообщение отредактировал(а) Zloxa - 30.10.2012, 10:57


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
linuxoid
Дата 29.10.2012, 21:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 17.4.2005

Репутация: нет
Всего: нет



Коллеги! Спасибо за предложения. К сожалению вариант Akina не работает, а FINANSIST (пробовал без индекса и с индексом на product_id + attribute_id) срабатывает примерно за 59 секунд, в то время как мой вариант отрабатывает макимум за 1 секунду (60000 продуктов, в таблице всего 630685 записей). 

Цитата

SELECT count(DISTINCT product_id) FROM article_attributes
WHERE (product_id) IN
(SELECT product_id FROM article_attributes WHERE attribute_id = 1 OR attribute_id = 4)
AND product_id IN (SELECT product_id FROM article_attributes WHERE attribute_id = 17);


Но это очень долго, т.к. к примеру есть 60 категорий, для которых надо посчитать кол-во товара с учетом входящих в фильтр атрибутов (к примеру "(красный ИЛИ зеленый) И летний", т.е. по аналогии с (1 OR 4) AND 17)

У мужика одного есть очень грамотная статья:

http://explainextended.com/2010/04/02/mult...-vs-not-exists/

Он там приводит пример скоростного запроса, но у него идет как бы рейндж для option1 (к примеру - цвет, от белого до пурпурного), рейндж для option2 (к примеру размер - от S до XXL), рейндж для option3 (к примеру сезон - от летнего до зимнего). Проблема в том, что я не знаю как можно для optionX указать несколько рейнджей (например ЦВЕТ от белого до красного ИЛИ от зеленого до пурпурного, при этом промежуточные цвета пропускаем/не учитываем, т.е. в данной концепции это можно считать как несколько рейнджей). Точнее я знаю, как это сделать для варианта GROUP BY, который он приводит в своей статье, но не понимаю, как сделать несколько рейнджей в его самом оптимальном запросе. Коллеги, может быть кому то будет интересно посмотреть статью, взгляните, пожалуйста. Я там в комментариях задал вопрос (phpcoder) на эту тему, но пока автор не ответил. В целом, в случае с GROUP BY, который в статье рассматривается, лично у меня запрос тоже работает 1 секунду, а это слишком долго :( Хотя в его примере запрос выполняется за 320 ms, но у него поменьше продуктов.
P.S. индекс я создал у себя такой 

Код

CREATE UNIQUE INDEX index_article_attributes
ON article_attributes ( product_id, attribute_id, attribute_value);        


Т.е., к примеру есть такая таблица

Цитата

product_id  attribute_id  attribute_value
1  1  2
1  1  1
1  1  4
1  1  3
1  2  8
1  3  10
1  4  15
1  4  14
1  4  13
1  5  17
1  6  21
2  1  3
2  1  4
2  1  1
2  1  2
2  2  8
2  2  7
2  2  5
2  3  12
2  4  14
2  5  17
2  6  23
3  1  1
3  1  4
3  1  3
3  2  8
3  3  12
3  4  16
3  4  15
3  4  14
3  4  13
3  5  18
3  6  28
4  1  1
4  1  3
4  2  5
4  2  8
4  2  7
4  3  12
4  4  14
4  4  13
4  4  16
4  4  15
4  5  17
4  6  22


Тогда можем выбрать продукт, у которого (цвет зеленый (1, option=1) ИЛИ цвет красный (3, option=1)) И размер = XL (17, option=5) И сезон летний (22, option=6)

Код

-- AND + OR
SELECT  COUNT(*)
FROM    (
            SELECT  product_id
            FROM    (
                        SELECT  product_id
                        FROM    (
                                SELECT  1 AS opt, 1 AS l, 1 AS h
                                UNION ALL
                                SELECT  1 AS opt, 3 AS l, 3 AS h
                                UNION ALL
                                SELECT  5 AS opt, 17 AS l, 17 AS h
                                UNION ALL
                                SELECT  6 AS opt, 22 AS l, 22 AS h
                                ) v
                        JOIN    article_attributes o
                        ON      o.attribute_id >= opt
                                AND o.attribute_id <= opt
                                AND o.attribute_value BETWEEN l AND h
                        GROUP BY product_id, attribute_id
                    ) o
            GROUP BY
                    o.product_id
            HAVING  COUNT(*) = 3
        ) q;


Данный запрос работает так же 1 секунду на 60000 продуктов (в таблице всего 630685 записей). Но у автора статьи есть более оптимальный вариант, но как прикрутить туда вариант типа (1 ИЛИ 3) И 17 И 22 я не знаю. На данный момент тот запрос сделан так, что можем выбрать ОТ 1 ДО 3 И ОТ 17 ДО 17 И ОТ 22 ДО 22, но не 1 ИЛИ 3 И ...

Это сообщение отредактировал(а) linuxoid - 29.10.2012, 21:58
PM MAIL   Вверх
Zloxa
Дата 30.10.2012, 00:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 53
Всего: 161



Цитата(linuxoid @  29.10.2012,  22:26 Найти цитируемый пост)
 К сожалению вариант Akina не работает

А ваш "рабочий" запрос не от ваших исходных данных. article_id, используемый в нем не описан в описании, и не показан в исходном наборе данных


Цитата(linuxoid @  29.10.2012,  22:26 Найти цитируемый пост)
P.S. индекс я создал у себя такой 

этот индекс вашему запросу ни о чем.

Создайте такой (attribute_id,product_id)

и попробуйте так:
Код

select /*distinct*/ product_id 
from article_attributes as aa
WHERE attribute_id = 17
and (exists ( select null from article_attributes as aa1 where aa1.attribute_id = 4 and aa1.product_id = aa.product_id)
        or 
        exists ( select null from article_attributes as aa2 where aa2.attribute_id = 1 and aa2.product_id = aa.product_id)
      )


Если вдруг окажется что запрос не использует индекса, попробуйте его зафорсить.

Совершенно не понятно нужен тут дистинкт или нет, как правило, при подобной организации пара  (attribute_id,product_id) уникальна и она таки выглядит таковой в вашем первом примере исходных данных. Однако во втором примере, все совсем по другому. Наверное опять пример не от тех данных не к тому запросу.

Это сообщение отредактировал(а) Zloxa - 30.10.2012, 00:46


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
linuxoid
Дата 30.10.2012, 01:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 17.4.2005

Репутация: нет
Всего: нет



Zloxa, виноват! Во втором примере просто добавился еще 1 колумн, но он теперь там называется "attribute_id", а бывший колумн теперь называется "attribute_value" что вводит в заблуждение. Это по аналогии EAV (entity, attribute, value), где во втором моем примере в роли attribute (он же option) выступает attribute_id (к примеру ЦВЕТ), а его значение, value (ЗЕЛЕНЫЙ, КРАСНЫЙ) - это attribute_value (бывший attribute_id из 1го примера). 

То что ты предложил меня впечатляет. Теперь запрос по тем же 60000 работает за в среднем 0.75 сек, что на данный момент является самым скоростным решением из тех, что я опробовал. И да - насчет первого примера (и во втором это тоже справедливо) - "Совершенно не понятно нужен тут дистинкт или нет, как правило, при подобной организации пара  (attribute_id,product_id) уникальна и она таки выглядит таковой в вашем первом примере исходных данных." - действительно не нужен, т.к. я изначально задумал, чтобы к примеру ЦВЕТ и РАЗМЕР не пересекались (чтобы можно было не использовать таблицу, как во втором примере).

Спасибо за квери!

Насчет скорости 0.75 сек - это предел?

И еще 1 нюанс - если будем использовать не 2 разных критерия (ЦВЕТ(1,4), РАЗМЕР(17)), а скажем 6, то и скорость квери значительно упадет? 

Типа:

Код

select /*distinct*/ product_id 
from article_attributes as aa
WHERE attribute_id = 17
and (exists ( select null from article_attributes as aa1 where aa1.attribute_id = 4 and aa1.product_id = aa.product_id)
        or 
        exists ( select null from article_attributes as aa2 where aa2.attribute_id = 1 and aa2.product_id = aa.product_id)
      )
AND (exists (select null from article_attributes as aa1 where aa1.attribute_id = 22 and aa1.product_id = aa.product_id))


В данном случае уже 0.87 сек. (добавили еще 1 фильтр)

Это сообщение отредактировал(а) linuxoid - 30.10.2012, 01:53
PM MAIL   Вверх
Zloxa
Дата 30.10.2012, 09:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 53
Всего: 161



Цитата(linuxoid @  30.10.2012,  02:41 Найти цитируемый пост)
это предел?

Запрос будет тем более производителен, чем более селективен предикат отбора по которому осуществляется индексный доступ и тем менее, чем этот предикат менее селективен.
Цитата(linuxoid @  30.10.2012,  02:41 Найти цитируемый пост)
добавили еще 1 фильтр

нет, это назвается не фильтр, это называется semi-join
добавление фильтра на производительности отразилось бы не столь существенно.
Цитата(linuxoid @  30.10.2012,  02:41 Найти цитируемый пост)
если будем использовать не 2 разных критерия (ЦВЕТ(1,4), РАЗМЕР(17)), а скажем 6, то и скорость квери значительно упадет? 

да, ибо это EAV - worse design, через который таки надо пройти

Это сообщение отредактировал(а) Zloxa - 30.10.2012, 09:33


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
linuxoid
Дата 30.10.2012, 10:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 17.4.2005

Репутация: нет
Всего: нет



Zloxa, я тебя понял. Посоветуй, пожалуйста, какие есть ПРАВИЛЬНЫЕ на твой взгляд варианты решения проблемы по фильтру товаров? (в рамках MySQL, т.к. по идее можно воспользоваться NoSQL, где хороша реализация задачи + скорость - проверено, просто хотелось бы всё оставить в MySQL).

Это сообщение отредактировал(а) linuxoid - 30.10.2012, 10:40
PM MAIL   Вверх
Zloxa
Дата 30.10.2012, 10:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 53
Всего: 161



Классикой. 
Провести анализ предметной области, систематизировать критерии отбора, наделить сущность товаров необходимым количеством аттрибутов, представить в виде таблицы, индексировать. Атрибуты, которые не задействованы в логике приложения, которые нужны только как доп информация на презентативном слое, вполне можно сохранить и в UDA(User Defined Attribute - вырожденный случай EAV, без Entity). Можно так же вынести в UDA атрибуты, по которым не предполагается фильтрация в совокупности с другими критериями по И, лишь по ИЛИ, но только лишь в случае, если этот атрибут используется только в фильтрации, никак не в логике, ибо тогда придется хардкодиться на код атрибута, что плохая практика.

Не гонитесь за универасльностью решения. Универсальность далеко не всегда нужна и имеет более высокую цену. Автомобиль-амфибия, хоть умеет и ездить и плавать, но и то и другое делает плохо. Хуже чем автомобиль, хуже чем катер. В велосипедостроении же гибкость не нужна. Велосипед должен быть надежен, прост в изготовлении, прост в обслуживании.

Это сообщение отредактировал(а) Zloxa - 30.10.2012, 11:48


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
linuxoid
Дата 30.10.2012, 11:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 17.4.2005

Репутация: нет
Всего: нет



Так то ж уже есть. Есть продукция, есть у неё атрибуты (цвет, размер, сезон и т.д.). Все это УЖЕ представлено в виде таблицы (т.е. product_id, option_name, option_value, к примеру одна из записей - продукт с айди 1 имеет (option_id=1)ЦВЕТ 3(option_value=3), имеет (option_id=1)ЦВЕТ 6(option_value=6)). Т.е. это таблица, в которой мы привязываем к артикулам значения различных опций (цвет, размер и т.д.). 

Сложность в том, что продуктов много, опций тоже много. Пользователю надо это как-то фильтровать. Типичный случай к примеру - пользователь выбрал цвет - КРАСНЫЙ, ЗЕЛЕНЫЙ, указал размер и категорию - к примеру платья. Тогда мы ему покажем, что в ПЛАТЬЯх есть кол-во X КРАСНЫХ или ЗЕЛЕНЫХ товаров с размером XL (к примеру, с учетом фильтра -> Платья (2637) <- count)

Уже много статей нашел, где товарищи рекомендуют подключить Sphinx, но это уже накладный солюшн.. В любом случае, Zloxa, спасибо за комментарии!
PM MAIL   Вверх
Zloxa
Дата 30.10.2012, 12:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Чо?
****


Профиль
Группа: Завсегдатай
Сообщений: 3473
Регистрация: 12.9.2008

Репутация: 53
Всего: 161



Цитата(linuxoid @  30.10.2012,  12:58 Найти цитируемый пост)
 Все это УЖЕ представлено в виде таблицы(т.е. product_id, option_name, option_value

Все это уже представлено так, чтобы услжнить а не упростить отбор smile

Чем обусловлен отказ от классического ER представления (product_id, name,color,size,weight) ?

Добавлено @ 12:26
Цитата(linuxoid @  30.10.2012,  12:58 Найти цитируемый пост)
Сложность в том, что продуктов много, опций тоже много.

Да, структуировать данные - основная работа.
Опустив ее, отдав на откуп структуре EAV, мол если вдруг чо - допилим, докрутим побыдному, мы не упрощаем, а усложняем себе жизнь.

Это сообщение отредактировал(а) Zloxa - 30.10.2012, 12:31


--------------------
Достоверно известно, что 89% людей доверяют статистике взятой с потолка smile
PM   Вверх
linuxoid
Дата 30.10.2012, 12:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 180
Регистрация: 17.4.2005

Репутация: нет
Всего: нет



Zloxa, да, проще, чем уже просто видимо не получится. Буду исправляться. Спасибо.

Единственное, что смущает, если у продукта 25 цветов, 40 рамзеров, 2 сезона, 6 материалов и .т., то для 1 продукта получаем как минимимум 25 * 40 * 2 * 6 = 12000 записей.. не лучший вариант конечно. Т.е. в худшем (а может это и не худший) случае это, скажем 720 000 000 записей на 60 000 продуктов. Я правильно понимаю?

Это сообщение отредактировал(а) linuxoid - 30.10.2012, 13:45
PM MAIL   Вверх
Akina
Дата 30.10.2012, 14:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Советчик
****


Профиль
Группа: Модератор
Сообщений: 20581
Регистрация: 8.4.2004
Где: Зеленоград

Репутация: 45
Всего: 454



Цитата(linuxoid @  30.10.2012,  13:42 Найти цитируемый пост)
для 1 продукта получаем как минимимум 25 * 40 * 2 * 6 = 12000 записей

Как максимум. А на самом деле гораздо меньше, даже не в разы - на порядки.


--------------------
 О(б)суждение моих действий - в соответствующей теме, пожалуйста. Или в РМ. И высшая инстанция - Администрация форума.

PM MAIL WWW ICQ Jabber   Вверх
Ответ в темуСоздание новой темы Создание опроса
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Составление SQL-запросов | Следующая тема »


 




[ Время генерации скрипта: 0.0730 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.