Mysql
 sql >> Datenbank >  >> RDS >> Mysql

SQL-Abfrage über mehrere Zeilen

Ein anderer Ansatz wäre -

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

AKTUALISIEREN - Inspiriert durch den Kommentar von Josvic beschloss ich, weitere Tests durchzuführen und dachte, ich würde meine Ergebnisse mit einbeziehen.

Einer der Vorteile der Verwendung dieser Abfrage besteht darin, dass sie einfach geändert werden kann, um mehr facility_ids einzubeziehen. Wenn Sie alle Housing_ids mit den Facility_ids 1, 3, 4 und 7 finden möchten, tun Sie einfach -

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

Die Leistung aller drei dieser Abfragen variiert stark je nach verwendeter Indizierungsstrategie. Ich konnte bei meinem Test-Datensatz mit der abhängigen Unterabfrageversion keine angemessene Leistung erzielen, unabhängig von der verwendeten Indizierung.

Die von Tim bereitgestellte Self-Join-Lösung funktioniert sehr gut, wenn separate Einzelspaltenindizes für die beiden Spalten gegeben sind, aber nicht ganz so gut, wenn die Anzahl der Kriterien zunimmt.

Hier sind einige grundlegende Statistiken zu meiner Testtabelle – 500.000 Zeilen – 147963 Housing_ids mit möglichen Werten für facility_id zwischen 1 und 9.

Hier sind die Indizes, die zum Ausführen all dieser Tests verwendet werden -

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      |
+---------+------------+---------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+

Die erste getestete Abfrage ist die abhängige Unterabfrage -

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              |
+----+--------------------+---------+-----------------+----------------------------------------------------------------+---------------------+---------+------------+--------+---------------------------------------+

Als nächstes kommt meine Version mit 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 |
+----+-------------+---------+-------+---------------------------------+-------------+---------+------+--------+------------------------------------------+

Und nicht zuletzt der Selbstbeitritt -

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 |
+----+-------------+-------+--------+----------------------------------------------------------------+---------------------+---------+-------------------------+-------+--------------------------+