如何在 MySQL < 8.0 中模拟窗口函数

Posted

技术标签:

【中文标题】如何在 MySQL < 8.0 中模拟窗口函数【英文标题】:How to emulate window functions in MySQL < 8.0 【发布时间】:2019-11-24 14:53:34 【问题描述】:

我有这样的表格:

INSERT INTO listings 
(id, external_id, variation_id, product_id) VALUES
(101, '9900001', '9900001var1', 1),
(102, '9900001', '9900001var2', 4),
(103, '9900002', '9900002var1', 1),
(104, '9900002', '9900002var2', 2),
(105, '9900003', '9900003var1', 3),
(106, '9900003', '9900003var2', 4);

INSERT INTO products
(id, price) VALUES
(1, 101),
(2, 100),
(3, 100),
(4, 102);

这意味着有 3 个列表(9900001、9900002、9900003),每个列表分别包含 2 个产品(1、4)、(1、2)和(3、4)。

我需要为每个列表检索一行,其中包含该列表中价格最高的产品的 ID(不是价格)。

因此,所需的输出将是:

id    | external_id | variation_id | product_id
[ANY]   9900001       [ANY]          4
[ANY]   9900002       [ANY]          1
[ANY]   9900003       [ANY]          4

我得到的最接近所需答案的是这个查询:

SELECT
    p.id AS product_id_max,
    p.price AS product_price_max,
    MAX(p.price) AS product_price_max_max,
    listings.* 
            FROM listings
            INNER JOIN (

              -- Subquery tested above:
              SELECT DISTINCT pp3.* FROM 
                (SELECT MAX(p2.price) as max_price
                FROM products p2
                INNER JOIN listings l2 ON l2.product_id = p2.id 
                GROUP BY l2.external_id) pp2
              INNER JOIN
                (SELECT p3.* FROM products p3 ) pp3
              ON
                pp2.max_price = pp3.price
              ORDER BY pp3.price DESC

            ) AS p
            ON p.id = listings.product_id
            -- WHERE MAX(p.price) = p.price
            GROUP BY external_id
            -- HAVING MAX(p.price) = p.price
            ORDER BY product_price_max DESC

取消注释 WHERE 子句会引发错误,取消注释 HAVING 子句返回的行数少于所需的行数。 Levin 都评论说在 product_id 列中给出了正确的行但错误的值。

小提琴:http://sqlfiddle.com/#!9/d58d665/54/0

【问题讨论】:

@bill-karwin 您发布的链接回答了不同的问题。听起来相似,但问题不同。 好的,我把重复问题的链接倒过来了。我会留下greatest-n-per-group 标签,因为它属于一般类型的问题。 自从 mysql 8.0 增加了窗口函数,每组最大 n 的每个问题都应该包括 MySQL 的版本。如果你可以使用 MySQL 8.0+,你应该使用窗口函数。 哇!窗口函数是我一直在寻找的答案!我使用 MariaDB 10.1.22,但我想我可以升级到 10.2,如果他们添加了对这些的支持。谢谢! 我将很快编辑这个问题以反映这里真正取得的成就。现在我改变了标题。 【参考方案1】:

我想通了:

SELECT
    listings.*,
    max_p.product_id AS max_product_id
            FROM listings
            INNER JOIN (

              SELECT DISTINCT max_external_id as external_id, pp3.id as product_id FROM
                (SELECT MAX(p2.price) as max_price, l2.external_id as max_external_id
                FROM products p2
                INNER JOIN listings l2 ON l2.product_id = p2.id
                GROUP BY l2.external_id) pp2
              INNER JOIN
                (SELECT p3.* FROM products p3 ) pp3
              ON
                pp2.max_price = pp3.price
              ORDER BY pp3.price DESC

            ) AS max_p
            ON max_p.external_id = listings.external_id
            GROUP BY listings.external_id

在这里测试:

http://sqlfiddle.com/#!9/d58d665/53/0

【讨论】:

以上是关于如何在 MySQL < 8.0 中模拟窗口函数的主要内容,如果未能解决你的问题,请参考以下文章

Navicat 8.0 MySQL 的快捷键

新特性解读 | MySQL 8.0 窗口函数框架用法

MySQL 8.0 新增SQL语法对窗口函数和CTE的支持

Windows环境下部署MySQL主从并模拟升级到8.0

如何安装MySQL 5.0?

如何在 Xcode 6.1 中安装 iOS 7.0 和 iOS 8.0 模拟器?