为什么这两个Slick查询不相等?



为了使Slick查询更具可读性,我有了这个查询构造函数,它可以

工作
val q = Users.filter(_.id === userId) join People on {
  case (u, p) => u.personId === p.id
} joinLeft Addresses on {
  case ((u, p), a) => p.addressId === a.id
} joinLeft Businesses on {
  case (((u, p), a), b) => p.businessId === b.id
} joinLeft Addresses on {
  case ((((u, p), a), b), ba) => b.flatMap(_.addressId) === ba.id
} map {
  case ((((u, p), a), b), ba) => (p, a, b, ba)
}

我认为这个是等价的,但是不起作用:

val q = Users.filter(_.id === userId) join People joinLeft Addresses joinLeft Businesses joinLeft Addresses on {
  case ((((u, p), a), b), ba) =>
    u.personId === p.id &&
    p.addressId === a.flatMap(_.id) &&
    p.businessId === b.flatMap(_.id) &&
    b.flatMap(_.addressId) === ba.id
} map {
  case ((((u, p), a), b), ba) => (p, a, b, ba)
} 

第二个似乎是返回一个配置文件列表,其中包含的配置文件多于目标配置文件。

为什么它们不一样?


"等效"SQL(即此构造的目标)是:
select p.*, a1.*, b.*, a2.* from Users u 
innerJoin People p on (u.personId == p.id) 
leftJoin Addresses a1 on (p.addressId == a1.id) 
leftJoin Businesses b on (p.businessId == b.id) 
leftJoin Addresses a2 on ( b.addressId == a2.id)

这里的问题是,slick使用LiteralNode(true)作为默认连接条件。因此,第二个查询将产生如下内容:

   select p.*, a1.*, b.*, a2.*
     from Users u 
     join People p on 1 = 1
left join Addresses a1 on 1 = 1
left join Businesses b on 1 = 1 
left join Addresses a2 on u.personId = p.id
                      and p.addressId = a1.id
                      and p.businessId = b.id
                      and b.addressId = a2.id

正如您所看到的,所有被期望作为每个连接表的连接条件的条件,实际上都是最后一个连接的连接条件的一部分。

为了理解这将如何影响最终结果,让我们将问题简化如下:

create temporary table A (id int primary key);
insert into A values (1), (2);
   select a1.id, a2.id, a3.id
     from A a1
     join A a2 on 1 = 1
left join A a3 on a1.id = a2.id
              and a2.id = a3.id;

在第一次连接时,a1和a2通过一个始终为真的条件连接,导致临时结果为:

(1, 1)
(1, 2)
(2, 1)
(2, 2)

现在让我们考虑第二个连接。我们将a1.id = a2.id作为连接条件的一部分,但请记住,连接条件用于决定如何从表a3中检索行,而不是过滤第一个连接的中间结果。我们在这里做的是一个左连接,所以即使不满足连接条件,也会生成一个额外的a3行NULL。最终结果将是:

(1, 1, 1)
(1, 2, NULL)
(2, 1, NULL)
(2, 2, 2)

因此,如果最后一个左连接表的列为NULL,您将看到更多意想不到的结果。

最新更新