仅在Oracle中的一列上有所区别

时间:2013-05-03 01:57:10

标签: sql database oracle

我想在下表中使用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数据库。

提前致谢。

3 个答案:

答案 0 :(得分:8)

几点......

  • 除非您使用Made_Up_OfSeason的联接来过滤掉行,否则您不需要这些表。我把它们留在了这里;如果需要,可以将它们添加回来。

  • 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函数则用于执行查询。如果是这样,请查看mysqliPDO,因为它们会保护您免受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