MySQL - JOIN, GROUP BY with ORDER 分组
MySQL - JOIN, GROUP BY with ORDER of grouping
我有两个 table:
CREATE TABLE Person {
ID INT PRIMARY KEY,
Name VARCHAR(50) NOT NULL,
Surname VARCHAR(50) NOT NULL
}
和
CREATE TABLE Address {
ID INT PRIMARY KEY,
ID_Person INT NOT NULL,
Street VARCHAR(50),
HouseNumber VARCHAR(15),
City VARCHAR(75),
Zipcode VARCHAR(10),
CountryCode CHAR(2),
IsPrimary TINYINT(1) DEFAULT 0
}
每个人可以有多个地址,但最多只能有一个地址(IsPrimary = 1
)。
我想获得一个地址的人员名单。如果person有主地址,应该提供,如果没有,没关系,找哪个地址。
我有这个查询:
SELECT
p.Name,
p.Surname,
a.Street,
a.Housenumber,
a.City,
a.Zipcode
FROM
Person AS p
LEFT JOIN (select * from Address ORDER BY IsPrimary DESC) AS a ON p.ID = a.ID_Person
GROUP BY p.ID
但这并没有提供我期望的结果。我希望在执行 GROUP BY
时检索第一行加入的 table,但事实并非如此。
有人问过类似的问题here,但在我的情况下解决起来相当困难。
ORDER BY
在子查询中通常只会减慢您的查询速度。
您将不得不订购结果:
SELECT
p.Name,
p.Surname,
a.Street,
a.Housenumber,
a.City,
a.Zipcode
FROM
Person AS p
LEFT JOIN Address AS a ON p.ID = a.ID_Person
GROUP BY p.ID ORDER BY a.isPrimary
此查询还有第二个问题:它不符合 ANSI,因此它仅在 MySQL 中有效,而 MySQL 不符合 运行 ANSI。
假设这样:
您有 1 p.ID
,在 table Address
中有两行。没有 GROUP
函数应用于 Address.City
,数据库如何知道要显示哪个 City
?它没有,所以你看到一个随机的。为防止这种情况,将函数应用于不在分组依据中的所有列(或将列置于分组依据中)。
在 MySQL 8.0 中,这最好作为排名查询来完成。
WITH PersonAddress AS (
SELECT
p.Name,
p.Surname,
a.Street,
a.Housenumber,
a.City,
a.Zipcode,
ROW_NUMBER() OVER (PARTITION BY p.ID ORDER BY a.IsPrimary DESC) AS rn
FROM Person AS p
LEFT OUTER JOIN Address AS a ON p.ID = a.ID_Person
)
SELECT * FROM PersonAddress WHERE rn = 1;
在 MySQL 8.0 之前,窗口功能不可用。解决方法是使用会话变量:
SELECT
t.Name,
t.Surname,
t.Street,
t.Housenumber,
t.City,
t.Zipcode
FROM (
SELECT
p.Name,
p.Surname,
a.Street,
a.Housenumber,
a.City,
a.Zipcode,
IF(p.ID = @pid, @rn:=@rn+1, @rn:=1) AS rn,
@pid := p.ID
FROM (SELECT @pid:=0, @rn:=1) AS _init
CROSS JOIN Person AS p
LEFT OUTER JOIN Address AS a ON p.ID = a.ID_Person
ORDER BY p.ID, a.IsPrimary DESC
) AS t
WHERE t.rn = 1;
我有两个 table:
CREATE TABLE Person {
ID INT PRIMARY KEY,
Name VARCHAR(50) NOT NULL,
Surname VARCHAR(50) NOT NULL
}
和
CREATE TABLE Address {
ID INT PRIMARY KEY,
ID_Person INT NOT NULL,
Street VARCHAR(50),
HouseNumber VARCHAR(15),
City VARCHAR(75),
Zipcode VARCHAR(10),
CountryCode CHAR(2),
IsPrimary TINYINT(1) DEFAULT 0
}
每个人可以有多个地址,但最多只能有一个地址(IsPrimary = 1
)。
我想获得一个地址的人员名单。如果person有主地址,应该提供,如果没有,没关系,找哪个地址。
我有这个查询:
SELECT
p.Name,
p.Surname,
a.Street,
a.Housenumber,
a.City,
a.Zipcode
FROM
Person AS p
LEFT JOIN (select * from Address ORDER BY IsPrimary DESC) AS a ON p.ID = a.ID_Person
GROUP BY p.ID
但这并没有提供我期望的结果。我希望在执行 GROUP BY
时检索第一行加入的 table,但事实并非如此。
有人问过类似的问题here,但在我的情况下解决起来相当困难。
ORDER BY
在子查询中通常只会减慢您的查询速度。
您将不得不订购结果:
SELECT
p.Name,
p.Surname,
a.Street,
a.Housenumber,
a.City,
a.Zipcode
FROM
Person AS p
LEFT JOIN Address AS a ON p.ID = a.ID_Person
GROUP BY p.ID ORDER BY a.isPrimary
此查询还有第二个问题:它不符合 ANSI,因此它仅在 MySQL 中有效,而 MySQL 不符合 运行 ANSI。
假设这样:
您有 1 p.ID
,在 table Address
中有两行。没有 GROUP
函数应用于 Address.City
,数据库如何知道要显示哪个 City
?它没有,所以你看到一个随机的。为防止这种情况,将函数应用于不在分组依据中的所有列(或将列置于分组依据中)。
在 MySQL 8.0 中,这最好作为排名查询来完成。
WITH PersonAddress AS (
SELECT
p.Name,
p.Surname,
a.Street,
a.Housenumber,
a.City,
a.Zipcode,
ROW_NUMBER() OVER (PARTITION BY p.ID ORDER BY a.IsPrimary DESC) AS rn
FROM Person AS p
LEFT OUTER JOIN Address AS a ON p.ID = a.ID_Person
)
SELECT * FROM PersonAddress WHERE rn = 1;
在 MySQL 8.0 之前,窗口功能不可用。解决方法是使用会话变量:
SELECT
t.Name,
t.Surname,
t.Street,
t.Housenumber,
t.City,
t.Zipcode
FROM (
SELECT
p.Name,
p.Surname,
a.Street,
a.Housenumber,
a.City,
a.Zipcode,
IF(p.ID = @pid, @rn:=@rn+1, @rn:=1) AS rn,
@pid := p.ID
FROM (SELECT @pid:=0, @rn:=1) AS _init
CROSS JOIN Person AS p
LEFT OUTER JOIN Address AS a ON p.ID = a.ID_Person
ORDER BY p.ID, a.IsPrimary DESC
) AS t
WHERE t.rn = 1;