如何在mysql中联合后获取最小值?

Posted

技术标签:

【中文标题】如何在mysql中联合后获取最小值?【英文标题】:How to get the min value after an union in mysql? 【发布时间】:2021-10-27 03:57:21 【问题描述】:

假设我有 2 个表 A 和 B。

这 2 个表共有 3 列,分别是 Name、Id 和 Price。

这是我用于 1 个表的查询:

SELECT Name, Id, Price FROM A WHERE Id = "123" and Price = (SELECT MIN(Price) FROM A);

我刚刚意识到,当最低价格由另一个 Id 持有时,此查询不起作用。

所以我环顾四周,我认为我应该使用 GROUP BY ?

我已将其更改为:

SELECT Name, MIN(Price) FROM A WHERE Id = "123" GROUP BY Name;

但这不是预期的结果。

假设在表 A 中我有:

Name Id Price
Au 123 12
Be 123 16
St 122 9
Ge 123 10

对于表 B,我有:

Name Id Price
La 123 14.5
La 123 12
St 123 13
Is 123 12
Is 123 10
La 123 10
Is 123 10

而预期的结果是:

Name Price
Ge 10
Is 10
La 10

预期结果是 1 行长,因为在数据集中只有一行与条件匹配,但如果我有另一行价格为 10 且 ID 为 123,它也应该存在。因此,如果有更多符合条件的行,我希望它们出现在结果中。

问题是,当我使用 UNION 执行以下查询时,我不知道如何获得特定 ID 的最低价格:

SELECT Name, Id, Price FROM A UNION SELECT Name, Id, Price FROM B;

那么我可以在查询中添加什么以获得预期的结果,然后如果我使用联合来获得 2 个表中特定 ID 的最低价格,它将如何工作?

【问题讨论】:

你的 B 表数据呢? 您的第一个查询应该会容易得多:SELECT Name, Id, Price FROM A WHERE Id = "123" order by Price limit 1; 我已经编辑了我的帖子以匹配我当前的数据和预期的结果 【参考方案1】:

mysql 8+ 上,我们可以在这里使用 RANK 和联合查询:

WITH cte AS (
    SELECT Name, Id, Price FROM A WHERE Id = '123'
    UNION ALL
    SELECT Name, Id, Price FROM B WHERE Id = '123'
),
cte2 AS (
    SELECT *, RANK() OVER (ORDER BY price) rnk
    FROM cte
)

SELECT Name, Id, Price
FROM cte2
WHERE rnk = 1;

这是一个适用于早期版本的 MySQL 的查询:

SELECT Name, Id, Price
FROM
(
    SELECT Name, Id, Price FROM A WHERE Id = '123'
    UNION ALL
    SELECT Name, Id, Price FROM B WHERE Id = '123'
) t
WHERE Price = (
    SELECT Price FROM A WHERE Id = '123'
    UNION ALL
    SELECT Price FROM B WHERE Id = '123'
    ORDER BY Price
    LIMIT 1
);

【讨论】:

感谢您的回答,但我不想将单个记录作为输出我实际上想要所有符合我的条件的记录,我想要特定 ID 的最低价格值 @DataM 如果可能有多个记录,那么我建议在这里使用RANK,q.v。我在上面的更新答案。 我在phpmyadmin中尝试过查询,但查询结果为空 @DataM 我怀疑您使用的 MySQL 版本早于 8+。在这种情况下,我给出了一个仍然可以工作的替代查询。 @DataM 您已经有了最低价格和 id。所以你可以这样做:dbfiddle.uk/…【参考方案2】:

这对你来说应该没问题:

select * 
from (select Name, id, Price FROM A
      union
      select Name, id, Price FROM B) Tab
where (Tab.id, Tab.price) = (select Tab2.id, min(Tab2.price)
                             from (select Name, id, Price FROM A
                                   union
                                   select Name, id, Price FROM B) Tab2
                             where Tab2.id = '123')

您只有一个地方可以放置您要查找的 ID。

在这里你可以看到演示: DEMO

/*This returns everything from your two tables*/
select * 
from (SELECT Name, id, Price FROM A
      union
      select Name, id, Price FROM B) Tab

/*this returns the minimal price for your requested ID, here you requested id =123*/
select Tab2.id, min(Tab2.price)
from (SELECT Name, id, Price FROM A
      union
      select Name, id, Price FROM B) Tab2
where Tab2.id = '123'

--with this where clause: 
where (Tab.id, Tab.price)
/*you are telling the query :
  give me every row from all the data(first query)that has 
  this combination of ID + PRICE: 
  123 + 10 (you have found this with the second query)

  So, you do not care what name it is, it only has to have :
  ID = 123 and the lowest price which is 10.
  ID 123 was requested from you and lowest price for that ID is 10,  
  which you have founded with the second query.*/

【讨论】:

你好@DataM 这是我的例子。干杯! 你能否分解查询以便我更好地理解,也许下次我会尝试自己找到解决方案,因为我会有更多的知识?为什么在这个中不需要 group by 来为每个不同的 Name 返回一行? 您好@DataM 我已将您问题中的新数据添加到我的演示中... 是的,它很有帮助,感谢您的宝贵时间,我对 sql 和子查询的工作原理有了更好的理解。并希望我的帖子对其他人有用,而不仅仅是我。 你好@DataM,很高兴我能提供帮助。【参考方案3】:

我在 db-fiddle.com 上对此进行了测试,它返回所有价格最低的行:

SELECT Id, Name, Price
FROM (SELECT * FROM A UNION SELECT * FROM B) TMP
WHERE (Price, Id) = (
    SELECT MIN(Price), Id
    FROM (SELECT * FROM A UNION SELECT * FROM B) TMP2
    WHERE Id = "123"
);

这是我测试查询所针对的表的脚本:

create table A(
   _id INT NOT NULL AUTO_INCREMENT,
   Id  VARCHAR(100) NOT NULL,
   Name VARCHAR(100) NOT NULL,
   Price INT NOT NULL,
   PRIMARY KEY ( _id )
);

create table B(
   _id INT NOT NULL AUTO_INCREMENT,
   Id  VARCHAR(100) NOT NULL,
   Name VARCHAR(100) NOT NULL,
   Price INT NOT NULL,
   PRIMARY KEY ( _id )
);

INSERT INTO A(Id, Name, Price)
VALUES
('123', 'Name123a1', 21),
('123', 'Name123a2', 41),
('124', 'Name124a', 40);

INSERT INTO B(Id, Name, Price)
VALUES
('123', 'Name123b1', 22),
('123', 'Name123b2', 21),
('124', 'Name124b', 20);

解决方案需要一些时间才能弄清楚,因为我生疏了。感谢 VBoka 帮助我解决了错误。

【讨论】:

让我们continue this discussion in chat。 你说得对,我对 SQL 太生疏了。感谢你们对我的帮助。我已经调整了答案,并得出了您的解决方案-该死的。【参考方案4】:

我会在您最后一次查询后执行此操作,即输出 3 个价格值并且您需要最小值的那个。

创建一个 cte 使用ROW_NUMBER 创建排名 过滤具有该排名列的数据
with data as (
    
select 
    *, ROW_NUMBER () OVER(ORDER BY price ) as rank_ from table
    )

select * from data where rank_ = 1

【讨论】:

以上是关于如何在mysql中联合后获取最小值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 MySQL 中获取记录数以及最小值/最大值?

如何从 MySQL 列中缺失的有序值中获取最小值? [复制]

如何根据一行中的数字序列获取mysql-table列的最小值

如何在 MySQL 中选择字段具有最小值的数据?

Linux下mysql如何关联多张数据表

如何在sql中获取行的最小最小值