tags:

views:

180

answers:

4

I want to list the recent activities of a user on my site without doing too many queries. I have a table where I list all the things the user did with the date.

page_id - reference_id - reference_table - created_at - updated_at

The reference_id is the ID I need to search for in the reference_table (example: comments). If I would do a SELECT on my activity table I would then have to query:

SELECT * FROM reference_table where id = reference_id LIMIT 1

An activity can be a comment, a page update or a subscription. Depending which one it is, I need to fetch different data from other tables in my database

For example if it is a comment, I need to fetch the author's name, the comment, if it is a reply I need to fetch the orignal comment username, etc.

I've looked into UNION keyword to union all my tables but I'm getting the error

1222 - The used SELECT statements have a different number of columns

and it seems rather complicated to make it work because the amount of columns has to match and none of my table has the same amount of tables and I'm not to fond of create column for the fun of it.

I've also looked into the CASE statement which also requires the amount of columns to match if I remember correctly (I could be wrong for this one though).

Does anyone has an idea of how I could list the recent activities of a user without doing too many queries?

I am using PHP and MySQL.

+1  A: 

The problem is that UNION should be used just to get similar recordsets together. If you try to unify two different queries (for example, with different columns being fetched) it's an error.

If the nature of the queries is different (having different column count, or data types) you'll need to make several different queries and treat them all separately.

Another approach (less elegant, I guess) would be LEFT JOINing your activities table with all the others, so you'll end up with a recordset with a lot of columns, and you'll need to check for each row which columns should be used depending on the activity nature.

Again, I'd rather stick with the first one, since the second procudes a rather sparse recorset.

Seb
+1  A: 

You probably want to split out the different activities into different tables. This will give you more flexiblity on how you query the data.

If you choose to use UNION, make sure that the you use the same number of columns in each select query that the UNION is comprised of.

EDIT: I was down-voted for my response, so perhaps I can give a better explanation.

Split Table into Separate Tables and UNION

I recommended this technique, because it will allow you to be more explicit about the resources for which you are querying. Having a single table for inserting is convenient, but you will always have to do separate queries to join with other tables to get meaningful information. Also, you database schema will be obfuscated by a single column being a foreign key for different tables depending on the data stored in that row.

You could have tables for comment, update and subscription. These would have their own data which could be queried on individually. If, say, you wanted to look at ALL user activity, you could somewhat easily use a UNION as follows:

(SELECT 'comment', title, comment_id AS id, created FROM comment)
UNION
(SELECT 'update', title, update_id as id, created FROM update)
UNION
(SELECT 'subscription', title, subscription_id as id, created 
  FROM subscription)
ORDER BY created desc

This will provide you with a listing view. You could then link to the details of each type or load it on an ajax call.

You could accomplish this with the method that you are currently using, but this will actually eliminate the need for the 'reference_table' and will accomplish the same thing in a cleaner way (IMO).

jonstjohn
+1  A: 

With UNION you don't have to get all of the columns from each table, just as long as all of the columns have the same datatypes.

So you could do something like this:

SELECT name, comment as description
FROM Comments

UNION

SELECT name, reply as description
FROM Replies

And it wouldn't matter if Comments and Replies have the same number of columns.

toast
A: 

This really depends on the amount of traffic on your site. The union approach is a straightforward and possibly the correct one, logically, but you'll suffer on the performance if your site is heavily loaded since the indexing of a UNIONed query is hard.

Joining might be good, but again, in terms of performance and code clarity, it's not the best of ways.

Another totally different approach is to create an 'activities' table, which will be updated with activity (in addition to the real activity, just for this purpose). In old terms of DB correctness, you should avoid this approach since it will create duplicate data on your system, I, however, found it very useful in terms of performance.

[Another side note about the UNION approach if you decide to take it: if you have difference in parameters length, you can SELECT bogus parameters on some of the unions, for example.. (SELECT UserId,UserName FROM users) UNION (SELECT 0,UserName from notes)

Liorsion