我的查询目前看起来像:
SELECT [column a], [column b], [column c], [column d]
FROM [table]
WHERE FIND_IN_SET(2, column d)
ORDER BY [column a] DESC
其中[column d]的类型为varchar
,并且包含一组数字(例如3, 2, 4, 6, 1, 9
)。所以基本上我试图返回2
在其数字集中的所有记录。但是,当我在上面的查询中执行EXPLAIN
时,这是我的输出:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE [table] ALL NULL NULL NULL NULL 500000 Using where; Using filesort
此查询在执行此查询期间似乎没有使用任何索引。 [column a]
是主键,因此该列上已有索引。有没有办法利用这个查询的索引运行得更快?或者还有另一种方法可以改善此查询的性能吗?
答案 0 :(得分:5)
替代方案:正确规范化架构。
FIND_IN_SET 不 Sargable且无法使用。
答案 1 :(得分:3)
一种可能的优化方法是将[column d]
定义为SET
类型。正如documentation says:
MySQL以数字方式存储SET值,其中包含低位 对应于第一组成员的存储值。如果你检索一个 在数值上下文中设置值,检索的值设置了位 对应于构成列值的集合成员。
这是一个简单明了的例子:
CREATE TABLE `tbl_name` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`set_col` set('a','b','c','d') NOT NULL,
PRIMARY KEY (`id`),
KEY `set_col_idx` (`set_col`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tbl_name` (`set_col`) VALUES
('a'),
('b'),
('c'),
('d'),
('a,b'),
('a,c'),
('a,d'),
('b,c'),
('b,d'),
('c,d'),
('a,b,c'),
('a,b,d'),
('a,c,d'),
('b,c,d'),
('a,b,c,d');
指定为set_col
的列SET('a','b','c','d')
的成员具有以下十进制和二进制值:
╔════════════╦═══════════════╦══════════════╗
║ SET Member ║ Decimal Value ║ Binary Value ║
╠════════════╬═══════════════╬══════════════╣
║ 'a' ║ 1 ║ 0001 ║
╠════════════╬═══════════════╬══════════════╣
║ 'b' ║ 2 ║ 0010 ║
╠════════════╬═══════════════╬══════════════╣
║ 'c' ║ 4 ║ 0100 ║
╠════════════╬═══════════════╬══════════════╣
║ 'd' ║ 8 ║ 1000 ║
╚════════════╩═══════════════╩══════════════╝
因此,如果您需要检索值为a,c,d
的记录,它是第一个,第三个和第四个成员,即1 + 4 + 8
,即13
。
如果您运行查询:
EXPLAIN SELECT * FROM `tbl_name` WHERE `tbl_name`.`set_col` = 13;
你会得到:
╔════╦═════════════╦══════════╦══════╦═══════════════╦═════════════╦═════════╦═══════╦══════╦═════════════╗
║ id ║ select_type ║ table ║ type ║ possible_keys ║ key ║ key_len ║ ref ║ rows ║ Extra ║
╠════╬═════════════╬══════════╬══════╬═══════════════╬═════════════╬═════════╬═══════╬══════╬═════════════╣
║ 1 ║ SIMPLE ║ tbl_name ║ ref ║ set_col_idx ║ set_col_idx ║ 1 ║ const ║ 1 ║ Using index ║
╚════╩═════════════╩══════════╩══════╩═══════════════╩═════════════╩═════════╩═══════╩══════╩═════════════╝
您不需要手动获取SET
选项的十进制值 - 您可以使用SUM
,例如:
SELECT *
FROM `tbl_name`
WHERE `set_col` = (SELECT SUM(`set_col`)
FROM `tbl_name`
WHERE `set_col` IN ('a', 'c', 'd')
);
╔════╦═════════════╦══════════╦═══════╦═══════════════╦═════════════╦═════════╦═══════╦══════╦══════════════════════════╗
║ id ║ select_type ║ table ║ type ║ possible_keys ║ key ║ key_len ║ ref ║ rows ║ Extra ║
╠════╬═════════════╬══════════╬═══════╬═══════════════╬═════════════╬═════════╬═══════╬══════╬══════════════════════════╣
║ 1 ║ PRIMARY ║ tbl_name ║ index ║ set_col_idx ║ set_col_idx ║ 1 ║ NULL ║ 15 ║ Using where; Using index ║
╠════╬═════════════╬══════════╬═══════╬═══════════════╬═════════════╬═════════╬═══════╬══════╬══════════════════════════╣
║ 1 ║ SUBQUERY ║ tbl_name ║ range ║ set_col_idx ║ set_col_idx ║ 1 ║ NULL ║ 3 ║ Using where; Using index ║
╚════╩═════════════╩══════════╩═══════╩═══════════════╩═════════════╩═════════╩═══════╩══════╩══════════════════════════╝
答案 2 :(得分:1)
1-全文索引在这种情况下不是一个好主意,因为: 在这种情况下,您搜索的字符串的长度很小(1), 这将无法找到(虽然可以配置,但不是很好 主意)
2-如果此查询频繁,我建议更改表的结构如下:
在这种一对多的关系中,您既可以进行连接,也可以从满足条件的table2获取PK,并从table1中选择该ID的行 示例:
Select * from table1 t1 inner join table2 t2 on t1.col_a=t2.col_a WHERE t2.value_of_sub_d=2