通过可能的重复名称排序区分 id

Distinct on id with ordering by possible duplicate names

我有以下查询条件:

以下查询将消除重复的名称,这是不需要的,因为在 order by:

中使用,我不得不在 from_products_products.name 上加上不同的名称
SELECT DISTINCT ON (from_products_products.name, products.id) "products".* FROM "products" 
  INNER JOIN "suppliers_plugin_source_products" ON "suppliers_plugin_source_products"."to_product_id" = "products"."id"
  INNER JOIN "products" "from_products_products" ON "from_products_products"."id" = "suppliers_plugin_source_products"."from_product_id"
  INNER JOIN "suppliers_plugin_source_products" "sources_from_products_products_join" ON "sources_from_products_products_join"."to_product_id" = "products"."id"
  INNER JOIN "suppliers_plugin_suppliers" ON "suppliers_plugin_suppliers"."id" = "sources_from_products_products_join"."supplier_id"
  WHERE "products"."profile_id" = 45781 AND (("products"."type" IN ('SuppliersPlugin::DistributedProduct') OR "products"."type" IS NULL)) AND (products.archived <> true)
  ORDER BY from_products_products.name ASC, products.id

使用GROUP BY效果相同,同样不去重;

INNER JOIN 不匹配任何产品时给出重复 products 的原始查询:

SELECT "products".* FROM "products" 
  INNER JOIN "suppliers_plugin_source_products" ON "suppliers_plugin_source_products"."to_product_id" = "products"."id"
  INNER JOIN "products" "from_products_products" ON "from_products_products"."id" = "suppliers_plugin_source_products"."from_product_id"
  INNER JOIN "suppliers_plugin_source_products" "sources_from_products_products_join" ON "sources_from_products_products_join"."to_product_id" = "products"."id"
  INNER JOIN "suppliers_plugin_suppliers" ON "suppliers_plugin_suppliers"."id" = "sources_from_products_products_join"."supplier_id"
  WHERE "products"."profile_id" = 45781 AND (("products"."type" IN ('SuppliersPlugin::DistributedProduct') OR "products"."type" IS NULL)) AND (products.archived <> true)
  ORDER BY from_products_products.name ASC

那么,如何在 PostgreSQL 上克服这个问题?

PS:这是开源软件 Noosfero-ecosol

的一部分

这是你想要的吗?

with t as (
    SELECT DISTINCT ON (products.id) "products".*,
           from_products_products.name as from_products_name
    FROM "products" 
      INNER JOIN "suppliers_plugin_source_products" ON "suppliers_plugin_source_products"."to_product_id" = "products"."id"
      INNER JOIN "products" "from_products_products" ON "from_products_products"."id" = "suppliers_plugin_source_products"."from_product_id"
      INNER JOIN "suppliers_plugin_source_products" "sources_from_products_products_join" ON "sources_from_products_products_join"."to_product_id" = "products"."id"
      INNER JOIN "suppliers_plugin_suppliers" ON "suppliers_plugin_suppliers"."id" = "sources_from_products_products_join"."supplier_id"
      WHERE "products"."profile_id" = 45781 AND (("products"."type" IN ('SuppliersPlugin::DistributedProduct') OR "products"."type" IS NULL)) AND (products.archived <> true)
      ORDER BY products.id
     )
select t.*
from t
order by from_products_name

看来符合你的要求

编辑:

如果以上是你想要的,我可以想到五个方案:

  • 以上使用 CTE。
  • 基本相同的逻辑,使用子查询。
  • 使用window函数,结构非常相似
  • 使用 group by.
  • 为过滤逻辑使用 where 子句。

这里是 group by 方法:

    SELECT "products".*,
           MIN(from_products_products.name) as from_products_name
    FROM "products" 
      INNER JOIN "suppliers_plugin_source_products" ON "suppliers_plugin_source_products"."to_product_id" = "products"."id"
      INNER JOIN "products" "from_products_products" ON "from_products_products"."id" = "suppliers_plugin_source_products"."from_product_id"
      INNER JOIN "suppliers_plugin_source_products" "sources_from_products_products_join" ON "sources_from_products_products_join"."to_product_id" = "products"."id"
      INNER JOIN "suppliers_plugin_suppliers" ON "suppliers_plugin_suppliers"."id" = "sources_from_products_products_join"."supplier_id"
      WHERE "products"."profile_id" = 45781 AND (("products"."type" IN ('SuppliersPlugin::DistributedProduct') OR "products"."type" IS NULL)) AND (products.archived <> true)
      GROUP BY products.id
      ORDER BY from_products_name;

这种形式取决于 products.id 被声明为主键。或者,您可以将 table 中的所有列放入 group by.

重写(简化别名)产生:

SELECT p1.*
FROM products p1
INNER JOIN suppliers_plugin_source_products spsp
        ON spsp.to_product_id = p1.id
INNER JOIN products p2
        ON p2.id = spsp.from_product_id
INNER JOIN suppliers_plugin_source_products spsp2
        ON spsp2.to_product_id = p1.id -- <<-- Huh?
INNER JOIN suppliers_plugin_suppliers sps
        ON sps.id = spsp2.supplier_id
WHERE p1.profile_id = 45781
  AND (p1."type" IN ('SuppliersPlugin::DistributedProduct') OR p1."type" IS NULL)
  AND p1.archived <> true
ORDER BY p2.name ASC -- <<-- Huh?
    ;

外部查询仅引用产品tables p1 和p2。 假设 JOINing "suppliers_plugin_source_products" table 两次 是无意的,这可以简化为:

SELECT p1.*
FROM products p1
JOIN products p2
  ON EXISTS (
    SELECT * FROM suppliers_plugin_source_products spsp
    -- the next line might not be necessary ...
    INNER JOIN suppliers_plugin_suppliers sps ON sps.id = spsp.supplier_id
    WHERE spsp.to_product_id = p1.id
    AND spsp.from_product_id = p2.id
    )
WHERE p1.profile_id = 45781 
  AND (p1."type" IN ('SuppliersPlugin::DistributedProduct') OR p1."type" IS NULL)
  AND p1.archived <> true
ORDER BY p2.name ASC
    ;