【问题标题】:SQL query with multiple "IN" on same many-to-many table在同一个多对多表上具有多个“IN”的 SQL 查询
【发布时间】:2009-07-11 22:06:09
【问题描述】:

我有这样的 m2m 关系:

#main table
CREATE TABLE products_product (
    id integer NOT NULL,
    company_id integer,
    user_id integer,
    type_id integer NOT NULL,
    name character varying(100) NOT NULL,
    description character varying(200) NOT NULL,
    tags character varying(255) NOT NULL,
    image character varying(200) NOT NULL
);
#intermediate table
CREATE TABLE products_ingridientbound (
    id integer NOT NULL,
    ingridient_id integer NOT NULL,
    company_id integer NOT NULL,
    price double precision NOT NULL,
    active boolean NOT NULL,
    "asTopping" boolean NOT NULL
);
#final m2m table
CREATE TABLE products_ingridientproductbound (
    id integer NOT NULL,
    product_id integer NOT NULL,
    ingridient_id integer NOT NULL,
    "optionValue" integer NOT NULL,
    CONSTRAINT "products_ingridientproductbound_optionValue_check" CHECK (("optionValue" >= 0))
);

我想要做的就是得到产品,它有 2 个(在这个例子中)ingridient 组,一个 ID 在范围 (16, 17, 18, 19) 中,另一个在范围 (43, 44, 45) .我希望 ingridient ID 同时在两个组中。

我的查询看起来像这样(它实际上是由 django orm 生成的):

SELECT "products_product"."id","products_product"."name",
FROM "products_product"
INNER JOIN "products_ingridientproductbound" 
ON ("products_product"."id" = "products_ingridientproductbound"."product_id")
WHERE ("products_ingridientproductbound"."ingridient_id" IN (16, 17, 18, 19)
AND "products_ingridientproductbound"."ingridient_id" IN (43, 44, 45)) LIMIT 21

它给了我 0 个结果,但如果我只使用一组 IN 查询运行查询,它就会起作用!

这是我的“products_ingridientproductbound”表中的数据。我认为我的查询可以返回产品 3,因为它在两个组(16 和 45)中都是如此,但现在我有点困惑。 Screenshot of phpPgAdmin

【问题讨论】:

  • 从谷歌上的快速搜索来看,postgres 确实支持 intersect 运算符。看我的回答。
  • 您可以为开发人员做的最好的事情是在您离开时维护它(并使您在老板看来更专业) - 在数据库中正确拼写“成分”。这是一件小事,但它真的很重要。

标签: sql postgresql many-to-many


【解决方案1】:

你在你的两个 IN 子句中询问相同的字段在两个没有共同元素的集合中。因此,您总是会在其中一个子句中得到错误,因此您的 AND 将是错误的。

【讨论】:

    【解决方案2】:

    txwikinger 是对的。如果要过滤具有两个相关 ingridientbounds 的产品,则需要在查询中有两个 JOIN,如下所示:

    SELECT "products_product"."id","products_product"."name",
    FROM "products_product"
    INNER JOIN "products_ingridientproductbound" ig1
    ON ("products_product"."id" = ig1."product_id")
    INNER JOIN "products_ingridientproductbound" ig2
    ON ("products_product"."id" = ig2."product_id")
    WHERE (ig1."ingridient_id" IN (16, 17, 18, 19)
    AND ig2."ingridient_id" IN (43, 44, 45)) 
    LIMIT 21
    

    【讨论】:

      【解决方案3】:

      如果我了解您所追求的,那么执行 INNER JOIN 不是您要在这里寻找的,我会改用 subquery

      您想要同时包含两种成分的产品 ID 和名称,对吗?

      我没有测试这个语句,但它应该是这样的:

      SELECT "products_product"."id","products_product"."name",
      FROM "products_product"
      WHERE (
          EXISTS (SELECT 1 FROM "products_ingridientproductbound" WHERE "products_product"."id" = "products_ingridientproductbound"."product_id" AND "products_ingridientproductbound"."ingridient_id" IN (16, 17, 18, 19))
              AND
          EXISTS (SELECT 1 FROM "products_ingridientproductbound" WHERE "products_product"."id" = "products_ingridientproductbound"."product_id" AND "products_ingridientproductbound"."ingridient_id" IN (43, 44, 45))
      )
      LIMIT 21
      

      编辑:删除关于组合的愚蠢评论,感谢 jvanderh。

      【讨论】:

      • 他不能组合的原因是他需要两者都有的产品,如果他把它们组合起来,他会得到所有具有这些ID之一的产品
      • 它不仅有效,而且正是我所需要的! :) 这样我可以创建更多的 EXISTS 语句并将 em 传递给 Django ORM。这里的另一个问题是性能......
      【解决方案4】:

      在 SQL Server 中,您可以像这样使用 INTERSECT

      SELECT "products_product"."id","products_product"."name",
      FROM "products_product"
      INNER JOIN "products_ingridientproductbound" 
      ON ("products_product"."id" = "products_ingridientproductbound"."product_id")
      WHERE ("products_ingridientproductbound"."ingridient_id" IN (16, 17, 18, 19)
      
      INTERSECT 
      
      SELECT "products_product"."id","products_product"."name",
      FROM "products_product"
      INNER JOIN "products_ingridientproductbound" 
      ON ("products_product"."id" = "products_ingridientproductbound"."product_id")
      WHERE ("products_ingridientproductbound"."ingridient_id" IN (43, 44, 45)
      

      意思是返回两个查询中的行。检查您的数据库是否有该选项。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-03-29
        • 1970-01-01
        • 2011-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-04
        相关资源
        最近更新 更多