如何编码深层嵌套的JOIN?

时间:2016-04-10 14:52:13

标签: mysql

我有五个表,让我们称它们为A到E,每个表都有一个唯一的密钥和一个外键,它是parent表的唯一键。

因此:A-> B-> C-> D-> E

A包含字段A_keyB_key; B有键B_keyC_key,依此类推......

鉴于A_key,我该如何SELECT * from E?这让我难过:-(

我需要嵌套的JOIN,还是什么?

为什么系统不让我提交问题而不在最后加上这个?为什么说它包含不正确的语法?

[更新]

我刚刚意识到我的说错了。

它是A->B->C->D<-E - 所有内容都指向“向下”,直到最后一个指向“向上”。

我看不出如何更改数据库。有没有办法调整@GiorgosBetsos的答案?

根据评论中的要求,以下是表格。鉴于campaign_id,我想SELECT * FROM taps

mysql> describe campaigns;
+--------------+------------+------+-----+---------+----------------+
| Field        | Type       | Null | Key | Default | Extra          |
+--------------+------------+------+-----+---------+----------------+
| campaign_id  | int(11)    | NO   | PRI | NULL    | auto_increment |
| title        | text       | NO   |     | NULL    |                |
| description  | text       | YES  |     | NULL    |                |
| path_to_logo | text       | YES  |     | NULL    |                |
| start_time   | text       | NO   |     | NULL    |                |
| end_time     | text       | NO   |     | NULL    |                |
| paused       | tinyint(1) | NO   |     | 0       |                |
+--------------+------------+------+-----+---------+----------------+
7 rows in set (0.00 sec)

mysql> describe destinations;
+----------------+---------------+------+-----+---------+----------------+
| Field          | Type          | Null | Key | Default | Extra          |
+----------------+---------------+------+-----+---------+----------------+
| destination_id | int(11)       | NO   | PRI | NULL    | auto_increment |
| campaign_id    | int(11)       | NO   | PRI | NULL    |                |
| url            | varchar(2048) | NO   |     | NULL    |                |
| description    | varchar(2048) | NO   |     | NULL    |                |
+----------------+---------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> describe connections;
+----------------+---------+------+-----+---------+----------------+
| Field          | Type    | Null | Key | Default | Extra          |
+----------------+---------+------+-----+---------+----------------+
| connection_id  | int(11) | NO   | MUL | NULL    | auto_increment |
| destination_id | int(11) | NO   |     | NULL    |                |
| description    | text    | NO   |     | NULL    |                |
| tag_id         | int(11) | YES  |     | NULL    |                |
| country        | text    | YES  |     | NULL    |                |
| county         | text    | YES  |     | NULL    |                |
| town           | text    | YES  |     | NULL    |                |
| post_code      | text    | YES  |     | NULL    |                |
| custom         | text    | YES  |     | NULL    |                |
+----------------+---------+------+-----+---------+----------------+
9 rows in set (0.00 sec)

mysql> describe tags;
+---------------+---------+------+-----+---------+-------+
| Field         | Type    | Null | Key | Default | Extra |
+---------------+---------+------+-----+---------+-------+
| tag_id        | int(11) | NO   | PRI | NULL    |       |
| tag_type      | int(11) | NO   |     | NULL    |       |
| customer_id   | int(11) | NO   | PRI | NULL    |       |
| connection_id | int(11) | NO   | PRI | NULL    |       |
+---------------+---------+------+-----+---------+-------+
4 rows in set (0.00 sec)

mysql> describe taps;
+-------------+-----------+------+-----+-------------------+-------+
| Field       | Type      | Null | Key | Default           | Extra |
+-------------+-----------+------+-----+-------------------+-------+
| tag_id      | int(11)   | NO   |     | NULL              |       |
| time_stamp  | timestamp | NO   |     | CURRENT_TIMESTAMP |       |
| device_id   | text      | YES  |     | NULL              |       |
| device_type | text      | YES  |     | NULL              |       |
+-------------+-----------+------+-----+-------------------+-------+
4 rows in set (0.00 sec)

2 个答案:

答案 0 :(得分:1)

这应该适合你:

SELECT * 
FROM E
WHERE EXISTS (
   SELECT 1
   FROM A
   JOIN B ON A.B_key = B.B_key
   JOIN C ON B.C_key = C.C_key
   JOIN D ON C.D_key = D.D_key
   WHERE D.E_key = E.E_key AND A.A_key = ? )

答案 1 :(得分:1)

以下查询将允许您选择taps表中的所有列:

SELECT
taps.tag_id,
taps.time_stamp,
taps.device_id,
taps.device_type
FROM campaigns AS camp
INNER JOIN destinations AS dest
ON dest.campaign_id = camp.campaign_id
INNER JOIN connections AS conn
ON conn.destination_id = dest.destination_id
INNER JOIN tags 
ON tags.connection_id = conn.connection_id
INNER JOIN taps
ON taps.tag_id = tags.tag_id
WHERE camp.campaign_id = :campaign_id

您可以使用SELECT替换taps.*之后的4行,但使用*是不好的做法,就像您将来添加任何列一样,您将开始回归不同的结果。

此外,此查询应该比Giorgos提供的查询更有效,因为EXISTS通常不会像JOIN那样进行优化。

(我发誓,我没有跟着你,试着提出你的答案,Giorgos,我碰巧碰到了两个与你相同的问题,并认为我可以提供更好的解决方案。)

从你如何描述你所遇到的问题来看,这是一个&#34;深度嵌套的JOIN&#34;表明您正在查看数据库关系不正确,因为许多人(包括我自己)在数据库相对缺乏经验的情况下首先会这样做。

使用集合思考简介

数据库将采用上面的INNER JOIN列表,并决定一个开始。在这种情况下,它将从campaigns表开始,因为它知道它可以从该表中获得最小的结果(一行,或者没有,如果没有与campaign_id匹配。)

从那里开始,它将构建一行,并且对于destinations表中的每个记录,它将找到所有具有匹配行ON campaign_id列的记录。从那里开始,它会将destination_id添加到其结果中,对于匹配的destinations表中的每一行,都会添加一个。

然后,对于其结果中的每一行(让我们说有3个目的地匹配),它会找到与其结果集中具有相同destination_id的所有连接匹配。然后,对于每个给定的connection_id,它会将destination_id添加到其结果中,每个行对应于连接表中的每一行。让我们说3个目的地分别有0,1和4个连接。这意味着数据库现在将结果设置为5行(destination_id没有任何connections从结果中删除,因为我们正在使用INNER JOIN s。)

该过程重复该行,直到它最终到达taps表,并且该表中的所有信息都被添加到结果中。最后,它只从它所持有的信息中获取所需的列,并从taps表中返回行。

重要的是,您没有告诉它要使用的订单。从数据库的角度来看,以下查询是等效的:

SELECT
taps.tag_id,
taps.time_stamp,
taps.device_id,
taps.device_type
FROM taps
INNER JOIN tags
ON taps.tag_id = tags.tag_id
INNER JOIN connections AS conn
ON tags.connection_id = conn.connection_id
INNER JOIN destinations AS dest
ON conn.destination_id = dest.destination_id
INNER JOIN campaigns AS camp
ON dest.campaign_id = camp.campaign_id
WHERE camp.campaign_id = :campaign_id

请注意,虽然从我们的角度来看订单是相反的,但数据库仍将按照与上述相同的顺序运行。这是因为数据库希望从最小的行集开始,然后一次向结果添加一个表。

您不必担心订单或任何嵌套。只担心通过ON语句告诉数据库如何比较表。您甚至可以将表(在此示例中)放在您想要的几乎任何顺序中,只要任何ON语句仅引用已定义的表(从我们的阅读角度,从上到下。)

我的第二个查询实际上可能更容易理解集合概念。从您想要的信息(taps表)开始,作为FROM子句。然后启动JOIN表,使您更接近WHERE子句中的信息,直到您为数据库引擎提供足够的信息,将事物拼凑在一起并获取所需的信息。

希望这有助于让你开始思考正确的道路(正如有人为我做的,大约5年前)。如果您有任何问题,请随时提出,我将使用这些来改进我的解释。