我有两张桌子:
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
)。
我想获得一个地址的人员名单。如果有人有主要地址,则应提供,如果没有,则无关紧要,检索哪个地址。
我有这个问题:
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
时检索第一行连接表,但事实并非如此。
类似的问题被问到here,但在我的情况下解决方案相当困难。
答案 0 :(得分:1)
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不符合ANSI标准时才能在MySQL中运行。
假设:
表p.ID
中有1 Address
行,其中有两行。没有GROUP
函数应用于Address.City
,数据库如何知道要显示哪个City
?它没有,所以你看到一个随机的。为了防止这种情况,将函数应用于不在组中的所有列(或将列放在组中)。
答案 1 :(得分:1)
在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;