ORDER BY on MySQL stored function in CakePHP 3 - php

I'm working on cakePHP 3. I have a user defined function(UDF or Routine) in mysql database. That function takes a parameter and returns an integer value. I have to order that returned value in MySQL order clause.
I know mysql query to use that function. i.e,
SELECT customer_id FROM table_name ORDER BY routine_name(param1); //param1 is 'customer_id' which I have written after SELECT
But I don't know that how to build this query in cakePHP 3. If anyone knows the solution, answer will be appreciate.
Here is my cakePHP 3 code.
$purchasesTable = TableRegistry::get("Purchases");
$query = $purchasesTable->find();
$sf_val = $query->func()->routine_name(['Purchases.customer_id' => 'literal']);
$query->select();
$query->order(
// Routine/Function call should be here as per MySQL query.
// So, I think here I have to do something.
);

I'd suggest that you have a closer look at the (API) docs, it's all mentioned there. You can pass expression objects to Query::order(), and in case you need to specify the direction, there's also Query::orderAsc() and Query::orderDesc().
So
$expression = $query->func()->routine_name(['Purchases.customer_id' => 'literal']);
$query->order($expression);
or
$query->orderAsc($expression);
or
$query->orderDesc($expression);
See also
Cookbook > Database Access & ORM > Query Builder > Selecting Data
API > \Cake\Database\Query::order()
API > \Cake\Database\Query::orderAsc()
API > \Cake\Database\Query::orderDesc()

Related

PHP Yii2 MYSQL query giving wrong output

I'm executing a PHP yii2 query to get records from the database but it's not working. But I directly executed the query in the MYSQL console then it gives me the expected output.
Normal SQL
SELECT * FROM `tbl_inbox` WHERE sender_id=778 AND recipient_id=736 OR sender_id=778 AND recipient_id=736 ORDER BY timestamp DESC LIMIT 1
Above query giving me expected result but when i change to Yii2 like :
$message=Inbox::find()->select(['message'])->where(['sender_id'=>$sender_id,'recipient_id'=>$recipient_id])->orWhere(['sender_id'=>$recipient_id,'recipient_id'=>$sender_id])->andWhere(['ad_id'=>$ad_id,'category'=>$category])->orderBy(['timestamp'=>SORT_DESC])->one();
It gives me the wrong result.
What is my logic :
I have a chat on my website and I need to get the last message order by timestamp. But sender_id and the recipient will be visa versa.
How to fix ?
There are significant differences between the normal SQL version of the query and the Yii version.
1) Selected fields
The normal SQL query selects all fields, but Yii version selects only message field.
This is because the normal SQL has SELECT * FROM ... but in Yii query you are calling select(['message']). If you want to select all fields you can leave the select() method call out or you can use select('*').
2) Your conditions are different
Considering operator priority your conditions in normal SQL are:
(sender_id=778 AND recipient_id=736)
OR (sender_id=778 AND recipient_id=736)
But in your Yii version of query:
(
(sender_id=778 AND recipient_id=736)
OR (sender_id=736 AND recipient_id=778)
) AND (
ad_id=$ad_id AND category=$category
)
In your normal SQL the both sides of OR condition are same but in your Yii version the values for sender/recipient are swapped in the second argument of OR operator.
Also there is extra part added by this call andWhere(['ad_id'=>$ad_id,'category'=>$category])
$message=Inbox::find()->select(['message'])->where(['sender_id'=>$sender_id,'recipient_id'=>$recipient_id])->orWhere(['sender_id'=>$recipient_id])->orWhere(['recipient_id'=>$sender_id])->andWhere(['ad_id'=>$ad_id,'category'=>$category])->orderBy(['timestamp'=>SORT_DESC])->one();
try this
I'm getting following sql
SELECT `message` FROM `user` WHERE ((((`sender_id`='2333') AND (`recipient_id`='23222')) OR (`sender_id`='23222')) OR (`recipient_id`='23333')) AND ((`ad_id`='10') AND (`category`='1')) ORDER BY `timestamp` DESC
you can check raw sql easily, then it will be mostly straight forward how Yii Query builder works.
use following code:
$rawSql = Inbox::find()
...
->createCommand()->getRawSql();
I would change your code into this:
$message=Inbox::find()
->select(['message'])
->where([
'and',
[
'or',
['sender_id'=>$sender_id,'recipient_id'=>$recipient_id],
['sender_id'=>$recipient_id,'recipient_id'=>$sender_id]
],
['ad_id'=>$ad_id,'category'=>$category]
])
->orderBy(['timestamp'=>SORT_DESC])->one();

Get json store data from database in codeigniter

I was storing Json data in Php database like this ["4","2"]. Problem is that i want to get data from json id using where clause . Is there any solution for that?
I was also use this JSON_CONTAINS but not working.
SELECT membermaster.* FROM membermaster WHERE membermaster.status = 'Active' AND membermaster.role = 'client' AND JSON_CONTAINS(client_type,"2")
Sorry for my bad english. Thank You.
Can you tell which Mysql version you are using because JSON_CONTAINS only will work in greater than equal MySql 5.7 version? It also depends on the server setting and database open extension settings.
If you want to get data using the where clause you can write a query like below.
SELECT membermaster FROM membermaster WHERE membermaster.status = 'Active' AND membermaster.role = 'client' AND client_type like '%"2"%';
Or if you want you can use regex also.

Add One to Integer Column Using Query in CakePHP 3

I dont recall exactly how, but I do remember being able to add one to an integer column in mysql with a query without knowing the value of that integer column...
something like
update table set column=column+1 where id=1
I am wondering, how can I do the above query with CakePHP 3
You can use \Cake\ORM\Table::updateAll() (or the underlying \Cake\Database\Query::update()), and use an expression to generate the arithmetic operation part, something like:
$additionExpression = $Table->query()->newExpr('column + 1');
$affectedRows = $Table->updateAll(
['column' => $additionExpression],
['id' => 1]
);
Or a little more complex in case you want to make use of automatic identifier quoting:
$additionExpression = $Table
->query()
->newExpr()
->add([
new \Cake\Database\Expression\IdentifierExpression('column'),
'1'
])
->setConjunction('+'); // tieWith() before CakePHP 3.4
See also
Cookbook > Database Access & ORM > Saving Data > Bulk Updates
Cookbook > Database Access & ORM > Query Builder > Raw Expressions

Mysql rand() function in Zend framework 2

In zend framework 1, we had used mysql rand() function like below or using zend_db_expr(). I have tried this in ZF2, but this is not working. Somebody please help me to use this in Zend Framework 2
$select = $this->db()->select()
->from('TABLE')
->order('RAND()');
Thanks,
Looking at the API it appears that the order function accepts a string or array of parameters order(string | array $order). My first thought was to use a key/val array. However, as I look at the actual code of the Db\Sql\Select, the string or array that you are passing gets quoted (see here). Assuming that your Db platform is Mysql, this is the function that quotes the fields (see here). It appears to iterate through each of the fields, and add these quotes, rendering your rand() function a useless string.
Getting to the point, the solution is up to you, but it does not appear that you can do it the way you want with this current version of ZF2.
You will need to extend the Db\Sql\Select class, OR extend the Db\Adapter\Platform\Mysql class, OR change the code in these classes, OR execute your query as a full select statement, OR change up your logic.
By changing up your logic I mean, for example, if your table has an Integer primary key, then first select the MAX(id) from the table. Then, choose your random numbers in PHP prior to executing your query like $ids[] = rand(1, $max) for as many results as you need back. Then your sql logic would look like SELECT * FROM table WHERE id IN(453, 234, 987, 12, 999). Same result, just different logic. Mysql's rand() is very "expensive" anyways.
Hope this helps!
Here you can use \Zend\Db\Sql\Expression. Example function from ModelTable:
public function getRandUsers($limit = 1){
$limit = (int)$limit;
$resultSet = $this->tableGateway->select(function(Select $select) use ($limit){
$select->where(array('role' => array(6,7), 'status' => 1));
$rand = new \Zend\Db\Sql\Expression('RAND()');
$select->order($rand);
$select->limit($limit);
//echo $select->getSqlString();
});
return $resultSet;
}

Zend_Db_Adapter_Oracle: Select with ORDER BY, LIMIT & WHERE

I'm trying to select some records from an Oracle 11g Database. The Statement is used to implement some kind of "filter" function for an HTML Table.
Requirements: limit for paging and order the filtered results.
The query is created with Zend_Db_Select
*Works like a charm:*
$select->where('APPLICATIONS LIKE ?', '%MYAPP1%');
$select->where('APPLICATIONS NOT LIKE ?', '%GENESIS%');
$select->limit(20);
= 1 matching result (which is ok!)
The problem occurs when I try to order the filtered result:
$select->order('PATH ASC');
= 3 matching results ??
I think it has something to do with the query generated by Zend DB Select, it looks like this:
SELECT z2.*
FROM (
SELECT z1.*, ROWNUM AS "zend_db_rownum"
FROM (
SELECT "APPS".* FROM "APPS" WHERE (APPLICATIONS LIKE '%MYAPP1%') AND (APPLICATIONS NOT LIKE '%GENESIS%') ORDER BY "PATH" ASC
) z1
) z2
WHERE z2."zend_db_rownum" BETWEEN 1 AND 20
If I run the query without order everything is fine.
If I run the query without limit everything is fine.
If I run the query with order + limit -> wrong result.
If I take the statement and put the order after "BETWEEN 1 AND 20" it works like I want. But how to say Zend DB Select to change it?
Important: I'm doing the query against an Oracle VIEW, if I do it against a "table" it works too.
Obviously Oracle's interpretation of the query is not what the Zend framework intents:
Oracle seems to associate the ROWNUM with the row number count on the inner query before ordering, so the alias zend_db_rownum delivers wrong numbers in the where clause of the outer query.
Since we're not in control of the way the Zend framework generates the SQL in response to the limit() instruction, the only workaround I can think of is skipping the Zend limit() instruction, and instead doing something along the lines of:
$select->where('APPLICATIONS LIKE ?', '%MYAPP1%');
$select->where('APPLICATIONS NOT LIKE ?', '%GENESIS%');
$select->order('PATH ASC');
$sql = $select->__toString();
$sql = "select * from (" . $sql . ") where ROWNUM between 1 and 20";
$stmt = $db->query( $sql, array());
$result = $stmt->fetchAll();
Of course, this workaround is only legitimate in case you're not developing cross-DB, so your code doesn't have to be DB agnostic.
Meaning, you will restrict your solution to Oracle if you use my suggested workaround.
If you checked that there is no error in SQL generation, and really no different conditions in WHERE clause, it may be Oracle server bug. To check it, try it on different Oracle server version or different Oracle server patch level.

Categories