Using RAND() in Zend Framework - zend-framework

My client website using Zendframework. Now they ask me to random the result. I using RAND() but not working. Anybody can help?
Here is the code (the RAND() at the bottom)
$oPlaceSelect = $oPlaces->select()->setIntegrityCheck(false)
->from(array('p' => 'places'),
array('id', 'place_title' => 'title', 'alias', 'categories_id',
=> '(SELECT path FROM places_images WHERE places_images.places_id = p.id group by places_id)'))
->order('RAND()');
Thanks,

To use mysql function in a Zend Select object, you need to use 'Zend_Db_Expr'
->order(new Zend_Db_Expr("RAND()"));

Related

TYPO3 DBAL Querybuilder: Nested SELECT statements?

Is it possible to build nested SELECT statements like the one below using the DBAL QueryBuilder?
SELECT i.id, i.stable_id, i.version, i.title
FROM initiatives AS i
INNER JOIN (
SELECT stable_id, MAX(version) AS max_version FROM initiatives GROUP BY stable_id
) AS tbl1
ON i.stable_id = tbl1.stable_id AND i.version = tbl1.max_version
ORDER BY i.stable_id ASC
The goal is to query an external non TYPO3 table which contains different versions of each data set. Only the data set with the highest version number should be rendered. The database looks like this:
id, stable_id, version, [rest of the data row]
stable_id is the external id of the data set. id is the internal autoincrement id. And version is also incremented automatically.
Code example:
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
$result = $queryBuilder
->select(...$this->select)
->from($this->table)
->join(
'initiatives',
$queryBuilder
->select('stable_id, MAX(version) AS max_version' )
->from('initiatives')
->groupBy('stable_id'),
'tbl1',
$queryBuilder->and(
$queryBuilder->expr()->eq(
'initiatives.stable_id',
$queryBuilder->quoteIdentifier('tbl1.stable_id')
),
$queryBuilder->expr()->eq(
'initiatives.version',
$queryBuilder->quoteIdentifier('tbl1.max_version')
)
)
)
->orderBy('stable_id', 'DESC')
I cannot figure out the correct syntax for the ON ... AND statement. Any idea?
Extbase queries have JOIN capabilities but are otherwise very limited. You could use custom SQL (see ->statement() here), though.
A better API to build complex queries is the (Doctrine DBAL) QueryBuilder, including support for JOINs, database functions like MAX() and raw expressions (->addSelectLiteral()). Make sure to read until the ExpressionBuilder where it gets interesting.
So Extbase queries are useful in order to retrieve Extbase (model) objects. It can make implicit use of its knowledge of your data structure in order to save you some code but only supports rather simple queries.
The (Doctrine DBAL) QueryBuilder fulfills all other needs. If needed, you can convert the raw data to Extbase models, too. (for example $propertyMapper->convert($data, Job::class)).
I realize that we lack clear distinguishing between the two because they were both known at some time as "QueryBuilder", but they are totally different. That's why I like to add "Doctrine" when referring to the non-Extbase one.
An example with a JOIN ON multiple criteria.
$q = TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(TYPO3\CMS\Core\Database\ConnectionPool::class)
->getQueryBuilderForTable('fe_users');
$res = $q->select('*')
->from('tt_content', 'c')
->join(
'c',
'be_users',
'bu',
$q->expr()->andX(
$q->expr()->eq(
'c.cruser_id', $q->quoteIdentifier('bu.uid')
),
$q->expr()->comparison(
'2', '=', '2'
)
)
)
->setMaxResults(5)
->execute()
->fetchAllAssociative();
Short answer: it is not possible because the table to be joined in is generated on the fly. The related expression is back-ticked and thus causes an SQL error.
But: The SQL query can be changed to the following SQL query which does basically the same:
SELECT i1.id,stable_id, version, title, p.name, l.name, s.name
FROM initiatives i1
WHERE version = (
SELECT MAX(i2.version)
FROM initiatives i2
WHERE i1.stable_id = i2.stable_id
)
ORDER BY stable_id ASC
And this can be rebuild with the DBAL queryBuilder:
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->table);
$result = $queryBuilder
->select(...$this->select)
->from($this->table)
->where(
$queryBuilder->expr()->eq(
'initiatives.version',
'(SELECT MAX(i2.version) FROM initiatives i2 WHERE initiatives.stable_id = i2.stable_id)'
),
->orderBy('stable_id', 'DESC')
->setMaxResults( 50 )
->execute();

Zend Db Select add subquery to FROM part

I try to build a query with subquery in FROM part using the Zend_Db_Select. Im looking for somthing like this:
SELECT COUNT(row_1) AS count_row FROM (SELECT row,row2,... FROM table WHERE row= ...) AS temp WHERE row = 0)
So I try to do it like this:
$oSubSelect =
$this->select()
->setIntegrityCheck(false)
->from('table',
array(
'row_id'
)
)
->where(PRFX.'table.id = '.PRFX.'table2.id')
->from(PRFX.'table2',array('row','row2'));
$this->select(false)
->setIntegrityCheck(false)
->from(new Zend_Db_Expr($oSubSelect).' AS temp',
array(
'COUNT(row_id) AS row_count',
)
);
But this gives me an error message.
Best regards.
Ok I fix this. The problem was in
->from(new Zend_Db_Expr($oSubSelect).' AS temp',
Should be:
->from(new Zend_Db_Expr('('.$oSubSelect.')'),

Zend_Db_Select: LEFT JOIN on a subselect

I have a query, that does a LEFT JOIN on a subselect. This query is run in a high load environment and performs within the set requirements. The query (highly simplified) looks like:
SELECT
table_A.pKey
, table_A.uKey
, table_A.aaa
, table_B.bbb
, alias_C.ccc
, alias_C.ddd
FROM table_A
INNER JOIN table_B ON table_A.pKey = table_B.pKey
LEFT JOIN (
SELECT
table_X.pKey
, table_X.ccc
, table_Y.ddd
FROM table_X
INNER JOIN table_Y ON table_X.pKey = table_Y.pKey
) AS alias_C ON table_A.uKey = alias_C.pKey;
(for various reasons, it is not possible to rewrite the subselect as a (direct) LEFT JOIN).
Now, I cannot get the LEFT JOIN on subselect to work with Zend_Db_Select. I've tried everything I could come up with, but it does not work.
So my question is:
Is it not possible to do a query as described above with Zend_Db_Select?
What syntax do I need to get it to work within Zend Framework?
I think that it should work like this:
$subselect = $db->select->from(array('x' => 'table_X'), array('x.pKey', 'x.ccc', 'y.ddd'), 'dbname')
->join(array('Y' => 'table_Y'), 'x.pkey = y.pkey', array(), 'dbname');
$select = $db->select->from(array('a' => 'table_A'), array(/*needed columns*/), 'dbname')
->join(array('b' => 'table_B'), 'a.pkey = b.pkey', array(), 'dbname')
->joinLeft(array('c' => new Zend_Db_Expr('('.$subselect.')'), 'c.pkey = a.ukey', array())
I haven't tried it but I believe it'll work.
...
->joinLeft(array('c' => new Zend_Db_Expr('(' . $subselect->assemble() . ')'), 'c.pkey = a.ukey', array())

Class::DBI, Class::DBI::AbstractSearch, and Oracle to_date

I'm trying to produce the following SQL using CDBI:
select * from mytable
where ref = "foo"
and to_date(received_date, 'DD-MM-YYYY') < to_date('01-04-2011', 'DD-MM-YYYY')
The closest syntax I can think of is:
mycdbi->search_where({
ref => 'foo',
received_date => { '<' => ["to_date(?, 'DD-MM-YYYY')", '01-04-2011'] }
});
However that's not converting the column mytable.received_date using Oracle function to_date.
So what's the correct way to do it?
Ps: please don't say use DBIC as the code needs to stay in CDBI.
thanks!
Might be easiest to just use retrieve_from_sql and do it by hand:
my #things = mycdbi->retrieve_from_sql(qq{
ref = 'foo'
AND to_date(received_date, 'DD-MM-YYYY') < to_date('01-04-2011', 'DD-MM-YYYY')
});
I usually end up pushing the ORMs out of the way for any instantiation more complicated than my $thing = Thing->retrieve(x), I already know SQL so I don't need to learn yet another API that tries to implement SQL and usually does so rather poorly.

Doctrine: How to do a UPDATE with a SELECT MIN subquery?

I have a photos table where users can have multiple photos.
I'm trying to create the following query:
$q = Doctrine_Query::create()
->update('Photo p')
->set('p.photo_type', 1)
->where('p.user_id = ?', $id)
->andWhere('p.date_added = (SELECT MIN(p.date_added) FROM Photo p WHERE p.user_id = ?)', $id)
The idea is to set the "photo_type" to 1 for that specific photo of this user that has the minimum date added (earliest photo).
I just can't seem to get the syntax right. Can someone point me in the right direction?
Thank you.
EDIT:
It seems I'm trying to do something that can't be done, as per the answer to this question (MySQL Error 1093 - Can't specify target table for update in FROM clause). This question can be closed.
Try this:
$q = Doctrine_Query::create()
->update('Photo p')
->set('p.photo_type', 1)
->where('p.user_id = ?', $id)
->orderBy('p.date_added', 'desc')
->limit(1);