Поиск по 20 критериям

pavlodaranet

Новичок
Поиск по 20 критериям

Упражняюсь с php. Была поставлена задача сделать поиск как на одном из развлекательных сайтов.
Смысл такой: необходимо выбрать заведение, в котором присутствуют определенные опции. Пример
Всего их порядка 20. Как быть?

Таблица опций реализована следующим путем:

Id | pid | pizza | karaoke ....
1 6 0 1

1 - присутствие опции
0 - отсутствие

Для любителей отправлять в поиск: только что оттуда. М.б. искал плохо - не спорю, так что если не затруднит дайте линк.
 

Alexandre

PHPПенсионер
делал похожий поиск 6 лет назад, пришлось повозится

используй битмаску
твоя структура не правильная.
должно быть так:
Заведение | mask

mask = BAR | LiveMuzik ( бар с живой бузыкой ) 1+3
mask = Pizza ( Пицерия ) = 4

BAR = 1
LiveMuzik = 2
Pizza = 4
Bliny = 8
etc...
 

dimagolov

Новичок
Alexandre, разве? А как же индексы, атомарность с конце концов? Поиск-то по битовой маске будет с перебором всех строк. Или есть фокус?

-~{}~ 23.10.09 08:29:

и не проще ли сделать справочник фич и таблицу связей многие-к-многим и в нем отбирать нужные записи по двум индексам?
 

Alexandre

PHPПенсионер
индекс на маску не наложить что-ли?

если уж затронули тему про индексы, то известный факт: если наложить индекс на бинарное поле пол (м\ж) то не эффективен.
очевидно то же самое и в этом случае, спасают комбинированные индексы - так чем они отличаются от индекса от маски???

хотя все определяет explain и тесты.
 

Вурдалак

Продвинутый новичок
pavlodaranet, структура должна быть такая:
[sql]CREATE TABLE `table` (
`id` INT NOT NULL ,
`bitfield` INT NOT NULL
);[/sql]
В PHP-коде должно быть следующее:
PHP:
<?php

define( '_PIZZA',    0x000001 );
define( '_KARAOKE',  0x000002 );
# ...

?>
Каждое из чисел (0x000001, 0x000002, 0x000004, ...) просто степень двойки. С помощью калькулятора Windows вычислишь.

Далее, перед поиском составляешь эту самую маску, где перечисляешь все необходимые сервисы:
PHP:
<?php
$m = _PIZZA | _KARAOKE | ...;

$query = "SELECT ... WHERE `bitfield` & {$m} <> 0;";
Ещё подробнее: ищи "битовое поле"
 

Alexandre

PHPПенсионер
то можно поподробнее
на каждое заведение заводится поле маска
маска представляет собой битовое число:
в твоем случае pid | pizza | karaoke | sportbar | milkbar
pid=1
pizza =2
karaoke=4
sportbar = 8
milkbar = 16
и тд
лучше сделать разные группы (две маски)
по кухне и типу бара... или услугам...

определяем константу sportbar = 8
ищем все заведения в которых третий бит (8) = 1
SELECT * FROM .. WHERE ( maska & 8 =8 )

-~{}~ 23.10.09 17:37:

уже ответили :)
 

Вурдалак

Продвинутый новичок
> pid=1

По-моему, pid не сервис (из первого поста), не запутывай человека :)
 

dimagolov

Новичок
индекс на маску не наложить что-ли?
как индекс на маску наложить? ведь чтобы узнать fld & mask нужно произвести эту операцию для каждой строки, explain подобного по индексированному полю показывает ALL / no keys / all rows / Using Where

или все еще чего-то не понимаю?
 

MiksIr

miksir@home:~$
Для поиска по полям с битовыми флагами нужен bitmap индекс. Если не ошибаюсь, в MySQL его нет, так что перебором будет искать.

-~{}~ 23.10.09 18:40:

pavlodaranet, а база MySQL?
В постгресе можно было бы массивом... =)
 

dimagolov

Новичок
чем плохо решение с таблицей фил и таблицей связей заведение<->фичи?
 

MiksIr

miksir@home:~$
PS классическая схема создания таких справочников:
таблица N1 : справочник опций
id | name
1 | караоке
2 | пицца
3 | мордобой

таблица N2 : заведения
id | name
1 | Пельменная на ул.Водочная
2 | Водочная на ул.Пельменная

таблица N3 : какие опции есть
id_N2 | id_N1
1 | 1
1 | 2
2 | 3

-~{}~ 23.10.09 18:46:

Автор оригинала: dimagolov
чем плохо решение с таблицей фил и таблицей связей заведение<->фичи?
Да ничем кроме как лишней таблицей и, соответственно, джойном.
Вообще давно интересно померять скорости в постгресе: массивы vs джойн, но все никак не доходят руки =)
 

Макс

Старожил PHPClub
set это просто удобная обертка вокруг битовых флагов
при поиске по нему не будет использоваться индекс.
 

Beavis

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

dimagolov

Новичок
Beavis, а толку? ключи не используются при запросе, чисто синтаксический сахар, причем не особо удобный

-~{}~ 23.10.09 13:32:

пока проверял уже отписались
Beavis, дело не в костылях а в эфективности такого решения. в запросе что с set-ом, что без него наиболее удобно писать WHERE field & mask = mask
 

Beavis

Banned
dimagolov
Ну насчет удобства использования - это к разработчикам MySQL

Я привел ссылку чтоб подсказать ТС какого типа поле надо создать для использования битовых масок.. если ему удобнее INT - флаг в руки)) Мне например больше нравится SET, тем более что его можно использовать и не только как битовое, а просто как строку
 

Alexandre

PHPПенсионер
dimagolov
конечно бенчмарки рулят,
делаем твоим и моим способом и сравниваем.

накладываем индекс на поле инт
смотрим explain : SELECT * FROM ... WHERE ( mask & 8 = 8 )
 
Сверху