【问题标题】:Why are these two Slick queries not equivalent?为什么这两个 Slick 查询不等价?
【发布时间】:2016-08-31 09:13:01
【问题描述】:

由于trying to make a Slick query more readable,我有这个查询构造函数,它可以工作

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)

【问题讨论】:

    标签: scala slick slick-3.0


    【解决方案1】:

    这里的问题是,slick uses a LiteralNode(true) as the default join condition. 所以第二个查询的结果如下:

       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 时,您应该会看到更多意想不到的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-09-12
      • 2011-03-18
      • 2018-01-21
      • 2013-04-14
      • 2019-05-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多