MySQL:连接与Bitwise运算符及其性能

时间:2011-05-18 16:55:37

标签: mysql sql performance join bit-manipulation

关于这个问题有很多问题,但我的问题更具针对性能问题。

关于一个物体,我想跟踪多个“属性”,每个属性都有多个离散的“值”(每个属性都有3到16个有效的“值”。)例如,考虑跟踪军事人员。属性/值可能是(不是真的,我完全做了这些):

属性:{values}
languages_spoken: {english, spanish, russian, chinese, …. }
certificates: {infantry, airborne, pilot, tank_driver…..}
approved_equipment: {m4, rocket_launcher, shovel, super_secret_radio_thingy….}
approved_operations: {reconnaissance, logistics, invasion, cooking, ….}
awards_won: {medal_honor, purple_heart, ….}

......等等。

一个人这样做 - 我想要这样做的方式 - 是有人员表和属性表:

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

以及相关的属性和值表。

因此,如果pesonnel_id = 31415被批准用于后勤,那么在personnel_attributes表中会有以下条目:

personnel_id | attribute_id | value_id
31415 | 3 | 2

其中3 =“approved_operations”的attribute_id和“物流”的2 = value_id(抱歉格式化空格没有对齐。)

然后搜索找到所有讲英语或西班牙语的人员,以及步兵或空降人员,并且可以操作铲子或者super_secret_radio_thingy会是这样的:

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

假设这不是编写SQL查询的完全愚蠢的方法,问题是它非常慢(即使有看似相关的索引。)

所以我正在使用按位运算符,其中每个属性都是表中的一列,每个值都是一个位。相同的搜索将是:

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

我知道这会进行全表扫描,但在我的350,000个样本人员的实验和每个16个属性的实验中,第一种方法需要20秒,而按位方法需要38毫秒!

我在这里做错了吗?这些是我应该期待的表现结果吗?

谢谢!

2 个答案:

答案 0 :(得分:0)

使用按位运算需要评估所有行。我相信您的问题可以通过更改原始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')

答案 1 :(得分:0)

使用django-bitfield或标记的单独表有相同的问题。

受实验的启发,我使用了一个3.5米的记录表(innodb),并使用count()并检索两种变体的查询。结果令人惊讶:大约5秒对比40秒的位域胜利。