从分组方式中排除列

Posted

技术标签:

【中文标题】从分组方式中排除列【英文标题】:Exclude Column From Group By 【发布时间】:2015-09-17 05:56:31 【问题描述】:

我有一张供应表,其中列出了来自不同供应商的商品的价格:

| ID | Item | Price | Supplier |

ID 是主键(只是一个自动生成的整数)。 Item 是产品的名称。 价格是产品的价格。 Supplier 是外键(整数)。

我想列出每件商品的最便宜价格和供应商。

我对数据库比较陌生,到目前为止我已经掌握了这个:

SELECT Name, MIN(Price), Supplier FROM Supplies GROUP BY Name

这当然给了我错误

供应商不在聚合函数或 group by 子句中。

我已经在 Google 上进行了一些搜索,并阅读了很多关于该主题的文章和答案,但它们似乎都适用于比我复杂得多的情况,这让我感到困惑(如前所述,我没有太多的数据库经验和SQL)或者他们只是谈论错误而不是如何解决它。

任何帮助将不胜感激。提前致谢。

【问题讨论】:

您使用的是哪个 RDBMS? 如果两个或多个价格相同,是否需要任何平局逻辑? 使用 HSQLDB。如果两个或多个价格相同,我希望它同时显示。 【参考方案1】:

在大多数数据库中,您可以使用 ANSI 标准窗口函数:

select s.*
from (select s.*, min(price) over (partition by item) as minprice
      from supplies
     ) s
where price = minprice;

【讨论】:

@MatBailie 。 . .更有意义的是,目标是获得每件商品的最低价格,而不是每个供应商。【参考方案2】:

根据您的 RDBMS,您可以使用 ROW_NUMBER() 为每条记录分配排名并选择排名第一的记录。这比使用额外的连接或相关子查询要快,但目前 mysql 不支持。

WITH
    sorted_supplies AS
(

    SELECT
        supplies.*,
        ROW_NUMBER() OVER (PARTITION BY name ORDER BY price)   AS price_ordinal
    FROM
        supplies

)
SELECT
    *
FROM
    sorted_supplies
WHERE
    price_ordinal = 1
;

如果没有对 ROW_NUMBER() 的支持,那么您几乎会被引导到其他聚合和连接的道路上......

SELECT
    supplies.*
FROM
    supplies
INNER JOIN
(
    SELECT
        name,
        MIN(price)   AS min_price
    FROM
        supplies
    GROUP BY
        name
)
    AS min_prices
        ON  min_prices.name      = supplies.name
        AND min_prices.min_price = supplies.price

请注意,如果所有供应商都以最低价格绑定,则此查询将返回价格相同的所有供应商。

可以通过使用RANK() 而不是ROW_NUMBER() 来强制执行第一个查询

【讨论】:

第二种方法按需要工作。非常感谢您,也非常感谢您的快速响应。【参考方案3】:

您可以对结果进行排名,然后选择排名最低/最高的项目(基于排序顺序)。假设您使用的是 SQL Server 2008 或更高版本:

SELECT
    Item, Price, Supplier
FROM (
    SELECT
        ROW_NUMBER() OVER (PARTITION BY Item ORDER BY Price ASC) PriceRank
        , Item
        , Price
        , Supplier
    FROM
        Supplies
    ) supplies_ranked
WHERE
    PriceRank = 1

【讨论】:

【参考方案4】:

问题的症结在于 - 如果您有多个供应商以完全相同(最低)的价格提供齿轮,该怎么办?那么结果集中应该返回什么供应商呢?

完成您所追求的方法是获得最低价格,然后外部连接到产品 = 产品和价格 = 价格等的同一张表。但是请理解,如果您有上述情况,您将返回两行 - 每个供应商提供该最低价格。

自从我开始编写此代码以来,这里出现了 3 或 4 个新答案,所以我愿意猜测实际代码已经存在。我将把这段文字留在这里,作为“简单的英语”解释为什么查询不能按您期望的方式工作。

【讨论】:

【参考方案5】:

如果您没有partition by(一些类似 Access 的 sql-ish 环境),那么您也可以这样做:

select s.name, s.supplier, s.price from
(select name, min(price) as min_price from supplies group by name) mp
join supplies s on mp.name=s.name and mp.min_price = s.price 

这不如partition by 高效,因为它有一个子查询,可以查询每个名称的最低价格。请注意,如果出现平局,它将返回多个供应商;你可能想要也可能不想要。

【讨论】:

以上是关于从分组方式中排除列的主要内容,如果未能解决你的问题,请参考以下文章

如何从RegEx分组中“排除”空白区域?

MYSQL/HIVESQL笔试题:HIVESQL分组求TopN/行转列/列转行

熊猫 groupby 没有将按列分组转换为索引

如何从数据视图中选择单个分组列?

按同一列分组,但以两种不同的方式聚合

如何使用SQL语句消除重复列