SQL,Arel和过滤记录的问题。轨。可以使用一些建议

时间:2017-06-08 19:33:00

标签: sql ruby-on-rails activerecord

我对SQL和Arel非常糟糕。我甚至不知道Arel是什么或如何使用它。有没有一个很好的资源呢?

我有一个问题,我不知道为什么花了这么长时间。也许人们可以指出为什么这需要我这么长时间。

无论如何,我有一个Products表和一个Restrictions表。产品可能有很多限制。限制是美国的州。我的大多数产品没有任何限制,但我的一个产品有限制(它不能出售在" CT"因为" CT"有疯狂的法律)。

我想退回所有对CT没有限制的产品。但是,此查询无效:

Product.joins(:restrictions).where.not(restrictions: { name: "CT" }).count

所以我希望能够退回我的所有产品,除了对CT有限制的产品。但是,它什么都不返回:

 Product.last.restrictions
  Product Load (0.5ms)  SELECT  "products".* FROM "products"  ORDER BY "products"."id" DESC LIMIT 1
  Restriction Load (0.3ms)  SELECT "restrictions".* FROM "restrictions" INNER JOIN "product_restrictions" ON "restrictions"."id" = "product_restrictions"."restriction_id" WHERE "product_restrictions"."product_id" = $1  [["product_id", 2559]]
[
  #<Restriction:0x007ff0f1dfec58> {
            :id => 11,
      :category => "US State",
          :name => "CT",
    :created_at => Wed, 07 Jun 2017 17:57:42 UTC +00:00,
    :updated_at => Wed, 07 Jun 2017 17:57:42 UTC +00:00
  }
]
[48] pry(main)> Product.count
   (0.6ms)  SELECT COUNT(*) FROM "products"
2494
[49] pry(main)> Product.joins(:restrictions).where.not(restrictions: { name: "AZ" }).count
   (47.8ms)  SELECT COUNT(*) FROM "products" INNER JOIN "product_restrictions" ON "product_restrictions"."product_id" = "products"."id" INNER JOIN "restrictions" ON "restrictions"."id" = "product_restrictions"."restriction_id" WHERE ("restrictions"."name" != $1)  [["name", "AZ"]]
1
[50] pry(main)> Product.joins(:restrictions).where.not(restrictions: { name: "CT" }).count
   (6.7ms)  SELECT COUNT(*) FROM "products" INNER JOIN "product_restrictions" ON "product_restrictions"."product_id" = "products"."id" INNER JOIN "restrictions" ON "restrictions"."id" = "product_restrictions"."restriction_id" WHERE ("restrictions"."name" != $1)  [["name", "CT"]]
0

我预计最后一个查询返回2493(只有最后一个产品有限制),而不是0.我希望AZ查询返回2494而不是0。

但是,这个引擎sql工作(使用NOT EXISTS):

state = "CT"
Product.where('NOT EXISTS ' \
    '(SELECT 1 from restrictions
        JOIN product_restrictions
          on product_id = products.id
        WHERE restrictions.name = ? AND restrictions.category = ?)', state, 'US State'
    )
  }

aboe查询返回所有产品,除了具有CT限制的产品外

问题

  1. 为什么我的第一个使用连接的查询不起作用?
  2. 我如何在这里变得更好?有关sql的好书吗?
  3. 有没有更好的方法来编写我的解决方案而不使用类似SQL字符串?

1 个答案:

答案 0 :(得分:0)

当您进行INNER JOIN时,它会创建一个连接表,其中只有具有相关限制的产品才有条目。 “没有限制的产品”或“没有product_id的限制”将不包括在内。

在你提到的问题中,“只有最后一个产品有限制”。因此,当您执行JOIN时,它在连接表中只创建了一个条目,当您在限制名称上进一步添加条件时,您就摆脱了它。因此最终结果是空的。

如果您想在连接表中包含产品,即使它们没有相关的限制,请使用LEFT OUTER JOIN。 这是一个有用的链接 - Inner Join vs Left Outer Join

解决方案: 如果使用rails5: -

Product.left_outer_joins(:restrictions).where.not(restrictions: { name: "CT" }).count

否则

Product.joins("LEFT OUTER JOIN restrictions on restrictions.product_id = products.id").where.not(restrictions: { name: "CT" }).count