Say I have a random zend_db_select object.
How can I perform a count on that object, so I know the amount of items that meet the query.
I tried the following:
$data->TotalRecords = $select->columns(new Zend_Db_Expr('COUNT(*)'))->query()->fetch();
But this gives me the following error:
Message: No table has been specifiedfor the FROM clause
The query by itself works fine and returns a resultset.
There's a couple of ways of specifying the columns to fetch in a Zend_Db_Select. The following two product the same SQL
$select = $db->select()
->from('myTable', array())
->columns(array('TotalRecords' => new Zend_Db_Expr('COUNT(*)')));
$select = $db->select()
->from('myTable', array('TotalRecords' => new Zend_Db_Expr('COUNT(*)')));
The from method takes a first argument, the table name, and a second argument, an array of columns to fetch. If you're using an expression, you can specify a 'key' => Expr.
It's really easy to convert a Zend_Db_Select into a SQL string for debugging or use with other functions.
echo $select; // prints SELECT COUNT(*) AS `TotalRecords` FROM `myTable`
This uses a toString method, which is called automatically by Zend_Db fetch methods:
$total = $db->fetchOne($select);
echo $total; //prints the number of rows matching the query
Where $db is an instance of Zend_Db.
Use $select->__toString() method to output your generated query and see what is wrong with it.
If u dont have a from clause in your query add From() method to your select object.
If you use Zend_Db_Select, you have to call the from method to set the table name. With a Zend_Db_Table_Select, the table is passed in the constructor, so you don't need to call from.
$select = $db->select();
$select->from(
'table_name',
array('cnt' => 'count(1)')
);
I just encountered the same issue and found out what is going wrong
the Zend_Db_Select::columns functions expects an Array instead of a Object or String (when the first parameter is an String or Object it'll probably use this as main table for the columns you give but Im not sure about that.).
Changing your code to
$data->TotalRecords = $select->columns(array(new Zend_Db_Expr('COUNT(*)')))->query()->fetch();
Will fix your issue
Related
In CakePHP3, there is a ORM that helps with building queries.
From the documentation, I can see that
$query = $articles->find(); // build a query that has not run yet
$query->where(['id' => 1]); // Return the same query object
So in this case, I want the string
WHERE `articles`.`id` = 1
After much googling, I found out that there is a way to return just the where clause of a query object.
$query->where(['id' => 1])->clause('where'); // Return the where clause in the form of a QueryExpression
More googling leads me to find out how to get the QueryExpression to spit out string representation
$query->where(['id' => 1])->clause('where')->sql($valueBinder); // Return the where clause in string format
Here is my problem. I don't know what the $valueBinder is supposed to look like. I don't know how to initialize it.
I am also happy not to use ValueBinder as long as I can get the where clause in string format using CakePHP 3 ORM and in the right SQL dialect. Please assume I am using MySQL.
Please advise.
EDIT
I tried to use $query->valueBinder() as the $valueBinder.
It is empty and does not contain the associated c:0 to the value 1.
To directly answer your question, you can get the SQL for any clause this way:
$binder = new \Cake\ORM\ValueBinder();
$query->clause('where')->sql($binder);
That will return the SQL with the correct placeholders, not with the values to be used. The values live in the $binder variable and are used for statement objects.
As I can see, you only wanted to preserve the internal structure of the where clause to pass it to another query in a different request. Your solution is fine, but I'd like to add that you can also encode a full conditions tree from an existing query:
$where = serialize($query->clause('where'));
$anotherQuery->where(unserialize($where)); // A query in another request
In any case, you need to be careful with what you are unserializing as taking it directly from user input will certainly lead to security problems.
You can choose to omit this param if you like. Please see http://api.cakephp.org/3.0/class-Cake.Database.Query.html#_sql
In addition, you can use the Query member function traverse($visitor, $parts) to isolate the where clause. $visitor is a function that takes a value and a clause. You define the behavior of $visitor. $parts is an array of clause names. I suggest passing array('where') into this param.
My workaround is that I store the conditions in json string format.
Using the same example, what I do is
$data['conditions'] = json_encode(['Articles.id' => 1]); // encode into JSON string
$this->DynamicRules->patchEntity($dynamicRule, $data); // use in edit action of DynamicRulesController
then when I need to reuse the conditions, I do:
$articlesTable = TableRegistry::get('Articles');
$query = $articlesTable->find(); // new query for Articles
$rule = json_decode($dynamicRule->conditions, true); // get back the conditions in associative array format
$query->where($rule); // re-assign the conditions back
This got me what I ultimately wanted.
I am trying to retrieve a specific field from the first row of my query results. The following works just fine...
$result = $db->query($query);
$firstrow = $result->fetch();
$desired_field = $firstrow["field"];
My question is, can i do this in one step without storing the first row of results in a variable? Thanks in advance!
You can use fetchColumn() to return a single column from the next row in the result set. If there is only one column in the result set, do:
$desired_field = $result->fetchColumn();
If there are multiple columns in the result set, specific the numeric index of the one you want:
$desired_field = $result->fetchColumn(1);
Take notice of the warning provided in the fetchColumn() documentation:
There is no way to return another column from the same row if you use PDOStatement::fetchColumn() to retrieve data.
You can use something like $firstrow = $db->query($query)->fetch() but this is not good practice to do with functions that aren't guaranteed to return an object.
The query() function can return FALSE on error, and the dynamic call to fetch() would be a fatal error. You can't call ->fetch() on a scalar FALSE value.
For example, try the following and your script will explode:
$firstrow = $db->query("SELECT * FROM table_that_does_not_exist")->fetch();
PDO does support a mode to throw exceptions instead of returning FALSE, so in that case you are guaranteed either query()->fetch() works, or else query() will throw an exception, so you will never reach the fatal error. But you might not be using PDO's exception mode.
The answer from #GeorgeCummins points out that there's a fetchColumn() method that you can use once you have a PDOStatement object, that's a good way to get a single column. If you only need one column, name that column as the only column in your select-list, and then always use fetchColumn(0):
$oneValue = $db->query("SELECT oneColumn FROM table")->fetchColumn(0);
Otherwise if you use fetch(), this returns an array. PHP 5.4 supports array dereferencing:
$oneValue = $db->query("SELECT oneColumn FROM table")->fetch()[0];
But if you're using PHP 5.3 or earlier this is not supported. And I've never tried it with the array returned by PDOStatement so if it's actually returning an ArrayObject or something this might not work anyway.
This is guarantee to work if and only if your query is correct and there is at least one row data.
$db->query($query)->fetch(PDO::FETCH_OBJ)->field;
In mu ZF2 project I have Model using TableGateway.
Inside function responsible for fetching objects based on search criteria (city, postal_code, range, type).
Generally I fetch rows of data by simple
$rowset = $this->tableGateway->select($where);
In my MySQL database I have procedure GetObjectsInCityRange(city, range) which returns all object_id in range of geocoded city coordinates
I intend to add in the where clause condition:
WHERE object_id IN (call GetObjectsInCityRange(city, range))
Is it possible with MySQL? How write correctly $where array element to make it work?
You can call a where() method in your select object and call in() on the return value or create a where statement.
E.g.
$select = new Select();
$select->from($this->tableName)->columns(array($expression));
$where = new Where();
$where->in($identifier, $valueSet);
// or
$where->addPredicate(
new Predicate\In($identifier, $valueSet)
);
// and then
$select->where($where);
and append it to the select object.
The link below is exactly what you need I believe :)
http://framework.zend.com/manual/2.1/en/modules/zend.db.sql.html#in-identifier-array-valueset-array
Hope this helps :)
For MySQL it is suggested that you should use function instead of procedure as function can be used in any sql query while procedure is itself a query like statement. Ex.
call MyPrcedure();
SET var = MyFunction();
So you can call function in your query most of the time. However as per my knowledge function will return valid mysql data-type like varchar, int float etc. So query rows may not be available in your In query. Then only way to execute your query to convert your procedure logic to a subquery and pass inside IN statement.
WHERE object_id IN (SUB_QUERY)
Say I have a PDOStatement object generated via PDO->query($query), is it possible to get the table it was executed on?
Something like this:
<?php
$statement = $pdo->query('SELECT * FROM `foo`;');
echo $statement->xyz;
// foo
I'm fully aware you can use $query->getColumnMeta(0)['table'] to do it, but as mentioned by the docs, it's not very safe. This needs to work across all PDO drivers.
You can retreive the name of the table using the PDOStatement that retrieves an associative array. The value ['name'] is the name of the table.
$select = $conn_pdo->query('SELECT * FROM foo');
$meta = $select->getColumnMeta(0);
echo "Name of table: ".$meta['table'];
You can check the query string:
$statement->queryString
Remember that a SQL query may reference multiple tables, or no tables, so there is no "table it was executed on". You could use regular expressions on the query string to find the first table referenced for standard queries (SELECT, INSERT, UPDATE, DELETE), but that may not be perfect.
Alternatively, use models to access your data, and you'll always know which tables are accessed by the model definition.
Can you not simply store the string used in the query and use simple string manipulations to access the table?
$this->facebook_applications = Doctrine::getTable('FacebookApplication')
->createQuery('a')
->execute();
I don't understand how this works at all. Why is the query just 'a' and why does that seem to get a list of the applications?
The static method Doctrine::getTable() gets an object that represents the FacebookApplication table.
That object has a method called createQuery(), which creates a Doctrine_Query object for querying that table. The argment ('a'), specifies an alias for the table in the query.
So essentially Doctrine::getTable('FacebookApplication')->createQuery('a') creates a query that translates to SQL like:
SELECT * FROM FacebookApplication as a
Which, naturally, returns all rows from that table.
You can see it by using :
$this->facebook_applications->getSqlQuery()