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

标签 mysql sql performance join bit-manipulation

关于这个主题有很多问题,但我的问题更具体到性能问题。

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

属性:{values}
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 被批准用于后勤,personnel_attributes 表中将有以下条目:

personnel_id | attribute_id | value_id
31415 | 3 | 2

其中 3 = attribute_id 表示“approved_operations”,2 = value_id 表示“logistics”(抱歉,格式空格没有对齐。)

然后搜索所有说英语或西类牙语的人员,以及步兵或空降兵,并且可以操作铲子或 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 毫秒!

我是不是做错了什么?这些是我应该期望的性能结果吗?

谢谢!

最佳答案

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

关于MySQL:连接与按位运算符及其性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6048274/

相关文章:

mysql - 加速 Rails 中的查询

MySQL tc.log 文件

mysql - 仅当与 Eloquent 中的 ID 匹配时才选择特定列

sql - 使用 PIVOT 选择列值作为列

php - 如何对我的结果进行分组并显示所有行?

c# - 自动列宽 EPPlus 慢

javascript - 优化 JS/jQuery 性能(getBoundingClientRect)并消除布局重绘

mysql - 原则 2 DQL 安全

mysql - 在对数据库中的可空列进行比较之前是否需要进行 NULL 值检查?

MySQL 分区 (innoDB) - 大表