Помогите с запросом. Поиск записей, которые имеют одинаковые отношения (MySQL)

dba.stackexchange https://dba.stackexchange.com/questions/14391

Вопрос

У меня есть таблица списков, которая имеет много отношений с таблицей таксонов. Структура таблицы выглядит так:

listings
----------------
id (int)
name (varchar)

listings_taxons
----------------
listing_id (int)
taxon_id (int)

taxons
----------------
id (int)
name (varchar)

Моя цель - выбрать все ряды в listings Таблица с соответствующим списком идентификаторов таксонов. Каждая возвращаемая запись списка должна иметь отношения с двумя таксонами, так что я получаю набор записей, содержащий пересечение записей между двумя таксонами.

Пример: у меня есть список под названием «глушитель», и в нем есть следующие таксоны: «ford», «Mustang», «выхлоп». Если я запрошу все списки с «Ford» и «выхлоп», я должен получить все списки, которые имеют «Ford» и «выхлоп» в качестве таксонов.

Как я бы эффективно построил этот запрос?

Это было полезно?

Решение

SELECT B.name
FROM
(
    SELECT BB.listing_id id,COUNT(1) taxon_count
    FROM
    (
        SELECT id taxon_id FROM taxons
        WHERE name IN ('Ford','Exhaust')
    ) AA
    INNER JOIN listings_taxons BB
    USING (taxon_id)
    GROUP BY listing_id HAVING COUNT(1) = 2
) A
INNER JOIN listings B USING (id);

Подпрограмма A вернет все списки_доды, которые имеют Ford, выхлоп или оба. Выполнение группы по подсчету в подразделении A дает любой идентификатор листинга, который имеет количество (1) из 2, имеет идентификаторы как Ford, так и выхлопных таксонов, потому что BB.Listing_ID появится дважды, таким образом, имея счет (1) = 2. Тогда у подкового A есть внутренний Присоединяйтесь к спискам.

Убедитесь, что у вас есть следующие индексы

ALTER TABLE listings_taxons ADD INDEX taxon_listing_ndx (taxon_id,listing_id);
ALTER TABLE taxons ADD INDEX name_id_ndx (name,id);

Вот несколько примеров данных

drop database if exists nwwatson;
create database nwwatson;
use nwwatson
create table listings
(id int not null auto_increment,
name varchar(25),
primary key (id),
key (name));
create table taxons like listings;
create table listings_taxons
(
    listing_id int,
    taxon_id int,
    primary key (listing_id,taxon_id),
    unique key (taxon_id,listing_id)
);
insert into listings (name) values ('SteeringWheel'),('WindShield'),('Muffler'),('AC');
insert into taxons (name) values ('Ford'),('Escort'),('Buick'),('Exhaust'),('Mustard');
insert into listings_taxons values
(1,1),(1,3),(1,5),(2,1),(2,2),(2,3),(2,5),
(3,1),(3,4),(4,2),(4,3),(4,4),(5,1),(5,5);
SELECT * FROM listings;
SELECT * FROM taxons;
SELECT * FROM listings_taxons;
SELECT B.name
FROM
(
    SELECT BB.listing_id id,COUNT(1) taxon_count
    FROM
    (
        SELECT id taxon_id FROM taxons
        WHERE name IN ('Ford','Exhaust')
    ) AA
    INNER JOIN listings_taxons BB
    USING (taxon_id)
    GROUP BY listing_id HAVING COUNT(1) = 2
) A
INNER JOIN listings B USING (id);

Вот он выполнен

mysql> drop database if exists nwwatson;
Query OK, 3 rows affected (0.09 sec)

mysql> create database nwwatson;
Query OK, 1 row affected (0.00 sec)

mysql> use nwwatson
Database changed
mysql> create table listings
    -> (
    -> id int not null auto_increment,
    -> name varchar(25),
    -> primary key (id),
    -> key (name)
    -> );
Query OK, 0 rows affected (0.08 sec)

mysql> create table taxons like listings;
Query OK, 0 rows affected (0.05 sec)

mysql> create table listings_taxons
    -> (
    ->     listing_id int,
    ->     taxon_id int,
    ->     primary key (listing_id,taxon_id),
    ->     unique key (taxon_id,listing_id)
    -> );
Query OK, 0 rows affected (0.08 sec)

mysql> insert into listings (name) values ('SteeringWheel'),('WindShield'),('Muffler'),('AC');
Query OK, 4 rows affected (0.06 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> insert into taxons (name) values ('Ford'),('Escort'),('Buick'),('Exhaust'),('Mustard');
Query OK, 5 rows affected (0.06 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> insert into listings_taxons values
    -> (1,1),(1,3),(1,5),(2,1),(2,2),(2,3),(2,5),
    -> (3,1),(3,4),(4,2),(4,3),(4,4),(5,1),(5,5);
Query OK, 14 rows affected (0.11 sec)
Records: 14  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM listings;
+----+---------------+
| id | name          |
+----+---------------+
|  4 | AC            |
|  3 | Muffler       |
|  1 | SteeringWheel |
|  2 | WindShield    |
+----+---------------+
4 rows in set (0.00 sec)

mysql> SELECT * FROM taxons;
+----+---------+
| id | name    |
+----+---------+
|  3 | Buick   |
|  2 | Escort  |
|  4 | Exhaust |
|  1 | Ford    |
|  5 | Mustard |
+----+---------+
5 rows in set (0.00 sec)

mysql> SELECT * FROM listings_taxons;
+------------+----------+
| listing_id | taxon_id |
+------------+----------+
|          1 |        1 |
|          1 |        3 |
|          1 |        5 |
|          2 |        1 |
|          2 |        2 |
|          2 |        3 |
|          2 |        5 |
|          3 |        1 |
|          3 |        4 |
|          4 |        2 |
|          4 |        3 |
|          4 |        4 |
|          5 |        1 |
|          5 |        5 |
+------------+----------+
14 rows in set (0.00 sec)

mysql> SELECT B.name
    -> FROM
    -> (
    ->     SELECT BB.listing_id id,COUNT(1) taxon_count
    ->     FROM
    ->     (
    ->         SELECT id taxon_id FROM taxons
    ->         WHERE name IN ('Ford','Exhaust')
    ->     ) AA
    ->     INNER JOIN listings_taxons BB
    ->     USING (taxon_id)
    ->     GROUP BY listing_id HAVING COUNT(1) = 2
    -> ) A
    -> INNER JOIN listings B USING (id);
+---------+
| name    |
+---------+
| Muffler |
+---------+
1 row in set (0.00 sec)

mysql>

Попробуй !!!

Другие советы

Если я правильно понимаю, вы хотите выполнить реляционное расстояние. Попробуйте этот вопрос с множеством разных способов этого: Как отфильтровать SQL приводит к тому, что он проведен..

Я бы пошел на (несколько) JOIN Решение, но вы всегда можете проверить свои данные и запросы:

SELECT 
    li.*

FROM
    listings AS li

  JOIN
    listings_taxons AS lt1
      ON  lt1.listing_id = li.id
  JOIN
    taxons AS t1 
      ON  t1.id = lt1.taxon_id
      AND t1.name = 'Ford'

  JOIN
    listings_taxons AS lt2
      ON  lt2.listing_id = li.id
  JOIN
    taxons AS t2 
      ON  t2.id = lt2.taxon_id
      AND t2.name = 'Exhaust'

Есть много способов решить этот классический случай реляционное разделение.
Для список таксонов (более чем несколько), эта форма является одной из самых коротких синтаксически:

SELECT l.*
FROM  (
   SELECT lt.listing_id
   FROM   taxons t
   JOIN   listings_taxons lt ON lt.taxon_id = t.id
   WHERE  t.name IN ('Ford', 'Mustang', 'Exhaust')
   GROUP  BY lt.listing_id
   HAVING COUNT(*) = 3
   ) x
JOIN   listings l ON l.id = x.listing_id;

Это предполагает UNIQUE ограничение на (listing_id, taxon_id) в таблице listings_taxons.

Сравните с другими методами под этим Связанный вопрос @ypercube уже связан с, чтобы найти, является ли это одним из самых быстрых.

SELECT listings.*
FROM listings
INNER JOIN listings_taxons ON listings.id = listings_taxons.listing_id
INNER JOIN taxons ON listing_taxons.taxon_id = taxon.id
WHERE taxon.id in 
  (SELECT taxon_id 
   FROM taxon
   WHERE name LIKE '%whatever%' OR name LIKE '%another%');

Это то, что вы имеете в виду?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с dba.stackexchange
scroll top