代码之家  ›  专栏  ›  技术社区  ›  GreenAsJade

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

  •  5
  • GreenAsJade  · 技术社区  · 8 年前

    由于 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)
    
    1 回复  |  直到 7 年前
        1
  •  3
  •   thirstycrow    8 年前

    这里的问题是, 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由一个始终为true的条件连接,从而产生临时结果:

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

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

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

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