Джентльмены, у меня дилемма.
Реорганизую таблицу в БД, в этой таблице есть столбец (далее services) который используется для хранения статуса объекта в каких-то категориях. Статус это цифра от 0 до 3. Количество категорий может меняться (сейчас их 11).
То есть, для примера:
Объект 1:
Категория 1 - Статус 3
Категория 2 - Статус 1
Категория 3 - Статус 2
...
Объект 2:
Категория 1 - Статус 1
Категория 2 - Статус 3
Категория 3 - Статус 2
...
На данный момент для хранения статусов используется VARCHAR. То есть, просто 11 символов, каждый из которых представляет статус категории с номером равным позиции этого символа.
...
(1, '31200000000'),
(2, '13200000000')
...
Очень часто приходится проверять статус категории. Для поиска используется LIKE.
Есть мысль хранить эти статусы в BIGINT используя четырехричную систему счисления. Иными словами, каждый разряд четырехричного числа будет хранить статус категории.
...
(1, 39), #3910 = 2134
(2, 45) #4510 = 2314
...
Для определения статуса будет использоваться формула:
services mod 4service + 1 div 4service
Где service это индекс категории, отчет с нуля.
Я провел небольшой тест, на идентичных таблицах из 13 тысяч записей. Используя следующие запросы:
/* Запрос 1 */ SELECT * FROM `test_b` WHERE `services` LIKE '___2%';
/* Запрос 2 */ SELECT * FROM `test_a` WHERE (`services` MOD 256) DIV 64 = 2;
Вот результаты для 10 000 выборок:# | Запрос 1 | Запрос 2 |
---|
01 | 28.21с | 27.31с |
02 | 27.18с | 27.26с |
03 | 28.31с | 27.56с |
04 | 29.25с | 27.14с |
05 | 29.18с | 27.47с |
06 | 27.60c | 27.47c |
07 | 27.74c | 26.79c |
08 | 27.43c | 26.95c |
09 | 27.99c | 26.52c |
10 | 28.80c | 28.06c |
Судя по этим тестам вариант с BIGINT выигрывает 3.2% в скорости. Могу предположить, что при увеличении объемов таблицы, прирост будет только увеличиваться.
Технология тестовЗамерялось все вот этим php кодом:
$time_taken = microtime(true);
for($i = 0; $i < 10000; $i++)
{
mysql_query("Запрос");
}
$time_taken = microtime(true) - $time_taken;
Так же, для удобства, была написана хранимая функция:
DROP FUNCTION IF EXISTS `GET_SERVICE_STATE`;
DELIMITER //
CREATE FUNCTION `GET_SERVICE_STATE`(`services` BIGINT, `service` BIGINT)
RETURNS TINYINT
BEGIN
RETURN (`services` MOD (`service` * 4)) DIV `service`;
END //
DELIMITER ;
Но результаты ее использования оказалось плачевны.
SELECT * FROM `test_a` WHERE GET_SERVICE_STATE(`services`, 64) = 2;
Работает в ~20 раз медленнее исходного запроса. Не могу понять, это из за дополнительного умножения или из за самого вызова функции?
Для себя я выявил следующие плюсы и минусы способа с BIGINT над способом с VARCHAR:
+ Экономия 3 байт на каждую запись в таблице
+ Прирост скорости, хоть и не значительный
+ Нет необходимости изменять структуру таблицы при увеличении количества категорий
- Ограничение на количество категорий - 31 штука
- В самой БД визуально не видно состояния категорий
Пока что, я склоняюсь в сторону способа с BIGINT. Пожалуйста, помогите сделать правильный выбор. Или подскажите способ более эффективный.