如何在带有 MIN 的 SELECT 中包含第三列

Posted

技术标签:

【中文标题】如何在带有 MIN 的 SELECT 中包含第三列【英文标题】:How can I include third column in a SELECT with MIN 【发布时间】:2019-07-01 00:07:30 【问题描述】:

在这个查询中,我还想在同一个查询中选择第三列wp_postmeta.post_id。如果不通过涉及子查询的 JOIN 附加它,我无法弄清楚如何做到这一点。难道没有更简单的方法,不使用子查询吗? (如果解决方案高效且不太占用资源,则愿意在子查询上让步。)

MariaDB 10.1

create table wp_posts (
  ID integer primary key auto_increment,
  post_title varchar(30),
  post_type varchar(30)
);
create table wp_postmeta (
  ID integer primary key auto_increment,
  post_id integer,
  meta_key varchar(30) not null default '_regular_price',
  meta_value integer not null
);
insert into wp_posts (post_title, post_type) values
('Apple Pie','Product'),
('French Toast','Product'),
('Shepards Pie','Product'),
('Jam Pie','Product'),
('Jam Pie','Product'),
('Plate','Not a Product'),
('Bucket','Not a Product'),
('Chequebook','Not a Product'),
('French Toast','Product'),
('French Toast','Product'),
('Banana','Product'),
('Banana','Product'),
('Banana','Product');
insert into wp_postmeta (post_id, meta_value) values
(1,10),
(2,5),
(3,9),
(4,8),
(5,11),
(6,12),
(7,10),
(8,6),
(9,1),
(10,1),
(11,7),
(12,2),
(13,2);
SELECT wp_posts.post_title, MIN(wp_postmeta.meta_value) FROM wp_postmeta JOIN wp_posts
ON wp_postmeta.post_id = wp_posts.id 
WHERE wp_posts.post_type = 'Product' AND wp_postmeta.meta_key = '_regular_price' 
GROUP BY wp_posts.post_title
ORDER BY wp_posts.post_title
post_title |最小值(wp_postmeta.meta_value) :----------- | --------------------------: 苹果派 | 10 香蕉 | 2 法式吐司 | 1 果酱派 | 8 谢泼德派 | 9

db小提琴here

编辑:我最初的问题不够清楚和明确 - 我想折叠重复的 post_titles 并使用与 MIN(pm.meta_value) 对应的 wp_postmeta.post_id

【问题讨论】:

由于您的版本没有 CTE,请按照 [groupwise-maximum] 标签获取解决方案。 【参考方案1】:

您不能将其包含在GROUP BY 中吗?

SELECT p.post_title, p.id, MIN(pm.meta_value)
FROM wp_postmeta pm JOIN
     wp_posts p
     ON pm.post_id = p.id 
WHERE p.post_type = 'Product' AND pm.meta_key = '_regular_price' 
GROUP BY p.post_title, p.id
ORDER BY p.post_title;

post_idp.id 相同,所以你不妨使用它。

编辑:

如果标题可以重复,那么只需使用相关子查询:

SELECT p.post_title, p.id, pm.meta_value
FROM wp_postmeta pm JOIN
     wp_posts p
     ON pm.post_id = p.id 
WHERE p.post_type = 'Product' AND
      pm.meta_key = '_regular_price' AND
      pm.meta_value = (SELECT MIN(pm2.meta_value)
                       FROM wp_postmeta pm2 JOIN
                            wp_posts p2
                            ON pm2.post_id = p2.id 
                       WHERE p2.post_title = p.post_title
                      )
ORDER BY p.post_title;

【讨论】:

我试过了,但是我想折叠重复的post_titles并使用与MIN(pm.meta_value)对应的p.id【参考方案2】:

如果我没听错的话,你的问题可以使用窗口函数来解决:

SELECT x.post_title, x.id
FROM (
    SELECT wp_posts.post_title, wp_postmeta.id, ROW_NUMBER() OVER (PARTITION BY wp_posts.post_title ORDER BY wp_postmeta.meta_value) rn
    FROM wp_postmeta 
    INNER JOIN wp_posts ON wp_postmeta.post_id = wp_posts.id 
    WHERE wp_posts.post_type = 'Product' AND wp_postmeta.meta_key = '_regular_price'
) x WHERE x.rn = 1;

See this DB Fiddle demo

窗口函数是此类用例的最佳选择(如果我理解正确的话)。他们通常在效率和可读性方面胜过其他解决方案。


如果您的 RDBMS 不支持窗口函数,一种解决方案是使用具有 NOT EXISTS 条件的相关子查询,以确保给定帖子标题没有具有较小元值的记录。

SELECT p.post_title, MIN(m.id)
FROM 
    wp_postmeta m
    INNER JOIN wp_posts p 
        ON  p.id = m.post_id
        AND p.post_type = 'Product' 
WHERE 
    m.meta_key = '_regular_price'
    AND NOT EXISTS (
        SELECT 1
        FROM 
            wp_postmeta m1
            INNER JOIN wp_posts p1
                ON  p1.id = m1.post_id
                AND p1.post_type = 'Product'
        WHERE 
            m1.meta_key = '_regular_price'
            AND p1.post_title = p.post_title
            AND m1.meta_value < m.meta_value
     )
 GROUP BY p.post_title

DB Fiddle demo

注意:我在您的 SQL 中做了一些清理(添加了表别名并将一些条件移动到相关连接的 then ON 子句)。

【讨论】:

你可以为 MariaDB 10.1 做这个工作吗?实际上这是我最初的解决方案,但是窗口功能支持不完整 @ptrcao 这不是涉及到你不想要的子查询吗? @Matthias 好点。如果可以选择,我宁愿不使用子查询,因为它会使代码过于复杂,而且我发现多个子查询嵌入会因为某种原因而真正减慢操作。但是,如果这是你能做的最好的,我会接受的。但是,MariaDB 10.1 不支持窗口函数,因此除非我升级,否则这仍然不是一个可行的解决方案,这可能会在其他地方引入其他复杂性。 @ptrcao :我用没有窗口的解决方案更新了我的答案, @ptrcao :好的,我明白了,那是因为有行甚至meta_value...我切换到GROUP BY 并进行了测试,它似乎工作(见小提琴)。【参考方案3】:

您可以使用 CTE 托盘:

WITH T1
AS ( SELECT   post_title, MIN(id) AS ID
     FROM     wp_posts
     GROUP BY post_title )
SELECT   T1.ID, wp_posts.post_title, MIN(wp_postmeta.meta_value)
FROM     wp_postmeta
         JOIN wp_posts ON wp_postmeta.post_id = wp_posts.id
         JOIN T1 ON T1.post_title = wp_posts.post_title
WHERE    wp_posts.post_type = 'Product'
         AND wp_postmeta.meta_key = '_regular_price'
GROUP BY T1.ID,  wp_posts.post_title
ORDER BY wp_posts.post_title;

编辑:

您的表 wp_posts 应该如下所示

Id, post_title, post_type
1, 'Apple Pie','Product'
2, 'French Toast','Product'
3, 'Shepards Pie','Product'
4, 'Jam Pie','Product'
5, 'Plate','Not a Product'
6, 'Bucket','Not a Product'
7, 'Chequebook','Not a Product'
8, 'Banana','Product'

比你在 wp_postmeta 表中的数据这样

post_id, meta_value
1,  10
2,  5
3,  9
4,  8
4,  11
5,  12 
6,  10
7,  6
2,  1
2,  1
8,  7
8,  2
8,  2

查询看起来像这样

SELECT   wp_postmeta.post_id, wp_posts.post_title, MIN(wp_postmeta.meta_value)
FROM     wp_postmeta
         JOIN wp_posts ON wp_postmeta.post_id = wp_posts.id
WHERE    wp_posts.post_type = 'Product'
         AND wp_postmeta.meta_key = 'REG PRICE'
GROUP BY wp_postmeta.post_id,  wp_posts.post_title
ORDER BY wp_posts.post_title;

【讨论】:

这不保持wp_postmeta.meta_valueID之间的原始对应关系。不是我想要每个组的最小ID;我想要原始表行中与MIN(wp_postmeta.meta_value) 对应的行的ID 您的产品香蕉和法式吐司具有相同的 MIN 元值的重复 ID。如果在 T1 中不使用 MIN 函数,则会得到重复的 post_title。 好点。但是,是否有另一个可以使用的聚合函数,而不是使用组的最小 ID 值,将检索派生 MIN meta_value 的行的 ID? (我想维护ID和meta_value之间的关系)。谢谢 您应该防止表 wp_posts 中出现重复行。创建一张表,列出您的所有产品和非产品。该表应该有两列 ID 和名称。来自新表的 ID 将用于表 wp_postmeta 的 Post_Id 列中。 如果你有时间,请告诉我你的意思。但是,我担心在一个非常大的数据库中创建表的副本可能会占用大量资源?

以上是关于如何在带有 MIN 的 SELECT 中包含第三列的主要内容,如果未能解决你的问题,请参考以下文章

为啥 lib 模块不能在 android studio 中包含第三个 lib

生成带有三列名称的随机 SELECT sql 总是在那里

SQL Select:获取列中的上一个日期

Python - 发送带有多个图像附件的电子邮件

正确包含标题

SQL Select min(date) group by 对应的行