我有这个功能多对多表。
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函数选择正确的日期。你可以阅读这个问题并告诉我,我错了吗?确切地说,错误是可能的吗?
感谢。
答案 0 :(得分:0)
是的,将返回c.Date
的最小值。只要Date
列的数据类型为DATE
,DATETIME
,TIMESTAMP
,或者该列中存储的值位于“{1}}列中,那么这将是”最早的日期“规范格式...较低的值对应于较早的日期。
但是,c.Name
表达式返回的值是 indeterminate 。也就是说,无法保证为该表达式返回的值将来自返回最小日期值的同一行。
(其他数据库会返回一条带有该SQL语句的错误,因为出现在SELECT列表中的“非聚合”表达式而没有出现在GROUP BY
子句中.MySQL为GROUP BY提供了非标准的扩展。允许此查询执行。通过将SQL_MODE
设置为包含ONLY_FULL_GROUP_BY
,可以修改MySQL的行为以禁用此扩展。)
有两种方法可以使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
答案 1 :(得分:0)
使用变量,您可以根据日期为每位艺术家创建排名,然后只选择每位艺术家的第一位。
另请参阅使用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