如何在 mysql 中获得并集后的最小值?

How to get the min value after an union in mysql?

假设我有 2 tables A 和 B。

这 2 个 table 有 3 列共同的名称、ID 和价格。

这是我用于 1 table 的查询:

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;

但这不是预期的结果。

让我们说 table A 我有 :

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

对于 table 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;

那么我可以向我的查询添加什么以获得预期结果,然后如果我使用 union 获得超过 2 tables 的特定 Id 的最低价格,它将如何工作?

我会在你最后一次查询后执行此操作,该查询输出 3 个价格值,你需要最小值。

  • 创建一个 cte
  • 使用 ROW_NUMBER 基于 ASC 顺序中的价格创建排名,如果您想要最高的,这是默认顺序,然后在最后添加 DESC
  • 使用该排名列过滤数据
with data as (
    
select 
    *, ROW_NUMBER () OVER(ORDER BY price ) as rank_ from table
    )

select * from data where rank_ = 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
);

我在 db-fiddle.com 上测试了它 returns 所有价格最低的行:

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帮我整理bug

这对你来说应该没问题:

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.*/