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
Related
I want to get count from 'user' table with 'plan_validity' is null or more than today's date. both condition need to check for same column with user_id and status = 1.
$currentUser = User::where('user_id', $card_details->user_id)
->where('status', 1)
->where(function (Builder $query) {
return $query
->whereDate('plan_validity', '>=', Carbon::now())
->orWhere('plan_validity', null);
})->count();
giving error
[2021-11-23 10:40:31] production.ERROR: Argument 1 passed to App\Http\Controllers\ProfileController::App\Http\Controllers\{closure}() must be an instance of App\Http\Controllers\Builder, instance of Illuminate\Database\Eloquent\Builder given, called in /home/hellovcard/public_html/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php on line 237 {"userId":17,"exception":"[object] (TypeError(code: 0): Argument 1 passed to App\\Http\\Controllers\\ProfileController::App\\Http\\Controllers\\{closure}() must be an instance of App\\Http\\Controllers\\Builder, instance of Illuminate\\Database\\Eloquent\\Builder given, called in /home/hellovcard/public_html/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Builder.php on line 237 at /home/hellovcard/public_html/app/Http/Controllers/ProfileController.php:34)
how to modify above mentioned query?
this is output I need to get
enter image description here
You are not combining your "where" clauses properly. With the "orWhere" at the end, it essentially ignores all the conditions (ie. which user it relates to) of the preceding where clauses, so adds to the results any row where plan_validity is null. So after you query by user ID and status, you then need to combine the remainder of the query into a logical group like so :
$user = Users::where('user_id', $card_details->user_id)
->where('status', 1)
->where(function (Builder $query) {
return $query
->whereDate('plan_validity', '>=', Carbon::now())
->orWhere('plan_validity', null);
})
->get();
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();
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,
],
I have the following query:
$products = Product::leftJoin(DB::Raw('(SELECT imageable_id, MIN(created_at) as min_created_at
FROM images WHERE imageable_type = "App\\\Product"
GROUP BY imageable_id) AS subquery'), function($join) {
$join->on('subquery.imageable_id', '=', 'products.id');
})
->leftJoin('images', function ($join) {
$join->on('images.imageable_id', '=', 'subquery.imageable_id')
->where('images.created_at', '=', 'subquery.min_created_at');})
->select('products.*', 'images.file_path')
->paginate(5);
When I die and dump the query log, the above gets translated as follows:
"query" => """
select `products`.*, `images`.`file_path` from `products`
left join (SELECT imageable_id, MIN(created_at) as min_created_at
FROM images WHERE imageable_type = "App\\Product"
GROUP BY imageable_id) AS subquery on `subquery`.`imageable_id` = `products`.`id`
left join `images` on `images`.`imageable_id` = `subquery`.`imageable_id` and `images`.`created_at` = ?
limit 5 offset 0
"""
"bindings" => array:1 [
0 => "subquery.min_created_at"
]
Which looks correct, though I'm unsure why a binding has been added for subquery.min_created_at
Now when I execute the above the query in laravel images.file_path is always null when clearly I know there are related images. When I test the above query by pasting it and running directly in MySQL command line I get the expected results i.e. for products which have images, file_path for image is not null. The only difference when I run in MySQL command line is that I'm not doing any binding for subquery.min_created_at - I simply replaced the ? with subquery.min_created_at
Any ideas why the query is behaving this way. If I remove the second left join it works correctly but then I can't select the first created image to load e.g doing the following give me the file_path:
$products = Product::leftJoin(DB::Raw('(SELECT imageable_id, file_path
FROM images WHERE imageable_type = "App\\\Product"
GROUP BY imageable_id) AS subquery'), function($join) {
$join->on('subquery.imageable_id', '=', 'products.id');
})
->select('products.*', 'subquery.file_path')
->paginate(5);
Ideally I want to get my original query working in - any help appreciated.
You are using ->where() for your second join condition:
->where('images.created_at', '=', 'subquery.min_created_at')
This generates
and `images`.`created_at` = ?
which the binding is for.
Instead you should use ->on();
$join->on('images.imageable_id', '=', 'subquery.imageable_id')
->on('images.created_at', '=', 'subquery.min_created_at');
I created a php function to fetch records from a sql table subscriptions, and I want to add a condition to mysql_query to ignore the records in table subscriptions that exists in table removed_items, here is my code;
function subscriptions_func($user_id, $limit){
$subs = array();
$sub_query = mysql_query("
SELECT `subscriptions`.`fo_id`, `subscriptions`.`for_id`, `picture`.`since`, `picture`.`user_id`, `picture`.`pic_id`
FROM `subscriptions`
LEFT JOIN `picture`
ON `subscriptions`.`fo_id` = `picture`.`user_id`
WHERE `subscriptions`.`for_id` = $user_id
AND `picture`.`since` > `subscriptions`.`timmp`
GROUP BY `subscriptions`.`fo_id`
ORDER BY MAX(`picture`.`since_id`) DESC
$limit
");
while ($sub_row = mysql_fetch_assoc($sub_query)) {
$subs [] = array(
'fo_id' => $sub_row['fo_id'],
'for_id' => $sub_row['for_id'],
'user_id' => $sub_row['user_id'],
'pic_id' => $sub_row['pic_id'],
'since' => $sub_row['since']
);
}
return $subs ;
}
My solution is to create another function to fetch the records from table removed_items and set a php condition where I call subscriptions_func() to skip/unset the records that resemble the records in subscriptions_func(), as the following
$sub = subscriptions_func($user_id);
foreach($sub as $sub){
$rmv_sub = rmv_items_func($sub[‘pic_id’]);
If($rmv_sub[‘pic_id’] != $sub[‘pic_id’]){
echo $sub[‘pic_id’];
}
}
This solution succeeded to skip the items in the table removed_items however this solution makes gaps in the array stored in the variable $sub which makes plank spots in the echoed items.
Is there a condition I can add to the function subscriptions_func() to cut all the additional conditions and checks?
Assuming id is the primary key of subscriptions and subs_id is the foreign key in removed_items, then you just have to add a condition to the WHERE clause. Something like this should work :
...
AND `subscriptions`.id NOT IN (SELECT `removed_items`.subs_id FROM `removed_items`)
...
Not related to your problem :
Your code seems vulnerable to SQL injection : use prepared statement to prevent this.
The original Mysql API is deprecated, it is highly recommended to switch to Mysqli instead.