SQL查询多行

时间:2012-04-12 10:20:28

标签: mysql sql

我有一个看起来像这样的表:

---------------------------
|housing_id | facility_id |
---------------------------
|    1      |      7      |
|    1      |      4      |
|    2      |      7      |
---------------------------

现在我要做的是获取所有housing_id,其facility_id为7和4。 因此,在这种情况下,查询应仅返回housing_id 1。 数据库是mysql。

3 个答案:

答案 0 :(得分:7)

另一种方法是 -

SELECT housing_id
FROM mytable
WHERE facility_id IN (4,7)
GROUP BY housing_id
HAVING COUNT(DISTINCT facility_id) = 2

更新 - 受到Josvic评论的启发我决定做更多测试,并认为我会包括我的发现。

使用此查询的一个好处是可以轻松修改以包含更多facility_ids。如果你想找到所有有facility_ids 1,3,4和4的housing_ids。 7你刚才做的 -

SELECT housing_id
FROM mytable
WHERE facility_id IN (1,3,4,7)
GROUP BY housing_id
HAVING COUNT(DISTINCT facility_id) = 4

根据所采用的索引策略,所有这三个查询的性能差别很大。无论使用哪种索引,我都无法在我的测试数据集上从依赖子查询版本获得合理的性能。

Tim提供的自联接解决方案在两列上给出了单独的单列索引,但是在标准数量增加的情况下表现不佳。

以下是我的测试表上的一些基本统计信息--500k行--147963 housing_ids,其中facility_id的潜在值介于1和9之间。

以下是用于运行所有这些测试的索引 -

SHOW INDEXES FROM mytable;
+---------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+
| Table   | Non_unique | Key_name            | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type |
+---------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+
| mytable |          0 | UQ_housing_facility |            1 | housing_id  | A         |      500537 |     NULL | NULL   |      | BTREE      |
| mytable |          0 | UQ_housing_facility |            2 | facility_id | A         |      500537 |     NULL | NULL   |      | BTREE      |
| mytable |          0 | UQ_facility_housing |            1 | facility_id | A         |          12 |     NULL | NULL   |      | BTREE      |
| mytable |          0 | UQ_facility_housing |            2 | housing_id  | A         |      500537 |     NULL | NULL   |      | BTREE      |
| mytable |          1 | IX_housing          |            1 | housing_id  | A         |      500537 |     NULL | NULL   |      | BTREE      |
| mytable |          1 | IX_facility         |            1 | facility_id | A         |          12 |     NULL | NULL   |      | BTREE      |
+---------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+

测试的第一个查询是从属子查询 -

SELECT SQL_NO_CACHE DISTINCT housing_id
FROM mytable
WHERE housing_id IN (SELECT housing_id FROM mytable WHERE facility_id=4)
AND housing_id IN (SELECT housing_id FROM mytable WHERE facility_id=7);

17321 rows in set (9.15 sec)

+----+--------------------+---------+-----------------+----------------------------------------------------------------+---------------------+---------+------------+--------+---------------------------------------+
| id | select_type        | table   | type            | possible_keys                                                  | key                 | key_len | ref        | rows   | Extra                                 |
+----+--------------------+---------+-----------------+----------------------------------------------------------------+---------------------+---------+------------+--------+---------------------------------------+
|  1 | PRIMARY            | mytable | range           | NULL                                                           | IX_housing          | 4       | NULL       | 500538 | Using where; Using index for group-by |
|  3 | DEPENDENT SUBQUERY | mytable | unique_subquery | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | UQ_housing_facility | 8       | func,const |      1 | Using index; Using where              |
|  2 | DEPENDENT SUBQUERY | mytable | unique_subquery | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | UQ_housing_facility | 8       | func,const |      1 | Using index; Using where              |
+----+--------------------+---------+-----------------+----------------------------------------------------------------+---------------------+---------+------------+--------+---------------------------------------+

SELECT SQL_NO_CACHE DISTINCT housing_id
FROM mytable
WHERE housing_id IN (SELECT housing_id FROM mytable WHERE facility_id=1)
AND housing_id IN (SELECT housing_id FROM mytable WHERE facility_id=3)
AND housing_id IN (SELECT housing_id FROM mytable WHERE facility_id=4)
AND housing_id IN (SELECT housing_id FROM mytable WHERE facility_id=7);

567 rows in set (9.30 sec)

+----+--------------------+---------+-----------------+----------------------------------------------------------------+---------------------+---------+------------+--------+---------------------------------------+
| id | select_type        | table   | type            | possible_keys                                                  | key                 | key_len | ref        | rows   | Extra                                 |
+----+--------------------+---------+-----------------+----------------------------------------------------------------+---------------------+---------+------------+--------+---------------------------------------+
|  1 | PRIMARY            | mytable | range           | NULL                                                           | IX_housing          | 4       | NULL       | 500538 | Using where; Using index for group-by |
|  5 | DEPENDENT SUBQUERY | mytable | unique_subquery | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | UQ_housing_facility | 8       | func,const |      1 | Using index; Using where              |
|  4 | DEPENDENT SUBQUERY | mytable | unique_subquery | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | UQ_housing_facility | 8       | func,const |      1 | Using index; Using where              |
|  3 | DEPENDENT SUBQUERY | mytable | unique_subquery | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | UQ_housing_facility | 8       | func,const |      1 | Using index; Using where              |
|  2 | DEPENDENT SUBQUERY | mytable | unique_subquery | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | UQ_housing_facility | 8       | func,const |      1 | Using index; Using where              |
+----+--------------------+---------+-----------------+----------------------------------------------------------------+---------------------+---------+------------+--------+---------------------------------------+

接下来是我的版本使用GROUP BY ... HAVING COUNT ...

SELECT SQL_NO_CACHE housing_id
FROM mytable
WHERE facility_id IN (4,7)
GROUP BY housing_id
HAVING COUNT(DISTINCT facility_id) = 2;

17321 rows in set (0.79 sec)

+----+-------------+---------+-------+---------------------------------+-------------+---------+------+--------+------------------------------------------+
| id | select_type | table   | type  | possible_keys                   | key         | key_len | ref  | rows   | Extra                                    |
+----+-------------+---------+-------+---------------------------------+-------------+---------+------+--------+------------------------------------------+
|  1 | SIMPLE      | mytable | range | UQ_facility_housing,IX_facility | IX_facility | 4       | NULL | 198646 | Using where; Using index; Using filesort |
+----+-------------+---------+-------+---------------------------------+-------------+---------+------+--------+------------------------------------------+

SELECT SQL_NO_CACHE housing_id
FROM mytable
WHERE facility_id IN (1,3,4,7)
GROUP BY housing_id
HAVING COUNT(DISTINCT facility_id) = 4;

567 rows in set (1.25 sec)

+----+-------------+---------+-------+---------------------------------+-------------+---------+------+--------+------------------------------------------+
| id | select_type | table   | type  | possible_keys                   | key         | key_len | ref  | rows   | Extra                                    |
+----+-------------+---------+-------+---------------------------------+-------------+---------+------+--------+------------------------------------------+
|  1 | SIMPLE      | mytable | range | UQ_facility_housing,IX_facility | IX_facility | 4       | NULL | 407160 | Using where; Using index; Using filesort |
+----+-------------+---------+-------+---------------------------------+-------------+---------+------+--------+------------------------------------------+

最后但并非最不重要的是自我加入 -

SELECT SQL_NO_CACHE a.housing_id
FROM mytable a
INNER JOIN mytable b
    ON a.housing_id = b.housing_id
WHERE a.facility_id = 4 AND b.facility_id = 7;

17321 rows in set (1.37 sec)

+----+-------------+-------+--------+----------------------------------------------------------------+---------------------+---------+-------------------------+-------+-------------+
| id | select_type | table | type   | possible_keys                                                  | key                 | key_len | ref                     | rows  | Extra       |
+----+-------------+-------+--------+----------------------------------------------------------------+---------------------+---------+-------------------------+-------+-------------+
|  1 | SIMPLE      | b     | ref    | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | IX_facility         | 4       | const                   | 94598 | Using index |
|  1 | SIMPLE      | a     | eq_ref | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | UQ_housing_facility | 8       | test.b.housing_id,const |     1 | Using index |
+----+-------------+-------+--------+----------------------------------------------------------------+---------------------+---------+-------------------------+-------+-------------+

SELECT SQL_NO_CACHE a.housing_id
FROM mytable a
INNER JOIN mytable b
    ON a.housing_id = b.housing_id
INNER JOIN mytable c
    ON a.housing_id = c.housing_id
INNER JOIN mytable d
    ON a.housing_id = d.housing_id
WHERE a.facility_id = 1
AND b.facility_id = 3
AND c.facility_id = 4
AND d.facility_id = 7;

567 rows in set (1.64 sec)

+----+-------------+-------+--------+----------------------------------------------------------------+---------------------+---------+-------------------------+-------+--------------------------+
| id | select_type | table | type   | possible_keys                                                  | key                 | key_len | ref                     | rows  | Extra                    |
+----+-------------+-------+--------+----------------------------------------------------------------+---------------------+---------+-------------------------+-------+--------------------------+
|  1 | SIMPLE      | b     | ref    | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | IX_facility         | 4       | const                   | 93782 | Using index              |
|  1 | SIMPLE      | d     | eq_ref | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | UQ_housing_facility | 8       | test.b.housing_id,const |     1 | Using index              |
|  1 | SIMPLE      | c     | eq_ref | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | UQ_housing_facility | 8       | test.b.housing_id,const |     1 | Using index              |
|  1 | SIMPLE      | a     | eq_ref | UQ_housing_facility,UQ_facility_housing,IX_housing,IX_facility | UQ_housing_facility | 8       | test.d.housing_id,const |     1 | Using where; Using index |
+----+-------------+-------+--------+----------------------------------------------------------------+---------------------+---------+-------------------------+-------+--------------------------+

答案 1 :(得分:4)

SELECT housing_id
FROM mytable
WHERE housing_id IN (SELECT housing_id FROM mytable WHERE facility_id=4)
AND housing_id IN (SELECT housing_id FROM mytable WHERE facility_id=7)

答案 2 :(得分:3)

你也可以做一个自我加入,最快的一个将取决于你的表中有多少数据。

SELECT a.housing_id
FROM mytable a
INNER JOIN mytable b
ON a.housing_id = b.housing_id AND a.facility_id <> b.facility_id
WHERE a.facility_id = 4 AND b.facility_id = 7