【问题标题】:SQL "GROUP BY" issueSQL“GROUP BY”问题
【发布时间】:2010-12-18 05:38:20
【问题描述】:

我正在设计一个购物车。为了避免旧发票在产品价格更改后显示不准确定价的问题,我将 Product 表中的 price 字段移动到 ProductPrice 表中,该表包含 3 个字段,pid、date 和 price。 pid 和 date 构成表的主键。下面是表格的示例:

pid    date     price
1      1/1/09   50
1      2/1/09   55
1      3/1/09   54

使用SELECTGROUP BY查找每个产品的最新价格,我想出了:

SELECT pid, price, max(date) FROM ProductPrice GROUP BY pid

返回的日期和 pid 准确无误。对于每个唯一的 pid,我正好收到 1 个条目,并且伴随它的日期是该 pid 的最新日期。然而,令人惊讶的是价格返回。它返回与 pid 匹配的第一行的价格,在本例中为 50。

修改我的声明后,我想出了这个:

SELECT pp.pid, pp.price, pp.date FROM ProductPrice AS pp
INNER JOIN (
    SELECT pid AS lastPid, max(date) AS lastDate FROM ProductPrice GROUP BY pid
) AS m
ON pp.pid = lastPid AND pp.date = lastDate

虽然重新设计的语句现在产生了正确的价格(54),但如此简单的查询需要内部连接才能执行,这似乎令人难以置信。我的问题是,我的第二个陈述是完成我需要做的最简单的方法吗?或者我在这里错过了什么?提前致谢!

詹姆斯

【问题讨论】:

  • 更简单,尽管仅在 Postgresql 上:SELECT DISTINCT ON(pid) pid, date, price FROM ProductPrice ORDER BY pid, date DESC

标签: sql select mysql group-by


【解决方案1】:

您获得任意价格的原因是如果您GROUP BY 某事,mysql 无法知道要选择哪些列。它知道每个 pid 需要 a 价格和 a 日期,并且可以按照您使用 max(date) 的请求获取最新日期,但选择返回对他最有效的价格检索 - 您没有为该列提供 aggregate function(实际上,您的第一个查询不是有效的 SQL。)

您的第二个查询看起来不错,但这里有一个更短的选择:

SELECT pid, price, date
FROM ProductPrice p
WHERE date = (SELECT MAX(date) FROM ProductPrice tmp WHERE tmp.pid = p.pid)

但是,如果您经常访问最新价格(我认为您会这样做),如果您可以选择再次更改数据库结构,我建议您将旧列添加回原始表以保存最新值。

【讨论】:

  • 其他 DBMS 会抱怨价格未包含在聚合或分组中。
  • 我不得不承认这更干净。
【解决方案2】:

我认为你破坏了你的数据库架构。

为了避免旧发票在产品价格更改后显示不准确定价的问题,我将价格字段从 Product 表移到 ProductPrice 表中,该表包含 3 个字段:pid、date 和 price。 pid 和 date 构成表的主键。

正如您所指出的,您需要保留价格的变化历史。但是除了那个新表之外,您仍然可以在 products 表中保留当前价格。这将使您的生活更轻松(并且您的查询更快)。

【讨论】:

    【解决方案3】:

    您无法使用 GROUP BY 子句解决您的问题,因为对于每组 pid,MySQL 将简单地获取第一个 pid、最大日期和找到的第一个价格(这不是您需要的)。

    您可以使用子查询(可能效率低下):

    SELECT pid, date, price
    FROM   ProductPrice p1
    WHERE  date = ( SELECT MAX(p2.date)
                    FROM ProductPrice p2
                    WHERE p1.pid = p2.pid)
    

    或者你可以简单地加入表格:

    SELECT    p1.pid, p1.date, p1.price
    FROM      ProductPrice p1
    LEFT JOIN ProductPrice p2 ON p1.pid = p2.pid
              AND p1.date < p2.date
    WHERE     p2.pid IS NULL
    

    查看this section 的 MySQL 文档。

    【讨论】:

      【解决方案4】:

      你可能想试试这个:

      SELECT pid, price, date FROM ProductPrice GROUP BY pid ORDER BY date DESC
      

      组有一些模糊的功能,我总是不确定它是否是正确的字段...但它应该是结果集中的第一个。

      【讨论】:

      • 这行不通。它按日期降序排列结果表,而不是原始表,因此不会选择最新价格。
      【解决方案5】:

      这是另一个 - 可能效率低下的 - 一个:

      SELECT pid, substring_index( group_concat( price order by date desc ), ',', 1 ) , max(date)
        FROM ProductPrice
      GROUP BY pid
      

      【讨论】:

        【解决方案6】:

        我认为这里的关键是简单的听起来查询 - 你可以看到你想要的,但计算机不是人类,因此要从基于集合的操作中产生所需的结果,你必须明确表示为在第二个查询中。

        内部查询确定每个产品的最后价格,然后外部查询让您获得最后价格的值 - 这几乎是最简单的。

        顺便说一句,如果您有发票系统,您确实应该将产品价格(以及税率以及“代码”)与发票一起存储,即发票表应该包含所有必要的财务信息信息以复制发票。通常,您希望能够在可变表中查找价格(或税率),即使允许上述系统。无论如何,定价历史都有其优点。

        【讨论】:

          【解决方案7】:

          我在我的一个项目中遇到了同样的问题,我使用子查询来获取日期然后进行比较,但是当数据增加时它会使系统变慢。因此,除了您为保存价格变化历史而创建的新表之外,最好在您的 Products 表中存储最新价格。

          您可以随时使用 ppl 建议的任何查询来获取特定日期的产品最新价格。但您也可以在同一张表中添加一个字段是最新的。因此,在某个日期,您可以将 flag 设为 true 一次。并且您总是可以通过一个简单的查询找到特定日期的产品最新价格。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-05-13
            • 2018-09-07
            • 2015-03-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多