Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
Самоучитель PHP 5 / 6 (3 издание). Авторы: Кузнецов М.В., Симдянов И.В. PHP Puzzles. Авторы: Кузнецов М.В., Симдянов И.В. MySQL 5. В подлиннике. Авторы: Кузнецов М.В., Симдянов И.В. PHP. Практика создания Web-сайтов (второе издание). Авторы: Кузнецов М.В., Симдянов И.В. MySQL на примерах. Авторы: Кузнецов М.В., Симдянов И.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум MySQL

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: Как работать с полем типа SET?
 
 автор: Sfinks   (01.11.2012 в 21:42)   письмо автору
 
 

Дело в том, что поле типа 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   (02.11.2012 в 00:16)   письмо автору
 
   для: 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   (07.11.2012 в 00:17)   письмо автору
 
   для: Sfinks   (02.11.2012 в 00:16)
 

Наконец придумал (хотя наверняка сейчас кто-нибудь скажет что это давно придумано) "гибридный" способ, который и нагляден для человека, и быстр для SQL и удобен при изменениях значений статусов в структуре БД =)))

Прописываем в файле config.php константы для статусов:
<?php
  define
'_avans'      );
  
define'_zakazano'   );
  
define'_polucheno'  );
  
define'_oplacheno'  );
  
define'_dostavleno' 16 );

И потом везде пользуемся в таком виде:
<?php
  $status 
_avans _oplacheno;
  
$sql 'SELECT * FROM tbl WHERE dogovor_status &' $status;
  
mysql_query($sql);

Для себя - буквы, для мускула - цифры, и если структура БД изменится, достаточно в config.php поправить значения =)))))
_______________
P.S.
А клевая записная книжка.... Написал и в закладки! И ни полетевшая винда, ни сгоревший винт не страшны =)
А то я уж обжигался.

  Ответить  
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования