我想在下表中使用distinct,但仅限于'PlayerID'列。这就是我现在所拥有的:
MATCHID PLAYERID TEAMID MATCHDATE STARTDATE
---------- ---------- ---------- --------- ---------
20 5 2 14-JAN-12 01-JUN-11
20 5 4 14-JAN-12 01-JUN-10
20 7 4 14-JAN-12 01-JUN-11
20 7 2 14-JAN-12 01-JUN-10
20 10 4 14-JAN-12 01-JUN-11
20 11 2 14-JAN-12 01-JUN-10
20 13 2 14-JAN-12 01-JUN-11
20 16 4 14-JAN-12 01-JUN-10
20 17 4 14-JAN-12 01-JUN-10
20 18 4 14-JAN-12 01-JUN-10
20 19 2 14-JAN-12 01-JUN-11
这就是我想要的,所以显示每个'PlayerID'的最高'StartDate'并忽略下一行:
MATCHID PLAYERID TEAMID MATCHDATE STARTDATE
---------- ---------- ---------- --------- ---------
20 5 2 14-JAN-12 01-JUN-11
20 7 4 14-JAN-12 01-JUN-11
20 10 4 14-JAN-12 01-JUN-11
20 11 2 14-JAN-12 01-JUN-10
20 13 2 14-JAN-12 01-JUN-11
20 16 4 14-JAN-12 01-JUN-10
20 17 4 14-JAN-12 01-JUN-10
20 18 4 14-JAN-12 01-JUN-10
20 19 2 14-JAN-12 01-JUN-11
当前SQL:
SELECT pi.MatchID, pi.PlayerID, t.TeamID, m.MatchDate, pf.StartDate
FROM Plays_In pi, Match m, Plays_A pa, Team t, Plays_For pf, Made_Up_Of muo, Season s
WHERE pi.MatchID = m.MatchID
AND m.MatchID = pa.MatchID
AND pa.TeamID = t.TeamID
AND pf.PlayerID = pi.PlayerID
AND pf.TeamID = t.TeamID
AND muo.MatchID = pi.MatchID
AND muo.SeasonID = s.SeasonID
AND pi.MatchID = '&match_id'
AND m.MatchDate >= pf.StartDate
ORDER BY pi.MatchID ASC, pi.PlayerID ASC, pf.StartDate DESC;
这是一个Oracle数据库。
提前致谢。
答案 0 :(得分:8)
几点......
除非您使用Made_Up_Of
和Season
的联接来过滤掉行,否则您不需要这些表。我把它们留在了这里;如果需要,可以将它们添加回来。
Mark Tickner是正确的,您应该使用ANSI JOIN语法。关于它的好处(除了标准之外)是它将连接逻辑与正在连接的表放在一起。一旦你习惯了它,我认为你会发现它更适合。
您真正追求的是每个pf.StartDate
的最大PlayerID
,这非常适合分析ROW_NUMBER()
函数。 PARTITION BY pi.PlayerID ORDER BY pf.StartDate DESC
基本上会将值1
分配给每个玩家最近排序日期的行。除了1
排名的行外,外部会过滤掉所有行。
您还可以使用RANK()
和DENSE_RANK()
分析功能分配排名,但如果玩家在最近的某个日期有一个平局,那么所有相关日期将排名第一,你将获得该玩家的多行。在这种情况下,您只需要每个玩家一行,请改用ROW_NUMBER()
。
把它们放在一起然后你就明白了:
SELECT MatchID, PlayerID, TeamID, MatchDte, StartDate FROM (
SELECT
pi.MatchID,
pi.PlayerID,
t.TeamID,
m.MatchDate,
pf.StartDate,
ROW_NUMBER() OVER (PARTITION BY pi.PlayerID ORDER BY pf.StartDate DESC) AS StartDateRank
FROM Plays_In pi
INNER JOIN Match m ON pi.MatchID = m.MatchID
INNER JOIN Plays_A pa ON m.MatchID = pa.MatchID
INNER JOIN Team t ON pa.TeamID = t.TeamID
INNER JOIN Plays_For pf ON pf.PlayerID = pi.PlayerID AND pf.TeamID = t.TeamID
WHERE pi.MatchID = '&match_id'
AND m.MatchDate >= pf.StartDate
)
WHERE StartDateRank = 1
ORDER BY MatchID, PlayerID
最后一点:基于WHERE pi.MatchID = '&match_id'
看起来您可能正在使用PHP作为前端,而mysql
函数则用于执行查询。如果是这样,请查看mysqli
或PDO
,因为它们会保护您免受SQL注入。 mysql
函数(正式弃用)不会。
附录:有关ROW_NUMBER
的更多信息,非常感谢@AndriyM。
使用ROW_NUMBER
,如果玩家的多行具有最新日期,则只会将其中一行指定为ROW_NUMBER = 1
,并且该行或多或少会随机选取。这是一个示例,其中玩家的最近日期是2013年5月1日,并且玩家有三行具有此日期:
pi.MatchID pi.PlayerID pf.StartDate
---------- ----------- ------------
100 1000 05/01/2013 <-- could be ROW_NUMBER = 1
101 1000 04/29/2013
105 1000 05/01/2013 <-- could be ROW_NUMBER = 1
102 1000 05/01/2013 <-- could be ROW_NUMBER = 1
107 1000 04/18/2013
请注意,上面只有一个行将被分配ROW_NUMBER = 1
,而可以是其中任何一行。 Oracle将决定,而不是你。
如果这种不确定性存在问题,请通过其他栏目订购以获得明确的赢家。对于此示例,最高pi.MatchID
将用于确定“true”ROW_NUMBER = 1
:
-- replace `ROW_NUMBER...` in the query above with this:
ROW_NUMBER() OVER (
PARTITION BY pi.PlayerID
ORDER BY pf.StartDate DESC, pi.MatchID DESC) AS StartDateRank
现在,如果最高pf.StartDate
达到平局,则Oracle会在pi.MatchID
最高的行子集中查找最高pf.StartDate
。事实证明,只有一行满足这个条件:
pi.MatchID pi.PlayerID pf.StartDate
---------- ----------- ------------
100 1000 05/01/2013
101 1000 04/29/2013
105 1000 05/01/2013 <-- is ROW_NUMBER = 1: highest MatchID for
-- most recent StartDate (5/1/2013)
102 1000 05/01/2013
107 1000 04/18/2013 <-- not considered: has the highest MatchID but isn't
-- in the subset with the most recent StartDate
答案 1 :(得分:2)
您可以使用rank()函数。
SELECT * FROM (
SELECT pi.MatchID, pi.PlayerID, t.TeamID, m.MatchDate, pf.StartDate,
rank() over (partition by pi.PlayerID order by m.MatchDate desc, rowid) as RNK
FROM Plays_In pi, Match m, Plays_A pa, Team t, Plays_For pf, Made_Up_Of muo, Season s
WHERE pi.MatchID = m.MatchID
AND m.MatchID = pa.MatchID
AND pa.TeamID = t.TeamID
AND pf.PlayerID = pi.PlayerID
AND pf.TeamID = t.TeamID
AND muo.MatchID = pi.MatchID
AND muo.SeasonID = s.SeasonID
AND pi.MatchID = '&match_id'
AND m.MatchDate >= pf.StartDate
) WHERE RNK = 1
ORDER BY MatchID ASC, PlayerID ASC, StartDate DESC;
答案 2 :(得分:0)
也许使用INTERSECT
,然后使用MAX(StartDate)
查找GROUP BY
?