FIND_IN_SET()替代?

时间:2014-05-30 00:41:03

标签: mysql

我的查询目前看起来像:

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]是主键,因此该列上已有索引。有没有办法利用这个查询的索引运行得更快?或者还有另一种方法可以改善此查询的性能吗?

3 个答案:

答案 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-如果此查询频繁,我建议更改表的结构如下:

  • table1(col_a PK,col_b,col_c)
  • table2(col_a FK,value_of_sub_d)其中value_of_sub_d是(2,3,...)
  • 之一

在这种一对多的关系中,您既可以进行连接,也可以从满足条件的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