Нужна помощь в реализации подбора по параметрам(+)

Sherman

Mephi
Нужна помощь в реализации подбора по параметрам(+)

У меня есть таблицы.

Код:
category(catID)
item(itmID, catID)
parameter(parID, catID)
item_parameter(itmID, parID, itm_par_VALUE)
Задача сделать подбор по параметрам.

я делаю так:

т.к. каждая позиция имеет n-параметров, значит для того чтобы выбрать позицию по условию AND для n-параметров нужно n-раз объединить саму на себя таблицу item_parameter. это решение весьма медленно, т.к. уже на 8-9 объединения запрос выполняется около секунды:(

может быть есть иные решения(mysql 3.23.58).

пример запроса:

Код:
SELECT * 
FROM item_parameter
as ip
inner join item_parameter
as ip2
on ip.itm_ID = ip2.itm_ID
inner join item_parameter
as ip3
on ip.itm_ID = ip3.itm_ID
inner join item_parameter
as ip4
on ip.itm_ID = ip4.itm_ID
inner join item_parameter
as ip5
on ip.itm_ID = ip5.itm_ID
inner join item_parameter
as ip6
on ip.itm_ID = ip6.itm_ID
inner join item_parameter
as ip7
on ip.itm_ID = ip7.itm_ID
inner join item_parameter
as ip8
on ip.itm_ID = ip8.itm_ID
inner join item_parameter
as ip9
on ip.itm_ID = ip9.itm_ID
inner join item
as i
on ip.itm_ID = i.itm_ID
inner join categories
as c
on i.cat_ID = c.cat_ID
where (
ip.par_ID = 3
and ip.itm_par_DESC > 50
)
and (
ip2.par_ID = 63
and ip2.itm_par_DESC = 'on'
)
and (
ip3.par_ID = 45
and ip3.itm_par_DESC = 'on'
)
and (
ip4.par_ID = 46
and ip4.itm_par_DESC = 'on'
)
and (
ip5.par_ID = 49
and ip5.itm_par_DESC = 'on'
)
and (
ip6.par_ID = 50
and ip6.itm_par_DESC = 'on'
)
and (
ip7.par_ID = 52
and ip7.itm_par_DESC = 'on'
)
and (
ip8.par_ID = 53
and ip8.itm_par_DESC = 'on'
)
and (
ip9.par_ID = 54
and ip9.itm_par_DESC = 'on'
)
and (
c.cat_LEFT
between 107
and 262
)
ORDER BY i.itm_ID
пояснения:

cat_LEFT - левый ключ(структура nested sets, тут значения не имеет, т.к. это просто ограничивает категории, в которых ведется поиск).

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

-~{}~ 19.04.05 13:54:

p.s. чудеса происходят. только что снова прогнал тест. результат примерно: 0.01 - 0.02:)

p.p.s. также заменил условие «= 'on'» на «is not null».

есть конечно варианты сделать часть работы на клиенте или даже закешировать что-то, но хотелось бы одним запросом, т.к. это менее геморно:)

-~{}~ 20.04.05 11:56:

неужели никто не сталкивался с подобной задачей?
 

fixxxer

К.О.
Партнер клуба
> каждая позиция имеет n-параметров

1) какое максимальное значение n?
2) какие возможны значения параметров? много ли параметров вида true/false?
 

Sherman

Mephi
1. значение n неограниченно теоритически(практически это примерно 30-100).

2. параметры для подбора(большинство: да или нет). как реализовано условие, что позиция поддерживает параметр:

наличие записи(itm_ID, par_ID...) в таблице item_parameter говорит о том, что позиция поддерживает параметр.
 

fixxxer

К.О.
Партнер клуба
ну тогда для начала возьмем упрощенную задачу.
1) есть 100 параметров вида да/нет
2) параметр может не поддерживаться, то есть на самом деле получаем троичную логику - true/false/NULL
3) на выходе надо получить набор записей, удовлетворяющих заданному набору вида P1=true and P2 = false and.. and Pn-1=true and Pn=false ?
 

alexhemp

Новичок
Sherman

Я делал такое, должно работать на 3.23.28, хоть и не тестил.

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

Детали:

1. Объединяем таблицу item с таблицей значений параметров

2. пишем в условии ограничения на значения параметров - в стиле
OR (item_parameter.PAR_ID=1 AND item_parameter.itm_par_VALUE IN( ...здесь список искомых значений... ))

и так для каждого параметра

потом группируем по item.itmID и ставим HAVING COUNT(item_parameter.parID)="количество выбранных для фильтра параметров"

Если параметр не важен в поиске - то его не нужно перечислять, он просто не входит в множество-условие.

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

Sherman

Mephi
Была идея хранить где-то маску из идентификаторов или битов, но она себя не опрадывает, т.к. при любом изменении(а это не только типичные операции: добавить, удалить, но и продвинутые операции изменения параметров: можно менять тип, например, изменить параметр для подбора на параметр-список) нужно будет перелопатить огромное кол-во данных.
Также если удаляем или изменяем позицию(например меняем родительскую категорию: а там уже другие параметры совершенно), то нужно будет менять маску опять.
А если удаляем или изменяем категорию?

Тут возникла другая идея.

1. Из всех параметров(выбранных) для побора пользователем, мы выбираем тот, который:

1. меньше всех встречается в таблице item_parameter.
2. удовлетворяет условиям поиска(т.е. принадлежит одной из категорий, в которых ведется подбор).

Далее где-то сохранем эти данных.

Затем выбираем все параметры для этих позиций и уже затем отсеиваем(по условию поддержки позиции всех требуемых параметров) нудные нам позиции.

Как это лучше реализовать технически(использовать временную таблицу, что переложить на клиентский код)?
 

fixxxer

К.О.
Партнер клуба
А, ясно. В таком случае - обязательно ли использовать именно MySQL?
Ибо хранимые процедуры тут напрашиваются.
 

Sherman

Mephi
Автор оригинала: alexhemp
Sherman

Я делал такое, должно работать на 3.23.28, хоть и не тестил.

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

Детали:

1. Объединяем таблицу item с таблицей значений параметров

2. пишем в условии ограничения на значения параметров - в стиле
OR (item_parameter.PAR_ID=1 AND item_parameter.itm_par_VALUE IN( ...здесь список искомых значений... ))

и так для каждого параметра

потом группируем по item.itmID и ставим HAVING COUNT(item_parameter.parID)="количество выбранных для фильтра параметров"

Если параметр не важен в поиске - то его не нужно перечислять, он просто не входит в множество-условие.

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

-~{}~ 20.04.05 13:26:

Автор оригинала: fixxxer
А, ясно. В таком случае - обязательно ли использовать именно MySQL?
Ибо хранимые процедуры тут напрашиваются.
К сожалению на хостинге больше ничего нет, а переезд не возможен. К тому же система уже написана на 90%(базовык лассы для работы с объектами, вся админка и т.д.) и уже частично функционирует, т.е. осталось сделать только «вывод»:)

-~{}~ 20.04.05 13:28:

примерный запрос:

Код:
SELECT * 
FROM item_parameter
as ip0
inner join item
as i
on ip0.itm_ID = i.itm_ID
inner join categories
as c
on i.cat_ID = c.cat_ID
where (
ip0.par_ID = 66
and ip0.itm_par_DESC
is 
not 
null 
)
or (
ip0.par_ID = 60
and ip0.itm_par_DESC
is 
not 
null 
)
and (
c.cat_LEFT
between 107
and 262
)
GROUP BY (
i.itm_ID
)
HAVING count( ip0.par_ID ) = 2
 

alexhemp

Новичок
Sherman

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

Вообще там нет ничего специфичного в этом запросе, для таких запросов в общем-то SQL и создавался...
 
Сверху