我想假设有几百万个数据集行,因为这严重影响了查询设计。
考虑到这一点,你可以这样做:
SELECT id,
SUM(x_id IN (90,91,92)) AS score,
SUM(1) AS count
FROM Table1
WHERE id IN (
SELECT id FROM Table1 WHERE x_id IN (90,91,92)
)
GROUP BY id
HAVING score = count AND count = 3;
这只会考虑行有一个x_id
匹配90,91,或92.计算多少x_id
值匹配的score
每个id
。它还计算每id
不同x_id
值的count
。这有助于我们排除具有90,91和92的值的ID,但也具有附加值。
确切的向量匹配将有一个score
等于count
。
这种方法应该在具有数百万行的表上更高效,因为只有那些行的子集将引用至少一个目标值。
它假定每个(id, x_id)
元组都是唯一的。
编辑:
修正了HAVING count = 3
问题的例子,如在评论报告@Strawberry。
当使用这样的子查询时,确保您使用的是最新版本的MySQL。由于查询计划员忽略密钥并执行昂贵的扫描,MySQL 5.5及更早版本的子查询性能较差。
为了演示额外子查询的性能改进,我们可以生成一堆样本数据以插入到Table1
中。下面是产生100,000行与值长度2-5的随机向量1-100之间的一个简单的PHP脚本:
<?php
$possible_values = range(1,100);
foreach(range(1,100000) as $id) {
$vector = array_rand($possible_values, mt_rand(2,5));
$values = array_map(function($x_id) use ($id) {
return sprintf("(%d, %d)", $id, $x_id);
}, $vector);
echo sprintf("INSERT INTO Table1 (id, x_id) VALUES %s;\n",
implode(',', $values)
);
}
我们假设该表的样子:
CREATE TABLE `Table1` (
`id` int(11) DEFAULT NULL,
`x_id` int(11) DEFAULT NULL,
KEY `id` (`id`, `x_id`)
KEY `x_id` (`x_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
让我们比较子查询优化的好处找到一个短向量:
mysql> SELECT SQL_NO_CACHE id,
-> SUM(x_id IN (6,25)) AS score,
-> SUM(1) AS count
-> FROM Table1
-> WHERE id IN (
-> SELECT id FROM Table1 WHERE x_id IN (6,25)
->)
-> GROUP BY id
-> HAVING score = count AND count = 2;
+-------+-------+-------+
| id | score | count |
+-------+-------+-------+
| 15265 | 2 | 2 |
| 40816 | 2 | 2 |
| 75000 | 2 | 2 |
| 75239 | 2 | 2 |
| 83498 | 2 | 2 |
+-------+-------+-------+
5 rows in set (0.04 sec)
mysql> SELECT SQL_NO_CACHE id
-> FROM table1
-> GROUP BY id
-> HAVING SUM(x_id IN (6,25)) = COUNT(x_id)
-> AND COUNT(*) = 2;
+-------+
| id |
+-------+
| 15265 |
| 40816 |
| 75000 |
| 75239 |
| 83498 |
+-------+
5 rows in set (0.14 sec)
优化是100毫秒更快(所花费的时间的29%是未优化的查询)。
你可以看到为什么用EXPLAIN
。
未优化我们扫描几乎整个表:
mysql> explain SELECT SQL_NO_CACHE id FROM table1 GROUP BY id HAVING SUM(x_id IN (6,25)) = COUNT(x_id) AND COUNT(*) = 2 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: table1
partitions: NULL
type: index
possible_keys: id
key: id
key_len: 10
ref: NULL
rows: 338846
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
优化:
mysql> explain SELECT SQL_NO_CACHE id, SUM(x_id IN (6,25)) AS score, SUM(1) AS count FROM Table1 WHERE id IN (SELECT id FROM Table1 WHERE x_id IN (6,25)) GROUP BY id HAVING score = count AND count = 2 \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: <subquery2>
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: 100.00
Extra: Using where; Using temporary; Using filesort
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: Table1
partitions: NULL
type: ref
possible_keys: id
key: id
key_len: 5
ref: <subquery2>.id
rows: 3
filtered: 100.00
Extra: Using index
*************************** 3. row ***************************
id: 2
select_type: MATERIALIZED
table: Table1
partitions: NULL
type: range
possible_keys: x_id,id
key: x_id
key_len: 5
ref: NULL
rows: 6874
filtered: 100.00
Extra: Using index condition
3 rows in set, 1 warning (0.00 sec)
在优化,我们能够限制我们从聚集行的子集〜 338,846只到〜6,874。这是一个lot为MySQL做的工作较少。
对于较长的矢量,像19,61,62,96
,优化查询在80毫秒VS 150ms的运行用表扫描(几乎快两倍)。
额外的复杂性可能不值得拯救100ms的,但如果Table1
了数百万行,优化查询的性能会比聚合整个表的未优化的方法变得非常明显。
你是什么意思* x_ids值完全匹配我的向量*? –
请注意,DISTINCT不是一个函数。 – Strawberry