【问题标题】:MySQL not sure about aggregation functionMySQL不确定聚合函数
【发布时间】:2016-04-17 05:43:33
【问题描述】:

我有这个功能多对多表。

table Actor
-----------
Actorid     Fullname

table entertainment
-------------------------
Entertainmentid   Name   Date


Actor_entertainment
-------------------------
Entertainmentid   Actorid

我需要选择所有演员的姓名,并为每个演员选择最早的娱乐日期和该演员的姓名。

我构建了这个查询:

SELECT 
a.fullname
, c.Name
, MIN(c.Date) 
FROM Actor a 
INNER JOIN Actor_entertainment b on b.Actorid = a.Actorid 
INNER JOIN entertainment c ON c.entertainmentID = b.entertainmentID 
GROUP BY 
a.Fullname

查询效果很好,但我不确定 MIN 函数选择了正确的日期。您能否阅读此查询并告诉我我在某处错了?准确的说有可能出错吗?

谢谢。

【问题讨论】:

  • 你会得到正确的日期。问题是你是否会得到正确的entertainment.name。如果 dbms 是 Oracle,如果任何组中有多个名称,则会出现错误。

标签: mysql group-by min


【解决方案1】:

是的,将返回c.Date 的最小值。只要Date 列的数据类型为DATEDATETIMETIMESTAMP,或者存储在该列中的值采用规范格式,那么这将是“最早日期”...较低的值对应更早的日期。

但是,c.Name 表达式的返回值是不确定的。也就是说,不能保证为该表达式返回的值将来自返回最小日期值的同一行。

(其他数据库将使用该 SQL 语句返回错误,阻止出现在 SELECT 列表中而不出现在 GROUP BY 子句中的“非聚合”表达式。MySQL 为 GROUP BY 提供了一个非标准扩展,它允许这样做要执行的查询。可以修改 MySQL 的行为,禁用此扩展,方法是将 SQL_MODE 设置为包含 ONLY_FULL_GROUP_BY。)

有几种方法可以让Name 与最早的日期相关联。

对于少量返回的行,并且有合适的索引可用,使用相关子查询是可行的:

  SELECT a.fullname
       , ( SELECT c.Name
             FROM entertainment c
             JOIN Actor_entertainment b
               ON b.entertainmentID = c.entertainmentID 
            WHERE b.Actorid = a.Actorid
            ORDER BY c.Date ASC, c.Name ASC
            LIMIT 1
          ) AS `Name`
       , ( SELECT c.Date
             FROM entertainment c
             JOIN Actor_entertainment b
               ON b.entertainmentID = c.entertainmentID 
            WHERE b.Actorid = a.Actorid
            ORDER BY c.Date ASC, c.Name ASC
            LIMIT 1
          ) AS `Date`
    FROM Actor a
   ORDER BY a.fullname

另一种方法是获取最早的日期,然后执行连接以查找与该最早日期对应的行。如果 Actor 中的给定行有多个具有相同“最小”日期的行,这将返回所有这些行:

  SELECT da.fullname
       , dc.Name
       , dc.Date
    FROM ( SELECT a.actorid
                , MIN(c.Date) AS min_date
             FROM Actor a
             JOIN Actor_entertainment b
               ON b.Actorid = a.Actorid
             JOIN entertainment c
               ON c.entertainmentID = b.entertainmentID
            GROUP BY a.actorid
         ) d
    JOIN Actor da
      ON da.actorid = d.actorid
    JOIN Actor_entertainment db
      ON db.Actorid = d.Actorid
    JOIN entertainment dc
      ON dc.entertainmentID = db.entertainmentID
     AND dc.Date = d.min_date

【讨论】:

    【解决方案2】:

    使用变量,您可以根据日期为每个艺术家创建一个排名,然后只需选择每个艺术家的第一个。

    另请参阅而不是使用别名 A, B, C 我使用 A, AE, E 来帮助理解查询。

    SELECT ArtistName,
           EntertainmentName,
           Date 
    FROM (
            SELECT 
                A.fullname ArtistName
              , E.Name     EntertainmentName
              , E.Date
              , (@rank := if(@prev_artist = A.fullname, 
                             @rank + 1,  -- increase rank
                             if(@prev_artist := A.fullname, --reset rank
                                0, 
                                0                       
                               )
                            )                  
               ) as ranking 
            FROM Actor A 
            INNER JOIN Actor_entertainment AE
                    on A.Actorid = AE.Actorid 
            INNER JOIN entertainment E 
                    ON AE.entertainmentID = E.entertainmentID 
            CROSS JOIN (select @rank := 0, @prev_artist := '') params
            ORDER BY A.Actorid, E.Date
         ) T
    WHERE ranking = 1
    

    【讨论】:

    • 感谢有关命名表的建议。
    猜你喜欢
    • 2018-12-21
    • 2013-02-13
    • 1970-01-01
    • 2015-10-22
    • 1970-01-01
    • 2014-08-12
    • 2010-12-27
    • 2020-06-07
    • 1970-01-01
    相关资源
    最近更新 更多