获取特定值的两个多对多关系的交集

时间:2019-02-02 14:07:24

标签: python mysql sqlalchemy mariadb intersection

N.B。我用SQLAlchemy和Python标记了该标签,因为问题的全部重点是开发一个查询以转换为SQLAlchemy。我发布的答案很清楚。它也适用于MySQL。

我有三个相互联系的表格,用来描述一本书。 (在下表说明中,我消除了手头问题的多余行。)

[mysql]
host        =   localhost
port        =   3306
charset     =   utf8mb4
dbname      =   myDatabase
username    =   myUsername
password    =   myPassword
options[20]     =   false   ;PDO::ATTR_EMULATE_PREPARES
options[1000]   =   true    ;PDO::MYSQL_ATTR_USE_BUFFERED_QUERY
options[3]      =   2       ;PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
options[19]     =   2       ;PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC

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) 是给定标题的层次结构级别。因此,如果“战争与和平”的“书籍”>“章节”,则所有行都具有与其所在的书籍相对应的属性(例如,书籍1具有line_attribute.precedenceprecedence=1)以及“章节”的属性它们所在的位置(例如,第2章包含num=1precedence=2)。这使我可以翻译书籍的层次结构,包括卷,书籍,零件,章节,章节,甚至是行为和场景。主列是一个布尔值,因此每一行都有 one 属性是主属性。如果是书籍标题,则为num=2属性;如果是书籍标题,则为Book属性。如果它是文本中的规则行,则它是Chapter属性,并且优先级为0,因为它不是层次结构的一部分。

我需要能够查询具有特定line且也具有两个line_attributes交集的所有行。

(这将使我能够从特定版本中获得《战争与和平》第二卷第一章的所有内容。)

我可以获取所有带有Book 1的行

edition_id

我可以得到所有包含第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=1 AND line_attribute.num=1;

除了第二个查询,返回《战争与和平》中每本书的第二章。

如何通过这两个查询来公正第一本书第二章的内容?

1 个答案:

答案 0 :(得分:0)

注释中来自Raymond Nijland的警告:

  

供以后的读者注意。.因为这个问题被标记为MySQL .. MySQL不支持INTERSECT关键字.. MariaDB确实是MySQL源代码的分支,但支持MySQL不支持的其他功能。在MySQL中,您可以simulate INTERSECT关键字加上INNER JOIN或IN()

尝试在SO上写一个问题可以帮助我弄清思路,并最终在必须提出问题之前解决问题。上面的查询比我最初的查询要清晰得多,而且这个问题本身也有很多答案,但是我从来没有找到一个关于intersect实用程序的明确答案,所以无论如何我都会发布这个答案。

解决方案 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克隆中有被标记的问题,则可以将问题与两个标签相交(例如,同时具有SQLAlchemyPython标签的所有帖子)。我肯定会在这种查询中使用这种方法。

我在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()  

希望这个问题中的数据库结构可以帮助某人。解决自己的问题后,我不想删除该问题。