2

The MySQL website has an excellent tutorial on storing hierarchal data in a database. I'm trying to write the query that returns a node's immediate children. I don't want to just copy/paste the query from the MySQL website, because I'm trying to work on this in a database agnostic manner.

This is the query I'm trying to Zend_Db_Select-ify

SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
    nested_category AS parent,
    nested_category AS sub_parent,
    (
        SELECT node.name, (COUNT(parent.name) - 1) AS depth
        FROM nested_category AS node,
        nested_category AS parent
        WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.name = 'PORTABLE ELECTRONICS'
        GROUP BY node.name
        ORDER BY node.lft
    )AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
    AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
    AND sub_parent.name = sub_tree.name
GROUP BY node.name
ORDER BY node.lft;
1
  • +1 just for the link to the tutorial! Commented Feb 3, 2011 at 8:37

2 Answers 2

1

You can try spliting your query into two Zend_Db_Select statements - parent query and subquery. You can use Zend_Db_Select object as param of from() method, like below:

$mainQuery = $db->select();
$mainQuery->from('user');

$sub = $db->select();
$sub->from('company');

$mainQuery->from(array('subquery' => $sub));

And you will get that kind of query:

SELECT `user`.*, `sub`.* FROM `user` 
    INNER JOIN (
        SELECT `company`.* FROM `company`
    ) AS `sub`

As you see, it automatically adds INNER JOIN when you add second from() - but I think, it's possible to rewrote your query to user joins instead of multi from syntax. So you should use joinInner() method, because then you can specify join condition as second param of it.

Notice, that subquery is similar to main query, so you could build main query, clone it as subquery, and thanks to Zend_Db_Select possibilities remove unnecessary parts (reset() method) and replace them:

$mainQuery = $db->select(); //and rest
$subQuery = clone $mainQuery;
$subQuery->reset(Zend_Db_Select::WHERE);
$subQuery->where(); // and add valid conditions for subquery
Sign up to request clarification or add additional context in comments.

6 Comments

+1 -- Is there an easier way to specify the AS clause in the from besides calling Zend_Db_Adapter_Abstract::quoteIdentifier ?
Saying 'specify' you mean sanitizing? If yes, when you creating a subquery you can use named bind parameters, for e.g. :nodeName and bind it at the moment of query execution - as second parameter of fetchAll() method from db adapter.
@singles: No, I mean if one of the fields on my table is called "Left" (instead of "Lft"), which is a SQL reserved word. Therefore it must be escaped. For example, on MySQL it needs to turn into `Left`, while on MSSQL it needs to be [[Left]].
@Billy ONeal Ohhh, in this case I don't know another way than quoteIdentifier, sorry :/
@Singles: I mean just like Zend_Db_Select already does. I.e. if you do $x->select()->from('MyTable', array('Left')); it gets escaped.
|
1

You can just paste in the query directly like this

$result = $db->query("SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
        FROM nested_category AS node,
        nested_category AS parent,
        nested_category AS sub_parent,
        (
            SELECT node.name, (COUNT(parent.name) - 1) AS depth
            FROM nested_category AS node,
            nested_category AS parent
            WHERE node.lft BETWEEN parent.lft AND parent.rgt
            AND node.name = 'PORTABLE ELECTRONICS'
            GROUP BY node.name
            ORDER BY node.lft
        )AS sub_tree
    WHERE node.lft BETWEEN parent.lft AND parent.rgt
        AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
        AND sub_parent.name = sub_tree.name
    GROUP BY node.name
    ORDER BY node.lf");

Be sure to use quote() to input any of your own parameters into this query.

This is a bit of a cheat and isn't something I have resorted to yet.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.