2012-04-12 4 views
1

У меня есть таблица, которая выглядит следующим образом:SQL-запрос на несколько строк

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

Теперь то, что я хочу сделать, это получить все housing_ids с facility_id 7 и 4. Таким образом, запрос должен возвращать только housing_id 1 в этом случае. База данных - это mysql.

ответ

7

Другой подход -

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

UPDATE - вдохновлена ​​комментарием Josvic я решил сделать некоторые испытания и думал, что будет включать мои выводы.

Одним из преимуществ использования этого запроса является то, что его легко модифицировать, чтобы включить в него больше объектов-объектов. Если вы хотите, чтобы найти все housing_ids, которые имеют facility_ids 1, 3, 4 & 7 вы просто делаете -

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

Выполнение всех этих трех запросов изменяется очень на основе стратегии индексирования работают. Я не смог получить разумную производительность на моем тестовом наборе данных из зависимой версии подзапроса, независимо от используемой индексации.

Решение для самостоятельного присоединения, предоставляемое Тимом, очень хорошо учитывает отдельные индексы отдельных столбцов на двух столбцах, но не работает так хорошо, как число критериев увеличивается.

Вот некоторые основные статистика по моей тестовой таблицы - 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 | 
+----+-------------+-------+--------+----------------------------------------------------------------+---------------------+---------+-------------------------+-------+--------------------------+ 
+0

теперь, когда я думаю об этом, это кажется единственным хорошим решением до сих пор, lol ... Я удалил свой ответ – SinistraD

+0

Whoa, thats gib speed difference, думаю, я постараюсь использовать вашу версию, тогда я думаю. –

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

это кажется немного усложненной – SinistraD

+0

@SinistraD The 'статье приведены результаты в' в копиях. Еще проще, чем 'GROUP BY .. HAVING COUNT'. –

+1

У команды GROUP BY ... HAVING COUNT будет меньше накладных расходов. Я просто попробовал это на тестовом столе с 500K-записями. – nnichols

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 
Смежные вопросы