MySQL: оператор объединения против побитового оператора и его производительность - PullRequest
1 голос
/ 18 мая 2011

Есть ряд вопросов по этому вопросу, но мой более конкретно касается вопросов производительности.

Что касается объекта, я хочу отслеживать множество «атрибутов», каждый из которых имеет множестводискретные «значения» (каждый атрибут имеет от 3 до 16 действительных «значений».) Например, рассмотрите возможность отслеживания военного персонала.Атрибуты / значения могут быть (не настоящими, я их полностью составил):

атрибут: {значения}
languages_spoken: {english, spanish, russian, chinese, …. }<br> certificates: {infantry, airborne, pilot, tank_driver…..}<br> approved_equipment: {m4, rocket_launcher, shovel, super_secret_radio_thingy….}<br> approved_operations: {reconnaissance, logistics, invasion, cooking, ….}<br> awards_won: {medal_honor, purple_heart, ….}
… и т. Д.

Один из способов сделать это - так, как я хочу это сделать - это иметь таблицу персонала и таблицу атрибутов:

personnel table => [id, name, rank, address…..]<br> personnel_attributes table => [personnel_id, attribute_id, value_id]

вместе со связанными таблицами атрибутов и значений.

Таким образом, если pesonnel_id = 31415 утвержден для логистики, в таблице personal_attributes будет следующая запись:

personnel_id | attribute_id | value_id
31415 | 3 | 2

, где 3 = attribute_id для «одобренных операций» и 2 = value_id для «логистики» (извините, места для форматирования не выстроились.)

Затем поиск, чтобы найти весь персонал, говорящий по-английскиИЛИ испанский, И кто пехотный ИЛИ воздушно-десантный И может управлять лопатой ИЛИ super_secret_radio_thingy будет выглядеть примерно так:

SELECT t1.personnel_id FROM personnel_attributes t1, personnel_attributes t2, personnel_attributes t3<br> WHERE ((t1.attribute_id = 1 and t1.value_id = 1) OR (t1.attribute_id = 1 and t1.value_id = 2))<br> AND ((t2.attribute_id = 2 and t1.value_id = 1) OR (t2.attribute_id = 2 and t1.value_id = 2))<br> AND ((t3.attribute_id = 3 and t1.value_id = 3) OR (t3.attribute_id = 3 and t1.value_id = 4))<br> AND t2.personnel_id = t1.personnel_id<br> AND t3.personnel_id = t1.personnel_id;

Предполагая, что это не совсем глупый способ написания SQLвопрос, проблема в том, что это очень медленно (даже с кажущимсяСоответствующие индексы.)

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

SELECT personnel_id FROM personnel_attributes<br> WHERE language & b'00000011'<br> AND certificates & b'00000011'<br> AND approved_operations & b'00001100';

Я знаю, что при этом выполняется полное сканирование таблицы, но в моих экспериментах с 350 000 человек выборки и 16 атрибутами каждый первый метод занял 20 секунд.тогда как побитовый метод занял 38 миллисекунд!

Я что-то здесь не так делаю?Это ожидаемые результаты производительности?

Спасибо!

Ответы [ 2 ]

0 голосов
/ 23 февраля 2013

У меня та же проблема использования django-битового поля или отдельной таблицы для флагов.

Вдохновленный вашим экспериментом, я использовал 3,5-метровую таблицу записей (innodb), сделал count () и получил запросы для обоихварианты.Результат был поразительным: примерно 5 секунд против 40 секунд побед битового поля.

0 голосов
/ 19 мая 2011

Использование побитовой операции потребует оценки всех строк. Я полагаю, что ваша проблема может быть решена путем изменения исходного выражения SELECT и способа объединения таблиц:

Чтобы было немного проще читать, я изменил значения атрибутов на слова вместо целых, чтобы было проще запутаться при чтении моего примера, но, очевидно, вы можете оставить их как целые числа, и концепция все равно будет работать:

CREATE TABLE PERSONNEL (
    ID INT,
    NAME VARCHAR(20)
)

CREATE TABLE PERSONNEL_ATTRIBUTES (
    PERSONNEL_ID INT,
    ATTRIB_ID INT,
    ATTRIB_VALUE VARCHAR(20)
)

INSERT INTO PERSONNEL VALUES (1, 'JIM SMITH')
INSERT INTO PERSONNEL VALUES (2, 'JANE DOE')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (1, 1, 'English')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (1, 1, 'Spanish')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (1, 1, 'Russian')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (1, 3, 'Logistics')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (1, 3, 'Infantry')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (2, 1, 'English')
INSERT INTO PERSONNEL_ATTRIBUTES VALUES (2, 3, 'Infantry')

SELECT P.ID, P.NAME, PA1.ATTRIB_VALUE AS DESIRED_LANGUAGE, PA2.ATTRIB_VALUE AS APPROVED_OPERATION
FROM PERSONNEL P
JOIN PERSONNEL_ATTRIBUTES PA1 ON P.ID = PA1.PERSONNEL_ID AND PA1.ATTRIB_ID = 1
JOIN PERSONNEL_ATTRIBUTES PA2 ON P.ID = PA2.PERSONNEL_ID AND PA2.ATTRIB_ID = 3
WHERE PA1.ATTRIB_VALUE = 'Spanish' AND (PA2.ATTRIB_VALUE = 'Infantry' OR PA2.ATTRIB_VALUE = 'Airborne')
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...