473,386 Members | 1,674 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,386 software developers and data experts.

Can SQL return a threaded-comment-view result set?

[I also posted this to comp.databases but since I'm actually using
PostgreSQL I would be content with a pgsql specific answer, so I
also posted this to comp.databases.postgresql.novice because I'm
a novice and it looked like a good group, but then I realized that
this might not be a novice question even though I'm a novice, so
I guess this group comp.databases.postgresql.general is the last
that I'll try.]

I think I already know that the answer is that this can't be done, but
I'll ask anyways.

Suppose you want to use an RDBMS to store messages for a threaded
message forum like usenet and then display the messages. A toy table
definition (that I've tried to make standards compliant) might look
like:

create table messages (
message_id integer,
in_reply_to integer,
created date,
author varchar(20),
title varchar(30),
message varchar(256),
primary key (message_id)
);
The in_reply_to field, if not null, means that the message is a reply
to the message with the message_id it has stored. Suppose now that we
populate the database with a 5 message discussion.
insert into messages values
(1, null, '2003-09-01', 'John', 'Favorite DB?',
'What is your favorite database?');
insert into messages values
(2, null, '2003-09-02', 'Mike', 'New DB2 benchmarks',
'I just posted some new DB2 benchmarks.');
insert into messages values
(3, 1, '2003-09-03', 'Mike', 'Re: Favorite DB?',
'I\'d say DB2.');
insert into messages values
(4, 1, '2003-09-05', 'Dave', 'Re: Favorite DB?',
'I\'m an Oracle man myself.');
insert into messages values
(5, 3, '2003-09-07', 'John', 'Re: Favorite DB?',
'DB2? I thought you liked free databases?');
If we rendered an oldest-first threaded view of the discussion it
would look like:

Author: John
Title: Favorite DB?
Date: 2003-09-01
What is your favorite database?

Author: Mike
Title: Re: Favorite DB?
Date: 2003-09-03
I'd say DB2.

Author: John
Title: Re: Favorite DB?
Date: 2003-09-07
DB2? I thought you liked free databases?.

Author: Dave
Title: Re: Favorite DB?
Date: 2003-09-05
I'm an Oracle man myself.

Author: Mike
Title: New DB2 benchmarks
Date: 2003-09-02
I just posted some new DB2 benchmarks.
My question is: is it possible to use pure SQL to return a result set
that would make rendering a threaded view like the above really easy?
That is, is there an SQL query that would return something like:
i | r | created | auth | title | message | nesting
---+---+------------+------+--------------------+-----------+---------
1 | | 2003-09-01 | John | Favorite DB? | What is y | 0
3 | 1 | 2003-09-03 | Mike | Re: Favorite DB? | I'd say D | 1
5 | 3 | 2003-09-07 | John | Re: Favorite DB? | DB2? I th | 2
4 | 1 | 2003-09-05 | Dave | Re: Favorite DB? | I'm an Or | 1
2 | | 2003-09-02 | Mike | New DB2 benchmarks | I just po | 0
If I had an SQL query that could return that then it would be very
easy to have a computer program print threaded views like the one
above.

If this can't be done, then do any of you have recommendations about
the best way to accomplish this with the least amount of inefficient
back-and-forth between the database and, say, Java or some other
language?

Thank you very much in advance for any answers! This has been a
frustrating matter for me.

Chris
Nov 12 '05 #1
2 2225
On Thursday 02 Oct 2003 09:13 in
<40*************************@posting.google.com> , Chris
(mv*******@yahoo.com) wrote:
Suppose you want to use an RDBMS to store messages for a threaded
message forum like usenet and then display the messages. A toy table
definition (that I've tried to make standards compliant) might look
like:

create table messages (
message_id integer,
in_reply_to integer,
created date,
author varchar(20),
title varchar(30),
message varchar(256),
primary key (message_id)
);
The in_reply_to field, if not null, means that the message is a reply
to the message with the message_id it has stored. Suppose now that we
populate the database with a 5 message discussion.


You will need a second table, called a path enumeration table. Joe Celko
wrote up this technique in his book "SQL For Smarties".

I think I can dig up some sample SQL for you, as I used this technique
several times a few years ago -- although on DB2 rather than PostrgeSQL.
Since the SQL can be a bit intricate, I have set follow-ups to
comp.databases.postgresql.sql, as it would be more on-topic there.

However, I recommend Joe Celko's book, as it explains the technique as well
as demonstrates it.
--
Regards,

Dave [RLU#314465]
================================================== ====
dw****@spamtrap.ntlworld.com (David W Noon)
Remove spam trap to reply via e-mail.
================================================== ====
Nov 12 '05 #2
Hello all,

I've been meaning to get back to you all but I just haven't had time.
Okay, I've got a little bit of time now so here goes....

I received many useful answers from many of you including Tom Lane,
Joe Conway, and Josh Berkus. Max Nachlinger in particular on October
5th (which was my birthday) sent me a large amount of threaded
discussion forum code of his own. (Nice birthday present, Max. Thank
you.) I will be investigating his solution when I have more time since
his is almost certainly more efficient than my own.

My own solution is a 20-line PL/pgSQL function I put together after
reading the 7.3 docs at postgresql.org. It requires no modifications
to my original example table definitions other than that I decided to
use a 0 value instead of a NULL value for the in_reply_to column when
a message isn't a reply, because that way my plpgsql function doesn't
have to treat NULL as a special case.

In particular, my solution doesn't require a message to keep pointers
to its children. If a message is a reply, it simply points to its
parent's id via in_reply_to. You can add as many messages as you want
with just single simple INSERT statements; you don't have to do any
tree-refactoring or updating to the parent. The downside is that while
insert speed couldn't be any better and inserting couldn't be any
easier, building the threaded view seems rather algorithmically
inefficient, and in almost all applications optimising for obtaining
the threaded view rather than insert speed is more important. One
probably couldn't base even a moderate-load application on this
solution, but if one wanted to anyways I suppose an in-memory tree
representation could be maintained which allows new messages to be
linked into the in-memory tree efficiently as they're inserted into
the database, and then whenever the application is shutdown and
reloaded it could rebuild that in-memory representation on startup. Or
something. And until you run out of memory.... (Also, simply caching
the results of queries could be effective if you have many identical
queries producing identical results [which my application does] so
this solution might not work too bad for me.)

For the sake of googlers and like novices reading this, I've adapted
my PL/pgSQL function so that it works with the original example I
posted. (My real code uses more fields, different types, and has some
other subtle differences because there's more than one type of table
to consider and there are foreign key constraints.) After loading the
below code, evaluating

select * from threadview(0, 0);

builds a table like the one I wanted in my original posting.

---Chris

=================================================
-- This code originally due to Chris Barry,
http://www.goodfig.org/feedback
-- It's hereby placed in the public domain. These public domain
licenses
-- usually have some sort of warning about no guarantee of fitness for
a particular
-- purpose, etc. Well, the below code is DEFINITELY not fit for any
purpose! So,
-- use it at your own peril. Caveat emptor.

-- drop database discussion;
create database discussion;
\c discussion

-- The path to plpgsql.so may need to be edited for your system.
create function plpgsql_call_handler()
returns opaque as '/usr/local/pgsql/lib/plpgsql.so' language
'c';

create language 'plpgsql' handler plpgsql_call_handler
lancompiler 'PL/pgSQL';

create table messages (
message_id integer,
in_reply_to integer,
created date,
author varchar(20),
title varchar(30),
message varchar(256),
primary key (message_id)
);

-- A threadrow is the same thing as a row from the messages table
-- except a nesting integer has been added so the client knows how
-- much to indent the thread message. I'm not sure if there's a
-- syntax that makes it unnecessary to duplicate the redundant
-- information from the messages table (e.g inheritance).
create type threadrow as (
message_id integer,
in_reply_to integer,
created date,
author varchar(20),
title varchar(30),
message varchar(256),
nesting integer
);
create or replace function threadview(int, int) returns setof
threadrow as '
declare
p alias for $1; -- p is the parent
i alias for $2; -- i is the indentation (nesting)
c threadrow%rowtype;
c2 threadrow%rowtype;
begin
for c in select *, 0 as nesting from messages
where in_reply_to = p
order by created asc
loop
c.nesting = i;
return next c;
for c2 in select * from threadview(c.message_id, i+1) loop
return next c2;
end loop;
end loop;
return;
end;
' language 'plpgsql';
-- Load the table with some example data:

insert into messages values
(1, 0, '2003-09-01', 'John', 'Favorite DB?',
'What is your favorite database?');
insert into messages values
(2, 0, '2003-09-02', 'Mike', 'New DB2 benchmarks',
'I just posted some new DB2 benchmarks.');
insert into messages values
(3, 1, '2003-09-03', 'Mike', 'Re: Favorite DB?',
'I\'d say DB2.');
insert into messages values
(4, 1, '2003-09-05', 'Dave', 'Re: Favorite DB?',
'I\'m an Oracle man myself.');
insert into messages values
(5, 3, '2003-09-07', 'John', 'Re: Favorite DB?',
'DB2? I thought you liked free databases?');
Nov 12 '05 #3

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
by: Bart Nessux | last post by:
When testing scripts, I normally type 'ctrl' combined with the key 'c' to abort the script, however, this doesn't work with scripts that are threaded. What's the proper way to abort a threaded...
8
by: Matthew Bell | last post by:
Hi, I've got a question about whether there are any issues with directly calling attributes and/or methods of a threaded class instance. I wonder if someone could give me some advice on this. ...
0
by: Ganbold | last post by:
Hi, I'm new to multi-threaded programming and reading the book "Programming with POSIX Threads" and trying to understand concepts and coding. What I'm trying to do is to rewrite mysql client...
8
by: Sreenivas | last post by:
Hi, We cannot return a reference to an automatic variable from a function, as per the ANSI C++ standard the behaviour is undefined. Does this hold for inline functions too? or can I return a...
2
by: Michal Przytulski | last post by:
Hi, I'm looking information abouth runing PHP CLI application in multi-threaded - on http://www.php.net/manual/pl/ref.pcntl.php i found information abouth process control - but is PHP support...
1
by: Mullin Yu | last post by:
As subject. I have lots of VB6 dll and ocx files and they are single-threaded as VB6 doesn't support multi-threaded indeed. Can I wrap anything at .NET so that multi-threaded feature can be...
6
by: ben | last post by:
I am needing a web service to be single threaded. Is this possible? Any ideas would be helpful
3
by: Rsrany | last post by:
I've been working on a few gtk applications and need to tie a hot key catcher into a thread. I am currently finding threaded user32.GetMessageA do not work. I have included two programs: 1) a...
14
by: Snor | last post by:
I'm attempting to create a lobby & game server for a multiplayer game, and have hit a problem early on with the server design. I am stuck between using a threaded server, and using an event driven...
14
by: =?ISO-8859-1?Q?Tom=E1s_=D3_h=C9ilidhe?= | last post by:
As far as I know, the C Standard has no mention of multi-threaded programming; it has no mention of how to achieve multi-threaded programming, nor does it mention whether the language or its...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.