如何在 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.*/
假设我有 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.*/