关于这个主题有很多问题,但我的问题更具体到性能问题。
关于一个对象,我想跟踪多个“属性”,每个属性都有多个离散的“值”(每个属性有 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/