|
|
|
| Дело в том, что поле типа SET - это по сути целочисленное поле с "именованными" битами, так же как и ENUM, но ENUM с именованными значениями.
Соответственно, если, например, есть поле fld типа ENUM('яблоко','помидор','виноград'), то поиск:
SELECT * FROM tbl WHERE fld='помидор'
| идентичен поиску
SELECT * FROM tbl WHERE fld=2
| , как по результату, так и по реализации. Т.е. оптимизатор приводит 'помидор' к числу 2 и поиск производится целочисленно.
А вот если есть поле fld типа SET('яблоко','помидор','виноград'), то чтобы найти строки в которых отмечено и яблоко и виноград, я знаю только один способ:
SELECT * FROM tbl WHERE fld LIKE '%яблоко%виноград%'
| и я сильно сомневаюсь, что оптимизатор поймет по этой записи, что я хочу и будет производить целочисленный поиск.
Кто-нибудь знает, как это сделать правильно??? | |
|
|
|
|
|
|
|
для: Sfinks
(01.11.2012 в 21:42)
| | Почитал, поискал, потестил..... Может кому пригодится.
В общем есть несколько способов выборки - с помошью побитовых операций, с помошью LIKE и с помошью функции FIND_IN_SET().
Допустим, у нас есть поле dogovor_status типа SET( 'аванс', 'заказано', 'получено', 'оплачено', 'доставлено' )
Для побитовых операций 'аванс' будет 00001=1, 'заказано' - 00010=2, 'получено' - 00100=4, 'оплачено' - 01000=8 и 'доставлено' - 10000=16
В таблице, на которой проводились тесты - 207 000 записей.
Каждая выборка запускалась по 5 раз.
Время принято среднее из 5ти попыток.
Сперва тестирую логику AND.
Допустим нам нужно выбрать все поля, где в статусе заказа присутствуют и 'аванс' и 'получено' и 'оплачено'.
Для побитовых операций это будет - 01101=13 (или из написанного выше - 8+4+1)
SELECT * FROM tbl WHERE dogovor_status =13
89.852 милисекунды
-------------
SELECT * FROM tbl WHERE dogovor_status LIKE '%аванс%получено%оплачено%'
101.806 милисекунд
-------------
SELECT * FROM tbl WHERE dogovor_status &1 && dogovor_status &4 && dogovor_status &8
93,24 милисекунды
-------------
SELECT * FROM tbl WHERE dogovor_status find_in_set( 'аванс', dogovor_status ) && find_in_set( 'получено', dogovor_status ) && find_in_set( 'оплачено', dogovor_status )
87,127 милисекунд
|
Тут результат налицо - И по времени и по удобству использования на 1ом месте последний вариант.
С логикой OR немного иначе.
SELECT * FROM tbl WHERE dogovor_status &13
87.702 милисекунды
-------------
SELECT * FROM tbl WHERE dogovor_status LIKE '%аванс%' OR dogovor_status LIKE '%получено%' OR dogovor_status LIKE '%оплачено%'
149.84 милисекунды
-------------
SELECT * FROM tbl WHERE dogovor_status &1 || dogovor_status &4 || dogovor_status &8
107.421 милисекунды
-------------
SELECT * FROM tbl WHERE find_in_set( 'аванс', dogovor_status ) || find_in_set( 'получено', dogovor_status ) || find_in_set( 'оплачено', dogovor_status )
100.904 милисекунд
| Тут удобный вариант хотя и на втором месте, но стоит ли оно того?
Дело в том еще, что если в определение поля добавить новое значение, и не в конец списка, а в середине, то побитовые выборки собьются. Но и строковые собьются, если какое-нибудь значение переименовать.
В общем как-то так.
Надеюсь, кому-нибудь это пригодится!
А я наверно остановлюсь на самом коротком способе. | |
|
|
|
|
|
|
|
для: Sfinks
(02.11.2012 в 00:16)
| | Наконец придумал (хотя наверняка сейчас кто-нибудь скажет что это давно придумано) "гибридный" способ, который и нагляден для человека, и быстр для SQL и удобен при изменениях значений статусов в структуре БД =)))
Прописываем в файле config.php константы для статусов:
<?php
define( '_avans' , 1 );
define( '_zakazano' , 2 );
define( '_polucheno' , 4 );
define( '_oplacheno' , 8 );
define( '_dostavleno' , 16 );
|
И потом везде пользуемся в таком виде:
<?php
$status = _avans + _oplacheno;
$sql = 'SELECT * FROM tbl WHERE dogovor_status &' . $status;
mysql_query($sql);
|
Для себя - буквы, для мускула - цифры, и если структура БД изменится, достаточно в config.php поправить значения =)))))
_______________
P.S.
А клевая записная книжка.... Написал и в закладки! И ни полетевшая винда, ни сгоревший винт не страшны =)
А то я уж обжигался. | |
|
|
|
|