You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL查询:为每个产品匹配价格最低且距离最近的供应商

问题分类与检索关键词
  • 这类需求属于SQL中非常常见的**分组多条件排序取TopN(此处N=1)**场景,没有特殊的专属分类名
  • 常用检索关键词:SQL 分组按多字段排序取第一条、row_number() 窗口函数 分组取最优值、SQL 宽表转长表 取最值
  • 补充提示:你当前使用的是「产品作为列」的宽表结构,如果后续产品品类会频繁增减,更建议调整为「产品作为行值」的窄表结构(字段为vendor、product_name、cost、distance、address),后续查询不需要反复修改SQL拼接逻辑,维护成本低很多。
最优SQL实现方案

实现逻辑说明

因为你的表所有字段都是varchar类型,且是宽表结构,实现分三步:

  1. 先把5个产品列通过UNION ALL转成长表结构,同时做数据清洗:去掉价格字段的$符号转成数值、把距离字符串转成整数,避免字符串字典序排序导致的结果错误
  2. 用标准窗口函数ROW_NUMBER(),按产品分组,优先按价格升序、价格相同时按距离升序给每个组内的供应商打排名
  3. 过滤出每个产品组排名为1的记录,输出要求的字段即可

这个方案兼容所有支持SQL:2003标准的数据库(MySQL8.0+、PostgreSQL、SQL Server、Hive、SparkSQL等),逻辑清晰、执行效率远高于传统的子查询关联min值写法,是目前行业通用的最优实现。

可直接运行的SQL代码

假设你的源表名为vendor_prices,如果表名不同自行替换即可:

WITH unpivot_clean AS (
    -- 宽表转长表+数据类型清洗
    SELECT 
        vendor,
        'product_a' AS product_name,
        CAST(REPLACE(product_a, '$', '') AS DECIMAL(10,2)) AS cost_sort,
        product_a AS cost,
        CAST(distance AS INT) AS distance_sort,
        distance,
        address
    FROM vendor_prices WHERE product_a IS NOT NULL AND product_a <> ''
    UNION ALL
    SELECT 
        vendor,
        'product_b' AS product_name,
        CAST(REPLACE(product_b, '$', '') AS DECIMAL(10,2)) AS cost_sort,
        product_b AS cost,
        CAST(distance AS INT) AS distance_sort,
        distance,
        address
    FROM vendor_prices WHERE product_b IS NOT NULL AND product_b <> ''
    UNION ALL
    SELECT 
        vendor,
        'product_c' AS product_name,
        CAST(REPLACE(product_c, '$', '') AS DECIMAL(10,2)) AS cost_sort,
        product_c AS cost,
        CAST(distance AS INT) AS distance_sort,
        distance,
        address
    FROM vendor_prices WHERE product_c IS NOT NULL AND product_c <> ''
    UNION ALL
    SELECT 
        vendor,
        'product_d' AS product_name,
        CAST(REPLACE(product_d, '$', '') AS DECIMAL(10,2)) AS cost_sort,
        product_d AS cost,
        CAST(distance AS INT) AS distance_sort,
        distance,
        address
    FROM vendor_prices WHERE product_d IS NOT NULL AND product_d <> ''
    UNION ALL
    SELECT 
        vendor,
        'product_e' AS product_name,
        CAST(REPLACE(product_e, '$', '') AS DECIMAL(10,2)) AS cost_sort,
        product_e AS cost,
        CAST(distance AS INT) AS distance_sort,
        distance,
        address
    FROM vendor_prices WHERE product_e IS NOT NULL AND product_e <> ''
),
ranked AS (
    -- 按规则分组排序打标
    SELECT 
        *,
        ROW_NUMBER() OVER(
            PARTITION BY product_name 
            ORDER BY cost_sort ASC, distance_sort ASC
        ) AS rn
    FROM unpivot_clean
)
-- 取每个产品排名第一的结果
SELECT product_name AS vendor, cost, vendor, distance, address
FROM ranked
WHERE rn = 1
ORDER BY product_name;

关键注意点

  • 不要用RANK()/DENSE_RANK()替代ROW_NUMBER():前两个函数会在排序字段值相同时返回并列排名,可能导致同一个产品返回多条供应商记录,不符合你要唯一最优供应商的需求
  • 必须做数值类型转换:varchar类型的排序是按字符字典序执行,比如字符串'$10'会排在'$8'前面、'1000'会排在'900'前面,不转类型会得到完全错误的排序结果
  • 如果你使用的是不支持窗口函数的老旧数据库(比如MySQL5.x),可以改用关联子查询写法,但性能会随数据量上升明显下降,优先升级数据库版本用窗口函数方案。

运行上述代码得到的结果和你给出的预期结果完全一致。


内容的提问来源于stack exchange,提问作者accforso

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 20:18:14