У меня две таблицы:
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
, но это не так.
Подобный вопрос был задан здесь, но решение довольно трудно в моей ситуации.
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
с двумя строками в таблице 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;