How to Convert SQL Query to Eloquent Laravel - mysql

MY SQL Query:
SELECT * ,COUNT(posts_id) as `view `FROM `post_view` JOIN posts ON post_view.posts_id = posts.id
GROUP BY
posts_id
My Eloquent Laravel:
$this->model->Join('posts','post_view.posts_id', '=', 'posts.id')
->selectRaw('*, count(posts_id) as view')
->groupBy('posts_id')
->get();
Error when Get by Postman:
SQLSTATE[42000]: Syntax error or access violation: 1055 'databasePost.post_view.id' isn't in GROUP BY
(SQL: select *, count(posts_id) as view from `post_view` inner join `posts` on `post_view`.`posts_id` =
`posts`.`id` group by `posts_id`)

This error occurs because group by requiring all columns, is the speed of the query.
I think you are using a MySQL server with version 5.7.5+.
Check this out: MySQL Handling of GROUP BY
Method 1:
Use ANY_VALUE to the column or disable mysql mode:ONLY_FULL_GROUP_BY.
->selectRaw('ANY_VALUE(post_view.id), ... count(posts_id) as view')
->groupBy('posts_id')
PS: post_view.id and posts.id both named id, select them all out that one of them will be covered.
Method 2:
edit your applications's database config file config/database.php
In mysql array, set strict => false to disable MySQL's strict mode:
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', '127.0.0.1'),
...
'strict' => false,
],

Related

Query builder GROUP BY, HAVING, COUNT in Laravel

how to express this code in query builder. I'm using Laravel 6.
SELECT * FROM feedback GROUP BY noTicket having count(`status`) < 2
My Code:
$feedback = DB::table('feedback')
->groupBy('noTicket')
->having('count(status)', '<', 2)
->get();
Error Code:
SQLSTATE[42000]: Syntax error or access violation: 1055 'sifora.feedback.idFeedback' isn't in GROUP BY
(SQL: select * from `feedback` group by `noTicket` having `count(status)` < 2)
What is wrong with my code? It seems match between sql code vs query builder.
Thank you
This is the working version of query
select noTicket
from feedback
group by noTicket
having count(status) < 2;
This is the query builder;
return DB::table('feedback')
->groupBy('noTicket')
->having(DB::raw('count(status)'), '<', 2)
->pluck('noTicket'); // you may replace this with get()/select()
$feedback = DB::table('feedback')
->selectRaw('feedback.*, count(status) as count_status')
->groupBy('noTicket')
->havingRaw('count(status) > ?', [2])
->get();
Also there exists strict mode, you can disable it in config/database.php
'connections' => [
'mysql' => [
'strict' => false
]
]
But I don't recommend to you to do it. Check this https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html here you get more info how group by works.
Here the complete code. Thanks a lot to Ersoy
$getArray = DB::table('feedback')
->groupBy('noTicket')
->having(DB::raw('count(status)'), '<', 2)
->pluck('noTicket');
$feedback = DB::table('feedback')
->whereIn('noTicket', $getArray)->get();

How do i use subqueries in a left join as a table

Cakephp informs me that it cannot find the table or does not recognize the alias what am i supposed to use?
Hello i am new to cake php ORM can any one tell me how to preform a left join on a subquery I'm really interested to know how to use in working the join
Here is sub query and left join so far please ignore any syntax error
$scl = TableRegistry::get('School');
$subquery = $scl->find();
$subquery->select([
'UID',
'SID',
'Total' => $subquery->func()->sum('numberOfStudents')
])->group(['UID, SID']);
$q->select([
'TeacherID',
'ClassID',
'StudentTotal' => 'sq.Total'
])->join([
'table' => $subquery,
'alias' => 'sq',
'type' => 'LEFT',
'conditions' => ['sq.UID = TeacherID', 'sq.SID = ClassID']
]);
here is the error:
[PDOException] SQLSTATE[42S22]: Column not found: 1054 Unknown column 'sq.UID' in 'on clause'
The problem is that ORM based queries automatically alias the selected fields, so you don't get
SELECT UID, SID ...
but
SELECT UID as School__UID, SID as School__SID
hence referring to sq.UID will fail, as no such field name was selected.
To avoid this problem you can either use an alias that matches the original field name:
->select([
'UID' => 'UID',
'SID' => 'SID',
// ...
])
use the lower level database query that doesn't automatically create aliases:
$subquery = $scl
->getConnection() // connection() in older CakePHP versions
->newQuery()
->from($scl->getTable()); // table() in older CakePHP versions
or refer to the aliased fields in the main query:
'conditions' => [
'sq.' . $scl->getAlias() . '__UID = TeacherID', // alias() in older CakePHP versions
'sq.' . $scl->getAlias() . '__SID = ClassID',
]

SQL Query GROUP BY Issue in Laravel Query Builder

I wrote the following MySQL query to get inbox messages of a user with each user and also with last message...
select *, `chat_channel` as `channel`, MAX(id) as max_id from `messages`
where (`message_to` = 2 or `message_from` = 2)
and (`delete_one` <> 2 and `delete_two` <> 2)
group by `channel`
order by `max_id` desc limit 40 offset 0
Laravel Method which I use...
public static function getInboxMessages($user_id, $limit = 40, $offset = 0, $search_key = null)
{
return Message::hasSearch($search_key)->select("*", DB::raw("MAX(id) as max_id"))->where(function ($sql) use (
$user_id
) {
$sql->where('message_to', '=', $user_id);
$sql->orWhere('message_from', '=', $user_id);
})->where(function ($sql) use ($user_id) {
$sql->where('delete_one', '<>', $user_id);
$sql->where('delete_two', '<>', $user_id);
})->with([
'sender' => function ($q) {
$q->select('id', 'uid', 'username', 'full_name', 'picture');
}
])->with([
'receiver' => function ($q) {
$q->select('id', 'uid', 'username', 'full_name', 'picture');
}
])->orderBy('max_id', 'DESC')->groupBy('chat_channel')->offset($offset)->limit($limit)->get();
}
However, when I run this query in phpMyAdmin I encounter the following error...
1055 - Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'db.messages.id' which is not
functionally dependent on columns in GROUP BY clause; this is
incompatible with sql_mode=only_full_group_by
When I run Laravel code directly then I don't receive any error but I do get the records ordered by as expected.
Go to phpMyAdmin -> YourDB -> SQL Tab
Use following command :
SET GLOBAL sql_mode = 'ONLY_FULL_GROUP_BY';
this will enable only full group by for your sql.
To revert the above command changes use :
SET GLOBAL sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''));
it's the STRICT MODE of mysql for aggregation.
take notice that when you group by something and you select another non aggregated field, the value of that field is not correct 100%. aggregated field are those like the fields you are grouping by, count(field), sum(field),...etc
if you are willing to take that risk, go to config\database.php and edit the strict => true to false
'prefix' => '',
'strict' => false,
'engine' => null,
it is not recommended, and you should rework your query with a join on a group by select
select x from table right join (select gb from table group by gb) as grouped on grouped.gb = table.gb
something like this

CakePHP 3.x - SUM() on ManyToMany

I am getting stuck on using SQL functions queries made in CakePHP 3 in combinations with associations.
The situation is as follows: I have three tables, a 'products' table, an 'orders' table and a join table called 'orders_products'.
In the index of OrdersController I would like to add the total price (= sum of relevant product prices) to the table of orders. In SQL this exactly can be done with the following query:
SELECT orders.id, SUM(products.price)
FROM orders
LEFT JOIN orders_products
ON orders.id = orders_products.order_id
LEFT JOIN products
ON orders_products.product_id = products.id
GROUP BY orders.id;
I figured to following controller code should do the trick:
$orders = $this->Orders->find('all')->contain(['Products']);
$orders
->select(['total_price' => $orders->func()->sum('Products.price')])
->group('Orders.id');
However, when the query object is executed, I get an error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column
'Products.price' in 'field list'
...Even though the association between orders and products is defined.
Calling only $orders = $this->Orders->find('all')->contain(['Products'])->all(); does return an array of orders with each order a number of products, the model has to be set up correctly. Any ideas what might be wrong? Thanks in advance!
From OrdersTable:
$this->belongsToMany('Products', [
'foreignKey' => 'order_id',
'targetForeignKey' => 'product_id',
'joinTable' => 'orders_products'
]);
And from ProductsTable:
$this->belongsToMany('Orders', [
'foreignKey' => 'product_id',
'targetForeignKey' => 'order_id',
'joinTable' => 'orders_products'
]);
One way to do it:
$orders = $this->Orders->find()
->select([
'order_id' =>'orders.id',
'price_sum' => 'SUM(products.price)'
])
->leftJoin('orders_products', 'orders.id = orders_products.order_id'),
->leftJoin('products', 'orders_products.product_id = products.id')
->group('orders.id');

Pagination using joining multiple models

Each exam has one syllabus, each syllabus has one exam. So, I did this in the Exam model:
public $hasOne = 'Syllabuses'; //table: syllabuses, model: Syllabuses
From UsersController I did this:
public $uses = array('Setting', 'Exam', 'Syllabuses');
And in a method in UsersController I wanted to call paginate:
$options = array(
'fields' => array('Exam.id', 'Exam.name', 'Syllabuses.id', 'Syllabuses.name', 'Syllabuses.syllabus', 'Syllabuses.last_updated'),
'joins' => array(
'table' => 'syllabuses',
'conditions' => array('Exam.id = Syllabuses.exam_id')
)
);
$this->paginate = $options;
$this->set('syllabuses', $this->Paginator->paginate('Syllabuses'));
exams table:
---+------+
id | name |
---+------+
and syllabuses table:
---+---------+------+----------+--------------+
id | exam_id | name | syllabus | last_updated |
---+---------+------+----------+--------------+
So, I got some error. Like this:
Error: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'syllabuses Array LEFT JOIN oes.syllabuses AS Syllabuses ON (Syllabuses.`' at line 1
And my SQL that CakePHP prepared is:
SELECT `Exam`.`id`, `Exam`.`name`, `Syllabuses`.`id`, `Syllabuses`.`name`, `Syllabuses`.`syllabus`, `Syllabuses`.`last_updated`
FROM `oes`.`exams` AS `Exam` syllabuses Array
LEFT JOIN `oes`.`syllabuses` AS `Syllabuses` ON (`Syllabuses`.`exam_id` = `Exam`.`id`)
WHERE 1 = 1 LIMIT 20
But what I want is something like the query bellow. I have tested it in mysql:
SELECT `Exam`.`id` AS eid, `Exam`.`name` AS ename, `Syllabuses`.`id` AS sid, `Syllabuses`.`name` AS sname, `Syllabuses`.`syllabus` , `Syllabuses`.`last_updated`
FROM `oes`.`syllabuses` AS `Syllabuses` , exams AS Exam
WHERE Exam.id = Syllabuses.exam_id
ORDER BY Exam.id
LIMIT 20
Now anybody please help me achieve this. What kind of change can make CakePHP to prepare query like that(what I've tasted) to make my Pagination done.
Ok, I think this can be helpful for many programmers. That's why I want to share what I did finally:
$options = array(
'fields' => array(
'Exam.id',
'Exam.name',
'Syllabuses.id',
'Syllabuses.name',
'Syllabuses.exam_id',
'Syllabuses.syllabus',
'Syllabuses.last_updated'
),
'recursive' => 0,
'conditions' => array('Exam.id = Syllabuses.exam_id'),
'limit' => 3
);
$this->paginate = $options;
$syllabuses = $this->Paginator->paginate('Exam');