【问题标题】:MySQL - JOIN, GROUP BY with ORDER of groupingMySQL - JOIN,GROUP BY 与分组的顺序
【发布时间】:2018-06-06 20:45:05
【问题描述】:

我有两张桌子:

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 提出了类似的问题,但在我的情况下解决方案相当困难。

【问题讨论】:

    标签: mysql join group-by sql-order-by greatest-n-per-group


    【解决方案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 中运行。

    假设: 您有 1 个p.ID,在表Address 中有两行。没有GROUP 函数应用于Address.City,数据库如何知道要显示哪个City?它没有,所以你看到一个随机的。为了防止这种情况,将函数应用于不在分组依据中的所有列(或将列放在分组依据中)。

    【讨论】:

      【解决方案2】:

      在 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;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-07
        相关资源
        最近更新 更多