Получить пересечение двух отношений многих ко многим определенных значений - PullRequest
0 голосов
/ 02 февраля 2019

NB. Я отметил это с помощью SQLAlchemy и Python, потому что суть вопроса заключалась в том, чтобы разработать запрос для преобразования в SQLAlchemy.Это ясно в ответе, который я отправил.Это также применимо к MySQL.

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

MariaDB [icc]> describe edition;
+-----------+------------+------+-----+---------+----------------+
| Field     | Type       | Null | Key | Default | Extra          |
+-----------+------------+------+-----+---------+----------------+
| id        | int(11)    | NO   | PRI | NULL    | auto_increment |
+-----------+------------+------+-----+---------+----------------+
7 rows in set (0.001 sec)

MariaDB [icc]> describe line;
+------------+--------------+------+-----+---------+----------------+
| Field      | Type         | Null | Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
| id         | int(11)      | NO   | PRI | NULL    | auto_increment |
| edition_id | int(11)      | YES  | MUL | NULL    |                |
| line       | varchar(200) | YES  |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+
5 rows in set (0.001 sec)

MariaDB [icc]> describe line_attribute;
+------------+------------+------+-----+---------+-------+
| Field      | Type       | Null | Key | Default | Extra |
+------------+------------+------+-----+---------+-------+
| line_id    | int(11)    | NO   | PRI | NULL    |       |
| num        | int(11)    | YES  |     | NULL    |       |
| precedence | int(11)    | YES  | MUL | NULL    |       |
| primary    | tinyint(1) | NO   | MUL | NULL    |       |
+------------+------------+------+-----+---------+-------+
5 rows in set (0.001 sec)

line_attribute.precedence - это иерархический уровень данного заголовка.Таким образом, если «Война и мир» имеют «Книги»> ​​«Главы», все строки имеют атрибут, соответствующий книге, в которой они находятся (например, «Книга 1» имеет precedence=1 и num=1), и атрибут главы, в которой они находятся(например, глава 2 имеет precedence=2 и num=2).Это позволяет мне переводить иерархическую структуру книг с томами, книгами, частями, главами, разделами или даже актами и сценами.Первичный столбец является логическим, поэтому каждая строка имеет атрибут one , который является первичным.Если это заголовок книги, это атрибут Book, если это заголовок главы, это атрибут Chapter.Если это обычная строка в тексте, это атрибут line, и приоритет равен 0, поскольку он не является частью иерархической структуры.

Мне нужно иметь возможность запрашивать все строки сконкретный edition_id, который также имеет пересечение двух атрибутов line_attributes.

(Это позволило бы мне получить все строки из определенного издания, скажем, в Книге 1, Глава 2 «Война и мир»).

Я могу получить все строки с Книгой 1 с помощью

SELECT
    line.*
FROM
    line
INNER JOIN
    line_attribute
ON
    line_attribute.line_id=line.id
WHERE
    line.edition_id=2 AND line_attribute.precedence=1 AND line_attribute.num=1;

и получить все строки с Главой 2:

SELECT
    line.*
FROM
    line
INNER JOIN
    line_attribute
ON
    line_attribute.line_id=line.id
WHERE
    line.edition_id=2 AND line_attribute.precedence=2 AND line_attribute.num=1;

За исключением возврата второго запросакаждая глава 2 из каждой книги «Война и мир».

Как мне получить из этих двух запросов всего строк из книги 1 главы 2?

1 Ответ

0 голосов
/ 02 февраля 2019

Предупреждение от Raymond Nijland в комментариях:

Примечание для будущих читателей .. Поскольку этот вопрос помечен MySQL .. MySQL не поддерживает ключевое слово INTERSECT .. MariaDB действительно является ответвлением отИсходный код MySQL, но поддерживает дополнительные функции, которые не поддерживает MySQL. В MySQL вы можете имитировать ключевое слово INTERSECT с INNER JOIN или IN ()

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

Решение был оператор INTERSECT.

Решение - это просто пересечение этих двух запросов:

SELECT
    line.*
FROM
    line
INNER JOIN
    line_attribute
ON
    line_attribute.line_id=line.id
WHERE
    line.edition_id=2 AND line_attribute.precedence=1 AND line_attribute.num=1

INTERSECT    /* it is literally this simple */

SELECT
    line.*
FROM
    line
INNER JOIN
    line_attribute
ON
    line_attribute.line_id=line.id
WHERE
    line.edition_id=2 AND line_attribute.precedence=2 AND line_attribute.num=2;

Это также означает, что я мог получить всю книгу и главузаголовки для конкретной книги, просто добавив дополнительное ограничение (line_attribute.primary=1).

Это решение кажется широко применимым для меня.Предположим, например, что у вас есть вопросы в клоне StackOverflow, помеченные тегами, вы можете получить пересечение вопросов с двумя тегами (например, все сообщения, имеющие теги SQLAlchemy и Python).Я, конечно, собираюсь использовать этот метод для такого рода запросов.

Я закодировал это в MySQL, потому что это помогает мне получить прямой запрос для перевода его в SQLAlchemy.

Запрос SQLAlchemyэто просто:

[nav] In [10]: q1 = Line.query.join(LineAttribute).filter(LineAttribute.precedence==1, LineAttribute.num==1)                              

[ins] In [11]: q2 = Line.query.join(LineAttribute).filter(LineAttribute.precedence==2, LineAttribute.num==1)                              

[ins] In [12]: q1.intersect(q2).all()  

Надеемся, что структура базы данных в этом вопросе поможет кому-то в будущем.Я не хотел удалять вопрос после того, как решил свою проблему.

...