Search code examples
sqlsql-serverzend-frameworkzend-db

Zend_Db_Select with 'FOR XML PATH' for SQL Server


I'm trying to code the following query using Zend_Db_Select:

SELECT p1.SKU,
(
 SELECT ',' + Status
 FROM "Products" "p2"
 WHERE p2.SKU = p1.SKU
 ORDER BY "Status"
 FOR XML PATH ('')
)  AS "Statuses"
FROM "Products" p1
GROUP BY SKU

This is what I have so far:

$s1 = $products->select()
               ->setIntegrityCheck(false)
               ->from(array('p2' => 'Products'),
                      new Zend_Db_Expr("',' + Status")
                 )
               ->where('p2.SKU = p1.SKU')
               ->order('Status');

$s2 = $products->select()
               ->from(array('p1' => 'Products'),
                      array('p1.SKU',
                            'Statuses' => new Zend_Db_Expr('(' . $s1 . ')')
                      )
                 )
               ->group('SKU');

echo $s2;
$dbRowSet = $Products->fetchAll($s2);

That gives me this:

SELECT "p1"."SKU",
(
 SELECT ',' + Status
 FROM "Products" AS "p2"
 WHERE (p2.SKU = p1.SKU)
 ORDER BY "Status" ASC
) AS "Statuses"
FROM "Products" AS "p1"
GROUP BY "SKU"

I can't figure out how to get the required FOR XML PATH ('').

Also, isn't using the . operator with $s1 calling __toString(), instead of leaving it as a native Zend_Db_Select object. Is there any other way to get the parens around $s1?

Alternatively, is there another way to do this whole query? I want to return each SKU and a concatenated grouping of all Statuses (a la GROUP_CONCAT() in MySQL). The table is huge, so iterating over them in PHP takes an unacceptably long time.


Solution

  • Looks like I was close, and with KSiimson's comments about string conversion in mind, this works:

    $s1 = $products->select()
                   ->setIntegrityCheck(false)
                   ->from(array('p2' => 'Products'),
                          new Zend_Db_Expr("',' + Status")
                     )
                   ->where('p2.SKU = p1.SKU')
                   ->order('Status');
    
    $s2 = $products->select()
                   ->from(array('p1' => 'Products'),
                          array('p1.SKU',
                                'Statuses' => new Zend_Db_Expr('(' . $s1 .
                                    " FOR XML PATH(''))")
                          )
                     )
                   ->group('SKU');
    
    echo $s2;
    $dbRowSet = $Products->fetchAll($s2);
    

    This just concats the FOR XML PATH clause with the first query as a string. Not quite as elegant as I was hoping for, but "perfect is the enemy of good".