logo

SQL: Using UNION To Create An Inbox Of Messages and Replies

I'm working on a system that has an internal "messaging system". Users who are logged-in can contact the "admin team" with any queries about the system and the administrators can reply to these messages, as, in turn, can the original user.

There are two tables involved. The first is the main "Messages" table, which looks like this:

Msg_ID User_ID Sent Subject Body
1 1 2010-12-07 12:34:56 First test This is a test
2 1 2010-12-07 12:36:12 Second test This is another test

There's then a table called "Replies" which looks like this:

Reply_ID Msg_ID User_ID Sent Body
1 1 2 2010-12-07 12:35:12 This is a reply. Seems to work.
2 2 2 2010-12-07 12:38:45 This is another reply. Got your second message too
3 2 1 2010-12-07 12:40:54 Cool. Glad I could help.

As you can see. The Replies table is tied back to the Message table via the Msg_ID foreign key. All very straightforward. Notice, however, that the replies don't have their own subject, but the Inbox we're creating will need one.

The problem, for me anyway, was working out how to display an Inbox to the Administrators.

The Inbox for the normal users was easy. They only ever see replies (this isn't the "Sent" folder after all) and so a simple SQL SELECT on the Replies table covered that.

Administrators need to see both the main messages and the replies too. They should be displayed in a time-ordered way, like this:

Sent Subject From
2010-12-07 12:40:54 Re: Second test User One
2010-12-07 12:38:45 Re: Second test Admin Two
2010-12-07 12:36:12 Second test User One
2010-12-07 12:35:12 Re: First test Admin Two
2010-12-07 12:34:56 First test User One

Knowing no better I tackled this by trying to JOIN the two table together using the Msg_ID foreign key. The trouble with that (as will be very obvious to most of you) is that the matching rows get joined in to one row. So, any Message that has a Reply will only appear in the inbox once - as the Reply. The message itself won't appear - which isn't how you'd expect an inbox to behave.

After much head scratching and fruitless Googling I found the solution. It has to use a UNION of two SELECT statements. Seems so obvious now. It's just been so long since I covered the very basics of SQL that I'd forgotten there was anything other than JOINs.

So, here's what the SQL looks like:

SELECT Msg_ID, 
	NULL as Reply_ID, 
	Sent, 
	Subject,
	(SELECT Users.FullName as [SentBy] FROM Users WHERE Users.User_ID = Message.User_ID)
FROM Messages

UNION 

SELECT Msg_ID, 
	Reply_ID, 
	Sent, 
	'Re: '+(SELECT Subject FROM Messages WHERE Msg_ID = Replies.Msg_ID) as Subject, 
	(SELECT Users.FullName as [SentBy] FROM Users WHERE Users.User_ID = Replies.User_ID)
FROM Replies

ORDER BY Sent DESC;

My quest to master SQL continues. Perhaps I need to take a step back and master the very basics first...

Comments

    • avatar
    • Paul
    • Tue 7 Dec 2010 01:15 PM

    Just a though Jake, could this have been done with just the one table - aren't replies messages too? ( not spent a great deal of time thinking about this so there may be a good answer why not! )

      • avatar
      • Jake Howlett
      • Wed 8 Dec 2010 01:13 AM

      Undoubtedly, yes. In fact this was my fall back plan in case I didn't work out the above solution. I always knew 1 table would work but it would feel like I'd given up and taken the easy route, which is no way to learn something new.

      I guess the question is whether it *should* be one table. I'd hate to think I were using 2 for the sake of it or just plain doing it wrong.

      There are a couple of columns I left off the tables which apply to my system and specific to each table. It could be done with one table but would feel messy.

      Show the rest of this thread

    • avatar
    • Aaron Hardin
    • Tue 7 Dec 2010 02:45 PM

    I was kinda thinking the same thing. Just have a field called frm_type and you could populate based on a button pushed or by the form or whatever you want.

    • avatar
    • osmor
    • Wed 8 Dec 2010 06:48 AM

    If Sent for replies Reply_ID = 1 is equal 2010-12-07 12:41:12 (for example) i.e. later than it is sent message "second test"

    SELECT Msg_ID,

    NULL as Reply_ID,

    Sent,

    Subject,

    (SELECT Users.FullName as [SentBy] FROM Users WHERE Users.User_ID = Messages.User_ID)

    FROM Messages

    UNION

    SELECT Msg_ID,

    Reply_ID,

    Sent,

    'Re: '+(SELECT Subject FROM Messages WHERE Msg_ID = Replies.Msg_ID) as Subject,

    (SELECT Users.FullName as [SentBy] FROM Users WHERE Users.User_ID = Replies.User_ID)

    FROM Replies

    ORDER BY Msg_ID DESC, Sent DESC;

    Excuse my bad English!

Your Comments

Name:
E-mail:
(optional)
Website:
(optional)
Comment:


About This Page

Written by Jake Howlett on Tue 7 Dec 2010

Share This Page

# ( ) '

Comments

The most recent comments added:

Skip to the comments or add your own.

You can subscribe to an individual RSS feed of comments on this entry.

Let's Get Social


About This Website

CodeStore is all about web development. Concentrating on Lotus Domino, ASP.NET, Flex, SharePoint and all things internet.

Your host is Jake Howlett who runs his own web development company called Rockall Design and is always on the lookout for new and interesting work to do.

You can find me on Twitter and on Linked In.

Read more about this site »

More Content