MySQL:从向量中获取具有完全相同数据的DB行
我有这张桌子:MySQL:从向量中获取具有完全相同数据的DB行,mysql,Mysql,我有这张桌子: CREATE TABLE Table1 (`id` int, `x_id` int) ; INSERT INTO Table1 (`id`, `x_id`) VALUES (1, 90), (1, 91), (1, 92), (2, 90), (2, 91), (2, 92), (2, 93) ; 我有一个向量[90,91,92]。我的问题很简单: SELECT DISTINCT(id) FROM Tab
CREATE TABLE Table1
(`id` int, `x_id` int)
;
INSERT INTO Table1
(`id`, `x_id`)
VALUES
(1, 90),
(1, 91),
(1, 92),
(2, 90),
(2, 91),
(2, 92),
(2, 93)
;
我有一个向量[90,91,92]。我的问题很简单:
SELECT DISTINCT(id) FROM Table1 WHERE x_id IN ( 90,91,92);
正确地返回这两个参数。如何仅获取x_id值与向量完全匹配的id 这里有一种方法假设id和x_id的每个组合都是唯一的:
SELECT id
FROM table1
GROUP
BY id
HAVING SUM(x_id IN (90,91,92)) = COUNT(x_id)
AND COUNT(*) = 3;
这里有一种方法假设id和x_id的每个组合都是唯一的:
SELECT id
FROM table1
GROUP
BY id
HAVING SUM(x_id IN (90,91,92)) = COUNT(x_id)
AND COUNT(*) = 3;
我喜欢假设数据集中有数百万行,因为这会严重影响查询设计 考虑到这一点,您可以执行以下操作:
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;
只考虑具有XYID匹配90, 91或92的行。它计算每个id匹配的x_id值的分数。它还计算每个id的不同x_id值的计数。这有助于我们排除具有90、91和92值,但也具有其他值的id
精确的向量匹配的分数等于计数 这种方法在有数百万行的表上应该更有效,因为只有这些行的子集将引用至少一个目标值 它假设每个id,x_id元组都是唯一的 编辑: 修复了示例中的HAVING count=3问题,如注释中报告的 当使用这样的子查询时,请确保使用的是最新版本的MySQL。MySQL 5.5及更早版本的子查询性能较差,这是因为查询规划器忽略键并进行昂贵的扫描 为了演示额外子查询的性能改进,我们可以生成一组样本数据插入到表1中。下面是一个简单的PHP脚本,它使用长度为2-5、值介于1-100之间的随机向量生成100000行:<?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)
);
}
让我们比较一下子查询优化的好处,找到一个短向量:
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%
你可以通过解释来了解原因
未优化我们正在扫描几乎整个表格:
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)
在优化过程中,我们可以将聚合的行子集从~338846限制到~6874。MySQL要做的工作要少得多
对于较长的向量(如19,61,62,96),优化的查询运行速度为80ms,而表扫描速度几乎是150ms的两倍
额外的复杂性可能不值得节省100毫秒,但如果Table1有数百万行,那么与聚合整个表的未优化方法相比,优化查询的性能将变得非常明显。我喜欢假设数据集中有数百万行,因为这严重影响了查询设计 考虑到这一点,您可以执行以下操作:
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;
只考虑具有XYID匹配90, 91或92的行。它计算每个id匹配的x_id值的分数。它还计算每个id的不同x_id值的计数。这有助于我们排除具有90、91和92值,但也具有其他值的id
精确的向量匹配的分数等于计数 这种方法在有数百万行的表上应该更有效,因为只有这些行的子集将引用至少一个目标值 它假设每个id,x_id元组都是唯一的 编辑: 修复了示例中的HAVING count=3问题,如注释中报告的 当使用这样的子查询时,请确保使用的是最新版本的MySQL。MySQL 5.5及更早版本的子查询性能较差,这是因为查询规划器忽略键并进行昂贵的扫描 为了演示额外子查询的性能改进,我们可以生成一组样本数据插入到表1中。下面是一个简单的PHP脚本,它使用长度为2-5、值介于1-100之间的随机向量生成100000行:<?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)
);
}
让我们比较一下子查询优化的好处,找到一个短向量:
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%
你可以通过解释来了解原因
未优化我们正在扫描几乎整个表格:
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)
在优化过程中,我们可以将聚合的行子集从~338846限制到~6874。MySQL要做的工作要少得多
对于较长的向量(如19,61,62,96),优化的查询运行速度为80ms,而表扫描速度几乎是150ms的两倍
额外的复杂性可能不值得节省100毫秒,但如果Table1有数百万行,与聚合整个表的未优化方法相比,优化查询的性能将变得非常明显。什么是x_ids值与我的向量完全匹配?请注意,DISTINCT不是一个函数。什么是x_ids值与我的向量完全匹配?请注意DISTINCT不是一个函数。这非常重要比最初接受的答案更干净。我采用了同样的方法,但加入了一个额外的连接
对至少包含一个目标值的行运行聚合函数,这是对大型表的一种有用的优化。对于大多数情况,你的答案可能足够好,而且更容易理解。这比最初接受的答案要清晰得多。我使用了相同的方法,但是使用了一个额外的连接,只对至少包含一个目标值的行运行聚合函数,这是对大型表的一个有用的优化。对于大多数情况,您的操作可能已经足够好了,而且更简单。谢谢!很好地抓住了计数=3。我一定是一路上错误地优化了它。我在我的例子中修正了这一点。但你是对的,搜索向量的子集仍然会以较小的长度匹配。关于性能,我将对其进行测试。这些数字听起来很不寻常。有没有可能你还在使用MySQL 5.5?它的子查询性能非常糟糕。实际上,我测试的版本甚至比这个版本还要旧,所以我很想看看它在您的机器上相对于我的查询的表现。我刚刚对一个有25000个id项的表进行了测试,该表的随机x_id向量长度为2-5,值在1-100之间。我为id添加了一个复合索引x_id。我们的两个查询都在MySQL 5.7.18上以大约60-70毫秒的时间返回。我也不希望看到额外的WHERE子查询步骤得到改进,直到有更多的数据。我经常处理有数百万行的表,所以我总是要做如上所述的额外优化,以避免全表扫描。我认为这一考虑值得作为增编加以注意,但你的答复值得接受。您的查询更干净、更简单,可以一直使用,直到与行计数相关的查询性能成为问题。我在答案中添加了一些比较。谢谢!很好地抓住了计数=3。我一定是一路上错误地优化了它。我在我的例子中修正了这一点。但你是对的,搜索向量的子集仍然会以较小的长度匹配。关于性能,我将对其进行测试。这些数字听起来很不寻常。有没有可能你还在使用MySQL 5.5?它的子查询性能非常糟糕。实际上,我测试的版本甚至比这个版本还要旧,所以我很想看看它在您的机器上相对于我的查询的表现。我刚刚对一个有25000个id项的表进行了测试,该表的随机x_id向量长度为2-5,值在1-100之间。我为id添加了一个复合索引x_id。我们的两个查询都在MySQL 5.7.18上以大约60-70毫秒的时间返回。我也不希望看到额外的WHERE子查询步骤得到改进,直到有更多的数据。我经常处理有数百万行的表,所以我总是要做如上所述的额外优化,以避免全表扫描。我认为这一考虑值得作为增编加以注意,但你的答复值得接受。您的查询更干净、更简单,可以一直使用,直到查询性能相对于行数成为一个问题。