多个JOIN(SQL)

dal*_*liz 1 sql jpa playframework

我的问题是玩!框架/ JPA具体.但我认为它适用于一般的SQL语法.

这是一个简单JOIN的示例查询:

return Post.find(
    "select distinct p from Post p join p.tags as t where t.name = ?", tag
).fetch();
Run Code Online (Sandbox Code Playgroud)

这很简单,效果很好.

我的问题是:如果我想在同一个表中加入更多值,该怎么办?

示例(不起作用.这是我创建的伪语法):

return Post.find(
    "select distinct p from Post p join p.tags1 as t, p.tags2 as u, p.tags3 as v where t.name = ?, u.name = ?, v.name = ?", tag1, tag2, tag3,
).fetch();
Run Code Online (Sandbox Code Playgroud)

All*_*n W 5

您的编程逻辑似乎没问题,但SQL语句需要一些工作.看起来你是SQL的新手,正如你所指出的,你似乎并不了解JOIN是什么.

您正尝试从名为POST,TAG1,TAG2和TAG3的4个表中选择数据.

我不知道这些表中有什么,并且很难在没有该信息的情况下提供示例SQL语句.所以,我只是为了讨论的目的而做些什么.假设表POST有6列,其中有8行数据.

P Fname Lname   Country Color Headgear
- ----- -----   ------- ----- --------
1 Alex  Andrews 1       1     0
2 Bob   Barker  2       3     0
3 Chuck Conners 1       5     0
4 Don   Duck    3       6     1
5 Ed    Edwards 2       4     2
6 Frank Farkle  4       2     1
7 Geoff Good    1       1     0
8 Hank  Howard  1       3     0
Run Code Online (Sandbox Code Playgroud)

我们会说TAG1,TAG2和TAG3是查找表,每个只有2列.表TAG1有4个国家代码:

C Name
- -------
1 USA
2 France
3 Germany
4 Spain
Run Code Online (Sandbox Code Playgroud)

表TAG2有6种颜色代码:

C Name
- ------
1 Red
2 Orange
3 Yellow
4 Green
5 Blue
6 Violet
Run Code Online (Sandbox Code Playgroud)

表TAG3有4个头盔代码:

C Name
- -------
0 None
1 Glasses
2 Hat
3 Monacle
Run Code Online (Sandbox Code Playgroud)

现在,当你从这4个表中选择数据时,对于P = 6,你试图得到这样的东西:

Fname Lname  Country Color  Headgear
----- ------ ------- ------ -------
Frank Farkle Spain   Orange None
Run Code Online (Sandbox Code Playgroud)

首先,让我们看看你的WHERE子句:

where t.name = ?, u.name = ?, v.name = ?
Run Code Online (Sandbox Code Playgroud)

抱歉,使用这样的逗号是语法错误.通常,您只想查找所有3个条件都为真的数据; 你通过使用AND来做到这一点:

where t.name=? AND u.name=? AND v.name=?
Run Code Online (Sandbox Code Playgroud)

第二,你为什么要一起加入桌子?因为您需要更多信息.表POST称Frank的COUNTRY值为4; 表格TAG1表示4表示西班牙.所以我们需要将这些表"加入"在一起.

古代(1980年以前,我认为)连接表的方法是在FROM子句中列出多个表名,用逗号分隔.这给了我们:

SELECT P.FNAME, P.LNAME, T.NAME As Country, U.NAME As Color, V.NAME As Headgear
FROM POST P, TAG1 T, TAG2 U, TAG3 V
Run Code Online (Sandbox Code Playgroud)

这个查询的问题在于你没有告诉它你想要哪些行,或者它们如何相互关联.因此,数据库生成称为"笛卡尔积"的东西.你想要一个笛卡尔积非常罕见 - 通常这是一个巨大的错误.即使您的数据库中只有22行,此SELECT语句将返回768行数据:

Alex Andrews USA   Red    None
Alex Andrews USA   Red    Glasses
Alex Andrews USA   Red    Hat
Alex Andrews USA   Red    Monacle
Alex Andrews USA   Orange None
Alex Andrews USA   Orange Glasses
...
Hank Howard  Spain Violet Monacle
Run Code Online (Sandbox Code Playgroud)

没错,它返回了4个表中每个可能的数据组合.想象一下,POST表最终会增长到20000行,而三个TAG表每行有100行.整个数据库将小于1兆字节,但笛卡尔积将拥有20,000,000,000行数据 - 可能大约120 GB的数据.任何数据库引擎都会阻塞它.

因此,如果您想使用古代指定表的方式,那么确保WHERE子句显示您正在查询的每个表之间的关系非常重要.这更有意义:

SELECT P.FNAME, P.LNAME, T.NAME As Country, U.NAME As Color, V.NAME As Headgear
FROM POST P, TAG1 T, TAG2 U, TAG3 V
WHERE P.Country=T.C AND P.Color=U.C AND P.Headgear=V.C
Run Code Online (Sandbox Code Playgroud)

这只返回8行数据.

使用古老的方式,很容易意外地创建笛卡尔积,这几乎总是坏的.所以他们修改了SQL以使其更难做到.这是JOIN关键字.现在,当您指定其他表时,您可以同时指定它们的关联方式.新方式是:

SELECT P.FNAME, P.LNAME, T.NAME As Country, U.NAME As Color, V.NAME As Headgear
FROM POST P
INNER JOIN TAG1 T ON P.Country=T.C
INNER JOIN TAG2 U ON P.Color=U.C
INNER JOIN TAG3 V ON P.Headgear=V.C
Run Code Online (Sandbox Code Playgroud)

您仍然可以使用WHERE子句.

SELECT P.FNAME, P.LNAME, T.NAME As Country, U.NAME As Color, V.NAME As Headgear
FROM POST P
INNER JOIN TAG1 T ON P.Country=T.C
INNER JOIN TAG2 U ON P.Color=U.C
INNER JOIN TAG3 V ON P.Headgear=V.C
WHERE P.P=?
Run Code Online (Sandbox Code Playgroud)

如果你调用它并传入值6,则只返回一行:

Fname Lname  Country Color  Headgear
----- ------ ------- ------ --------
Frank Farkle Spain   Orange None
Run Code Online (Sandbox Code Playgroud)