2017-10-19 28 views
1

我有这样的表:MySQL的:掌握完全相同的数据DB行从矢量

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_ids值完全匹配我的向量的id?

+0

你是什么意思* x_ids值完全匹配我的向量*? –

+0

请注意,DISTINCT不是一个函数。 – Strawberry

回答

2

这里有一种方法(假设ID的每个组合X_ID是唯一的):

SELECT id 
    FROM table1 
GROUP 
    BY id 
HAVING SUM(x_id IN (90,91,92)) = COUNT(x_id) 
    AND COUNT(*) = 3; 
+0

这比最初接受的答案要干净得多。我采用了相同的方法,但使用额外的JOIN只对至少包含一个目标值的行运行聚合函数 - 这是对大型表格的有用优化。对于大多数情况,您的情况可能已经足够好了,并且更容易遵循。 –

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; 

这只会考虑行有一个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了数百万行,优化查询的性能会比聚合整个表的未优化的方法变得非常明显。

+0

谢谢!赶上'HAVING count = 3'。我一定错误地将这一点优化了。我以我的例子解决了这个问题。但是你说得对,搜索矢量的一个子集仍然会以较小的长度进行匹配。关于表现,我会给它一个测试。这些数字听起来很不寻常。任何你仍然在MySQL 5.5上的机会?它有非常可怕的子查询性能。 –

+0

我测试过的版本甚至比它老,实际上,所以我有兴趣看看它是如何在你的机器上(当然,相对于我的查询)。 – Strawberry

+0

我刚刚对一个带有25,000个'id'条目的表进行了测试,该条目带有长度为2-5的随机x_id向量,值在1-100之间。我为'(id,x_id)'添加了一个复合索引。我们的两个查询都在MySQL 5.7.18上以〜60-70ms返回。 –