0

I have a mysql statement that queries a database for the latest track. However, since the database is partially normalized the ID's are in different tables. In the query's I get the artist ID'd from the artists table and put them into a variable. The variable in then parsed into a query that looks at the tracks to find the latest one, this is where the problem lies. Since the $artist variable can have tonnes of ID's in, all those ID's are parsed into the query and the outcome is several url's put together even though I have put a LIMIT on the query.

Bear in mind that I cannot LIMIT the artist query as I need to get all the artists from the table and find the latest track out of all the artists.

How would I get just the latest url from the query without limiting the artist query?

//Set up artist query so only NBS artists are chose

$findartist = mysql_query("SELECT * FROM artists") or die(mysql_error());
  while ($artist = mysql_fetch_array($findartist)){

     $artist =  $artist['ID']; 

        //get track url
        $fetchurl = mysql_query("SELECT * FROM tracks WHERE id = '$artist' ORDER BY timestamp DESC LIMIT 1");

             url = mysql_fetch_array($fetchurl);
             $track_ID = $url ['ID'];
             $trackname = $url ['name'];
             $trackurl = $url ['url'];
             $artist_ID =$url['ID'];

    }

ADDITION:

 $findartist = mysql_query("SELECT A.*, T.*
  FROM (
         SELECT T.ARTIST_ID, MIN(T.TRACK_ID) TRACK_ID
           FROM (
                    SELECT ARTIST_ID, MAX(`TIMESTAMP`) `TIMESTAMP`
                      FROM TRACKS
                  GROUP BY ARTIST_ID
                 ) L
           JOIN TRACKS T ON (    L.ARTIST_ID   = T.ARTIST_ID 
                             AND L.`TIMESTAMP` = T.`TIMESTAMP`)
       GROUP BY T.ARTIST_ID
       ) X
  JOIN ARTISTS A ON X.ARTIST_ID = A.ARTIST_ID
  JOIN TRACKS  T ON (X.TRACK_ID = T.TRACK_ID AND X.ARTIST_ID = T.ARTIST_ID)
  ORDER BY A.NAME");



             while ($artist = mysql_fetch_array($findartist)){

             $artist =  $artist['ID']; 
             $trackurl = $artist['url'];
Jacob Windsor
  • 6,750
  • 6
  • 33
  • 49
  • 1
    You should be able to replace all those queries with [this approach](http://stackoverflow.com/questions/1895110/row-number-in-mysql). However, please stop writing new code with the ancient mysql_* functions. They are no longer maintained and community has begun the [deprecation process](http://news.php.net/php.internals/53799). Instead you should learn about prepared statements and use either [PDO](http://php.net/pdo) or [MySQLi](http://php.net/mysqli). If you care to learn, [here is a quite good PDO-related tutorial](http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers). – DCoder Aug 18 '12 at 14:20

3 Answers3

1

If I understand you correctly, you want the latest (most recent timestamp) track from each artist in your artist table.

It would help if you had your table definitions displayed. I think you're confusing ARTIST_ID and TRACK_ID in your query from your tracks table. So I will use the column names ARTIST_ID and TRACK_ID throughout.

(TIMESTAMP is an unfortunate choice for a column name, because it's also a MySQL data type name, by the way. No matter.)

You can do this with one query. Let us construct that query. It's not super simple but it will work just fine.

First, let's get the timestamp of the latest track or tracks by each artist. This returns a virtual table with ARTISTS_ID and latest TIMESTAMP shown.

   SELECT ARTIST_ID, MAX(`TIMESTAMP`) `TIMESTAMP`
     FROM TRACKS
 GROUP BY ARTIST_ID

Now, let's nest that query into another query to come up with a particular track_id that is the latest track from each artist. It is necessary to disambiguate the situation where an artist has more than one track with precisely the same timestamp. In this case we'll grab the lowest numbered TRACK_ID.

I suppose that all the tracks on an album by an artist have the same timestamp, but they have ascending track IDs, so this picks the first track on the artist's latest album.

  SELECT T.ARTIST_ID, MIN(T.TRACK_ID) TRACK_ID
    FROM (
             SELECT ARTIST_ID, MAX(`TIMESTAMP`) `TIMESTAMP`
               FROM TRACKS
           GROUP BY ARTIST_ID
          ) L
    JOIN TRACKS T ON (    L.ARTIST_ID   = T.ARTIST_ID 
                      AND L.`TIMESTAMP` = T.`TIMESTAMP`)
GROUP BY T.ARTIST_ID

See how this goes? The inner subquery finds the latest timestamp for each artist, and the outer query uses the subquery to find the lowest-numbered track ID for that artist and timestamp. So, now we have a virtual table that shows the latest track_id for each artist.

Finally, we need to query the joined-together artist and track information to get your list of artists and their latest tracks. We'll join the two physical tables with the virtual table we just figured out.

    SELECT A.*, T.*
      FROM (
             SELECT T.ARTIST_ID, MIN(T.TRACK_ID) TRACK_ID
               FROM (
                        SELECT ARTIST_ID, MAX(`TIMESTAMP`) `TIMESTAMP`
                          FROM TRACKS
                      GROUP BY ARTIST_ID
                     ) L
               JOIN TRACKS T ON (    L.ARTIST_ID   = T.ARTIST_ID 
                                 AND L.`TIMESTAMP` = T.`TIMESTAMP`)
           GROUP BY T.ARTIST_ID
           ) X
      JOIN ARTISTS A ON X.ARTIST_ID = A.ARTIST_ID
      JOIN TRACKS  T ON (X.TRACK_ID = T.TRACK_ID AND X.ARTIST_ID = T.ARTIST_ID)
  ORDER BY A.NAME

Think of it this way: You have some physical tables with your data in them. You can also create virtual tables with subqueries and use them as if they were physical tables by including them, nested, in your queries. That nesting is one of the reasons it's called Structured Query Language.

You're going to need indexes on your TIMESTAMP, ARTIST_ID, and TRACK_ID columns for this to work efficiently.

Edit: There really isn't sufficient information about your schema in your question to figure out how unambiguously to get the most recently uploaded track.

If the TRACK_ID is the autoincrementing primary key for the TRACKS table, it's easy. Get the highest numbered track ID left joined to the artist (left joined in case there's no corresponding row in the artist table).

        SELECT T.*, A.*
          FROM TRACKS T
     LEFT JOIN ARTISTS A ON T.ARTIST_ID = A.ARTIST_ID
      ORDER BY T.TRACK_ID DESC
         LIMIT 1

If TRACK_ID isn't an autoincrementing primary key but you almost never have two timestamps the same, do this. If there happen to be two or more tracks with the same timestamp, it will arbitrarily select one of them.

        SELECT T.*, A.*
          FROM TRACKS T
     LEFT JOIN ARTISTS A ON T.ARTIST_ID = A.ARTIST_ID
      ORDER BY T.`TIMESTAMP` DESC
         LIMIT 1

The trick to this data stuff is to be very careful to specify exactly what you want. It's pretty clear from your question that you're trying, in a loop, to get the most recent track for each artist in turn. My query did that without a loop in your program. But, you know what, I don't know the names of all your columns so my SQL might not be perfect.

O. Jones
  • 103,626
  • 17
  • 118
  • 172
  • That sure is one long mysql query. I am trying it out now and am having some a few troubles. I have posted what I have in my answer. – Jacob Windsor Aug 18 '12 at 18:58
  • I forgot to say what's happening. It is just coming up with a Boolean error since the query is finding no data. I am not good enough with sql to fully understand you query but from what you have written I think you have misunderstood a few things. I only want the most recent track from ALL the artists so only one track url should come from the query. Also, an artist cannot upload a whole album at the same time so the second part of the query shouldn't be necessary. Thanks a lot for the detailed answer by the way – Jacob Windsor Aug 18 '12 at 19:01
  • Thanks for the edit and the effort in answering. Your edit is now helping me a long nicely actually and yes the track_ID is auto incremented. I am much more familiar with PHP than sql even though I am not that familiar with either. Your sql is helping me along nicely now though, i am just patching up a few bugs. – Jacob Windsor Aug 18 '12 at 22:44
  • Thanks very much and I have now sorted it and used much of your query. It all works well now and with only one query! – Jacob Windsor Aug 18 '12 at 23:24
  • When writing code that's made of SQL and PHP (or SQL and Java, or whatever) to develop the SQL code in an SQL client like PHPmyAdmin, then build it into the other code. That way you can look at the query results. Good luck. – O. Jones Aug 19 '12 at 12:10
  • yes that's what I ended up doing, never had done it before but now will. Starting to develop my sql skills now thanks – Jacob Windsor Aug 19 '12 at 13:02
1

The relation between artists table and tracks table is one-to-many. So your tracks table should have a column artist_id and foreign key constraint which cross-references this column with id column in artists table. When this is done, the query to get latest tracks would look like:

SELECT id, name, url, MAX(timestamp) timestamp
FROM tracks
GROUP BY artist_id
mintobit
  • 2,363
  • 2
  • 15
  • 15
  • A foreign Key constraint would be perfect if I didn't also have tracks from users that weren't artists in the table. I have used foreign keys in other tables though – Jacob Windsor Aug 18 '12 at 19:21
  • @nbs189 what is a relation between `tracks.id` and `artists.id`? – mintobit Aug 18 '12 at 20:56
  • There is no relation. Tracks ID is used in other tables such as the votes table to reference which track has been voted on. There is no artists.id just ID. The ID is in every table and is the user_ID. If a user's ID is in the artists table they are an artist if not then they're not. For this query I just want to get the track IF a user IS an artist. – Jacob Windsor Aug 18 '12 at 22:01
  • @nbs189 sounds like your database schema design is messy. [This](http://stackoverflow.com/a/621891/1017165) answer might be helpful. – mintobit Aug 18 '12 at 23:51
  • I had a read through and there are a few things i have changed but not much really. I had redesigned the database after this question so was pretty neat anyway. Will abide to those rules though thanks – Jacob Windsor Aug 19 '12 at 00:07
0

Big thanks to @OllieJones and @hookman for helping me out on this. I have found the query I need and I have done it all in one query without any PHP so big thanks to them both.

Anyway here it is;

SELECT T.url, A.ID, T.ID
FROM tracks T
LEFT JOIN ARTISTS A ON T.ID = A.ID
WHERE T.ID = A.ID
ORDER BY T.timestamp DESC                                      
LIMIT 1

I took much of @OllieJones query and edited it a bit. I added the WHERE clause so that only artists are chosen and took away the * so only the needed data is returned. I also took @hookman advice and used a load of foreign keys. Gonna help a lot in the future.

Jacob Windsor
  • 6,750
  • 6
  • 33
  • 49