MySQL - JOIN, GROUP BY с порядком группировки

0

У меня две таблицы:

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, но это не так.

Подобный вопрос был задан здесь, но решение довольно трудно в моей ситуации.

Теги:
join
group-by
sql-order-by
greatest-n-per-group

2 ответа

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, когда MySQL не поддерживает ANSI.

Предположим следующее: у вас есть 1 p.ID с двумя строками в таблице Address. Какие функции GROUP применяются к Address.City, как база данных знает, какой City отображать? Это не так, поэтому вы видите случайный. Чтобы предотвратить это, примените функции ко всем столбцам, которые не входят в группу, (или поместите столбцы в группу).

  • 0
    Это работает, спасибо!
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;

Ещё вопросы

Сообщество Overcoder
Наверх
Меню