Differs from one column only in Oracle

I would like to use individual rows in the following table, but only in the "PlayerID" column. This is what I have at the moment:

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 

And this is what I want, so that the highest "StartDate" for each "PlayerID" is displayed, and the next line is ignored:

  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 

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

This is an Oracle database.

Thanks in advance.

+4
source share
3 answers

A few points ...

  • Unless you use joins with Made_Up_Of and Season to filter rows, you do not need these tables. I left them here; you can add them back if you need them.

  • Mark Tickner is correct that you should use the ANSI JOIN syntax. The nice thing (besides the standard one) is that it correctly connects the join logic to the joined table. Once you get used to it, I think you will find it preferable.

  • What you really need is the maximum pf.StartDate for each PlayerID , which is suitable for the ROW_NUMBER() analytic function. PARTITION BY pi.PlayerID ORDER BY pf.StartDate DESC will basically assign a value to 1 row with each last sort date of each player. An external filter selects all rows except those that have a rating of 1 .

  • You can also assign ranking to the analytical functions RANK() and DENSE_RANK() , but if a player has a binding to the most recent date, then all the linked dates will be ranked # 1, and you will get several rows for this player. In such situations, when you want only one row per player, use ROW_NUMBER() instead.

Put it all together and you get the following:

 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 

One final point: based on WHERE pi.MatchID = '&match_id' it looks like you can use PHP as a front end and mysql function to execute a query. If so, check out mysqli or PDO instead, as they will protect you from SQL Injection. mysql functions (which are officially deprecated) will not.


Addendum : More information on ROW_NUMBER , thanks in large part to @AndriyM.

With ROW_NUMBER , if a player has more than one row with the most recent date, only one row will be assigned as ROW_NUMBER = 1 , and this row will be selected more or less randomly. Here is an example when the player’s last date is 5/1/2013, and the player has three lines with this date:

 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 

Please note that only one of the above lines will be assigned ROW_NUMBER = 1 , and this can be any of them. Oracle will decide, not you.

If this uncertainty is a problem, order additional columns to get a clear gain. For this example, the highest pi.MatchID will be used to determine the "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 

Now, if there is a relationship for the highest pf.StartDate , Oracle is looking for the highest pi.MatchID in a subset of the rows with the highest pf.StartDate . As it turned out, only one line satisfies this condition:

 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 
+8
source

You can use the rank () function.

 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
source

Perhaps using INTERSECT and then find MAX(StartDate) using GROUP BY ?

0
source

Source: https://habr.com/ru/post/1478899/


All Articles