仅当所有关联记录符合条件

时间:2017-06-27 14:23:16

标签: sql ruby-on-rails ruby postgresql

我们有:

class Campaign::Item < ActiveRecord::Base
  belongs_to :campaign

  has_many :criteria
end

class Campaign::Item::Criterium < ActiveRecord::Base
  belongs_to :campaign_item

  # This model has a +type+ field.
end

class Campaign::Item::Criterium::Gender < Campaign::Item::Criterium
  belongs_to :campaign_item

  # This model uses the a +gender+ field.
end

class Campaign::Item::Criterium::Age < Campaign::Item::Criterium
  belongs_to :campaign_item

  # This model uses the +age_min+ and +age_max+ fields.
end

class User
  # This model has a +gender+ and +birth_date+ field.
end

如您所见,用户可以使用其中的项目制作广告系列。 每个广告系列项目都可以根据性别和年龄制定许多条件。

现在我们正在尝试列出与当前用户信息匹配的所有广告系列项目。

例如:
当前用户为female并且25 years old 此用户应该能够看到只有female Gender标准的项目 它还应该能够看到具有female Gender标准和Age between 18 and 30 years old标准的项目。
但是,此用户不应该看到具有female Gender标准和Age between 30 and 35 years old标准的项目。

因此,此处的目标是返回所有Campaign::Item,其中 all 的条件与当前用户的信息相匹配。 目前,我们只设法返回Campaign::Item,其中至少一个标准与当前用户的信息相匹配。

以下是我们尝试实现此目标的一些查询示例:

SELECT "campaign_items".*
FROM "campaign_items"
INNER JOIN "campaign_item_criteria"
  ON "campaign_item_criteria"."campaign_item_id" = "campaign_items"."id"
WHERE
  ("campaign_item_criteria"."type" = 'Campaign::Item::Criterium::Gender'
   AND ("campaign_item_criteria"."gender" IS NULL
        OR "campaign_item_criteria"."gender" = 'female'))
  AND ("campaign_item_criteria"."type" = 'Campaign::Item::Criterium::Age'
       AND ("campaign_item_criteria"."age_min" IS NULL
            OR "campaign_item_criteria"."age_min" <= 17)
       AND ("campaign_item_criteria"."age_max" IS NULL
            OR "campaign_item_criteria"."age_max" >= 17))

这个不起作用,因为它试图找到同时具有Campaign::Item::Criterium::GenderCampaign::Item::Criterium::Age类型的标准,显然,这不会很快就会出现这种情况。

SELECT "campaign_items".*
FROM "campaign_items"
INNER JOIN "campaign_item_criteria"
  ON "campaign_item_criteria"."campaign_item_id" = "campaign_items"."id"
WHERE
CASE
  WHEN "campaign_item_criteria"."type" = 'Campaign::Item::Criterium::Gender'
       AND "campaign_item_criteria"."gender" = 'female'
       THEN 1
  WHEN "campaign_item_criteria"."type" = 'Campaign::Item::Criterium::Age'
       AND "campaign_item_criteria"."age_min" <= 17
       AND ("campaign_item_criteria"."age_max" IS NULL OR "campaign_item_criteria"."age_max" >= 17)
       THEN 1
  ELSE 0
END = 1

即使其他条件与用户的信息不匹配,只要一个标准与用户的信息匹配,它就会返回Campaign::Item,这也不起作用。< / p>

现在我们的想法已经不多了。我们是否有机会在纯SQL中按预期工作?有人有任何导致吗?

提前致谢!

2 个答案:

答案 0 :(得分:0)

为什么不在where子句中使用exists? (简写因为f ***再次输入所有内容)

SELECT ci.*
FROM campaign_items ci
WHERE exists (select 1
              from campaign_item_criteria cic
              where cic.campaign_item_id = ci.id
              and cic.type = '::gender'
              and (cic.gender = 'female' or cic.gender is null)
             )
and exists (select 1
              from campaign_item_criteria cic
              where cic.campaign_item_id = ci.id
              and cic.type = '::age'
              and (cic.age = '35' or cic.age is null)
             )

答案 1 :(得分:0)

我能够找到一个完全符合我要求的查询:

SELECT DISTINCT "campaign_items".*
FROM "campaign_items"
INNER JOIN "campaign_item_criteria"
  ON "campaign_item_criteria"."campaign_item_id" = "campaign_items"."id"
WHERE
  (NOT EXISTS (SELECT 1
               FROM "campaign_item_criteria"
               WHERE "campaign_item_criteria"."campaign_item_id" = "campaign_items"."id"
                     AND "campaign_item_criteria"."type" = 'Campaign::Item::Criterium::Gender')
   OR EXISTS (SELECT 1
              FROM "campaign_item_criteria"
              WHERE "campaign_item_criteria"."campaign_item_id" = "campaign_items"."id"
                    AND "campaign_item_criteria"."type" = 'Campaign::Item::Criterium::Gender'
                    AND "campaign_item_criteria"."gender" = 'female'))
  AND (NOT EXISTS (SELECT 1
                   FROM "campaign_item_criteria"
                   WHERE "campaign_item_criteria"."campaign_item_id" = "campaign_items"."id"
                         AND "campaign_item_criteria"."type" = 'Campaign::Item::Criterium::Age')
       OR EXISTS (SELECT 1
                  FROM "campaign_item_criteria"
                  WHERE "campaign_item_criteria"."campaign_item_id" = "campaign_items"."id"
                        AND "campaign_item_criteria"."type" = 'Campaign::Item::Criterium::Age'
                        AND "campaign_item_criteria"."age_min" <= 17
                        AND ("campaign_item_criteria"."age_max" IS NULL
                             OR "campaign_item_criteria"."age_max" >= 17)))

这样就可以查询2个条件并且它有4个子查询。我们已经知道我们最终会得到~10个不同的标准。因此,最终请求的总共20个子查询 目前(有2个标准),数据库中需要大约2.5ms,2 Campaign::Item,数据库需要大约16ms和2 000 Campaign::Item。它并不像我们想象的那样具有指数性,因此我们会说它足够好&#39;。

我们现在要保留这个 有没有人可以添加任何内容或改进任何路径?