tags:

views:

33

answers:

2

This is sort of what I want to do, but MySQL doesn't seem to accept it.

SELECT Name, Content, Lft, Rht FROM Articles WHERE
    (Lft > (SELECT Lft FROM Articles WHERE idArticle = 1))
    AND WHERE
    (Rht < (SELECT Rht FROM Articles WHERE idArticle = 1));

I'm implementing the modified preorder tree transversal algorithm, and I want to get all the children of an Article using a single database query.

The Articles table looks like this:

Articles
+=============+===========+
| Type        | Name      |
+=============+===========+
| VARCHAR(45) | Name      |
+-------------+-----------+
| LONGTEXT    | Content   |
+-------------+-----------+
| INT         | Lft       |
+-------------+-----------+
| INT         | Rht       |
+-------------+-----------+
| INT         | idArticle |
+-------------+-----------+

idArticle is a primary key, and there are UNIQUE indexes on the Lft and Rht columns.

How might something like this be accomplished?

*Note: I'm currently using MySQL but I'd like to avoid any MySQL extensions where possible, because there are possible plans to move to another DB like Postgres or Oracle sometime in the future...

+4  A: 

It's not being accepted because the WHERE keyword can only appear once in a query (outside of a subquery):

SELECT x.name,  
       x.content, 
       x.lft, 
       x.rht 
  FROM ARTICLES x
 WHERE EXISTS(SELECT NULL
                FROM ARTICLES y
               WHERE y.idarticle = 1
                 AND y.lft < x.lft)
  AND EXISTS(SELECT NULL
               FROM ARTICLES z
              WHERE z.idarticle = 1
                AND z.rht > x.rht)
OMG Ponies
+1 Oh, that was easy...
Thilo
@Thilo: Hey now! I re-wrote it :p
OMG Ponies
Oh god.. I'm such an idiot. I'll look in to getting the foot removed from my mouth next week :P
Billy ONeal
@Billy ONeal: Don't feel so bad -- [I didn't read the documentation](http://stackoverflow.com/questions/1189810/mysql-5-1-35-select-into-not-working)
OMG Ponies
+2  A: 

Apparently it was just a simple syntax error (too many WHERE), but if you want to avoid the scalar subselect, you could also do a join (with the potential advantage that the row with idArticle 1 only needs to be loaded once, an optimization that your query planner may or may not be able to figure out by itself):

SELECT a.Name, a.Content, a.Lft, a.Rht FROM Articles a
   join Articles b on (b.idArticle =1 )
   where a.lft > b.lft and a.rht < b.rht;

Alternate equivalent

SELECT a.Name, a.Content, a.Lft, a.Rht 
  FROM Articles a
  join Articles b on a.lft > b.lft 
                 and a.rht < b.rht 
                 AND b.idArticle = 1 ;
Thilo
You had some funky JOIN criteria there ;) But have to be careful, because the JOINs will duplicate records associated to the `a` alias if there's more than one that matches.
OMG Ponies
@OMG: I prefer to separate the JOIN condition from the WHERE condition in this case. Reverting your edit. (I am assuming that idArticle is unique, so there should be no duplicate records).
Thilo
@OMG: It should work that way, right (if idArticle returns exactly one row as it would have had to in the original query)?
Thilo
I have my suspicions about how MySQL will interpret the JOIN, not fond of mixing ANSI-89 and ANSI-92 syntax...
OMG Ponies
@OMG: What about the syntax is mixed? (Sorry, total newbie at this)
Billy ONeal
Cannot check with MySQL. Maybe someone can confirm. About mixing the syntax, I think the stuff that was originally in the WHERE clause should stay there, the stuff that was originally in the subselect should go to the JOIN clause. You are right that a JOIN clause that does not reference the other table at all is a bit weird (but so are > and <). I'd probably just keep the original query. Anyway, you earned me a Cleanup Badge, thanks ;-)
Thilo
@Billy ONeal: Look at the JOIN to ARTICLES -- after the ON, there should be something there to relate the `a` to `b` references. But there isn't -- the JOIN criteria is in the WHERE, which is how ANSI-89 syntax works. It might work on MySQL, I think it's less likely to work on Oracle/Postgresql
OMG Ponies
I think the proper way to write this query is with the scalar subselects (like in your question). As you can see, we cannot really agree what the JOIN condition is, so this is not your typical JOIN. The main advantage with writing it as a JOIN is that it is clearer how to execute it efficiently (but one could hope that the database figures that out by itself).
Thilo
@Thilo: `SELECT EXPLAIN` reports that your solution results in two `SIMPLE` queries. @OMG's solution results in a `PRIMARY` query, and two `DEPENDENT SUBQUERY` items from `EXPLAIN`. I believe the two `SIMPLE`s are better, but I'm not sure.
Billy ONeal
@Billy ONeal: two > three; I'd believe Thilo's to be more efficient. I'm just not certain about how transfer able the syntax is, as-is. Thilo, hope you don't mind that I left yours in place while adding the rewrite I provided earlier.
OMG Ponies