I have a strange problem with Eloquent which I'm trying to do the following:
$this->node = \DB::table('permission')
->select('permission.id',
'object.name as object_name',
'permission.created_at',
'object.id as object_id')
->join('object', 'object.id', '=', 'permission.object_id')
->join('action', 'action.id', '=', 'permission.action_id')
->where('permission.person_id', $this->person['id'])
->groupBy('permission.object_id')
->orderBy('permission.created_at', 'desc')
->paginate(5);
Laravel Framework report an Error:
QueryException in Connection.php line 761: SQLSTATE[42000]: Syntax
error or access violation: 1055 'permission.id' isn't in GROUP
BY (SQL: select permission.id, object.name as object_name,
permission.created_at, object.id as object_id from
permission inner join object on object.id =
permission.object_id inner join action on action.id =
permission.action_id where permission.person_id = 1 group by
permission.object_id order by permission.created_at desc limit
5 offset 0)
I've added an Eloquent debugging function DB::listen in AppServiceProvider:
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
DB::listen(function ($query) {
echo "<pre>";
print_r($query->sql);
echo "</pre>";
// $query->sql
// $query->bindings
// $query->time
});
}
...
And it does print this SQL query:
select `permission`.`id`,
`object`.`name` as `object_name`,
`permission`.`created_at`,
`object`.`id` as `object_id`
from `permission`
inner join `object` on `object`.`id` = `permission`.`object_id`
inner join `action` on `action`.`id` = `permission`.`action_id`
where `permission`.`person_id` = 1
group by `permission`.`object_id`
order by `permission`.`created_at` desc
limit 5 offset 0
Which is valid in MySQL through PhpMyAdmin and here is the output for the query:
Even So, I tested in mysql command directly and it does work just fine, look at mysql output:
Any idea?
Thanks
Faced same problem with laravel 5.3
They are trying to enforce strict query writing came with mysql-5.7
However to disabled this just go to config/database.php and change strict flag
'mysql' => [
.
.
.
'strict' => false,
//'strict' => true,
.
.
],
Hope this will solve your problem too.
PS - For details on strict query writing refer to #Shadow's answer
This query is against the sql standard and is only valid in mysql under certain sql mode settings. See mysql documentation on MySQL Handling of GROUP BY:
SQL92 and earlier does not permit queries for which the select list,
HAVING condition, or ORDER BY list refer to nonaggregated columns that
are neither named in the GROUP BY clause nor are functionally
dependent on (uniquely determined by) GROUP BY columns. For example,
this query is illegal in standard SQL92 because the nonaggregated name
column in the select list does not appear in the GROUP BY:
SELECT o.custid, c.name, MAX(o.payment) FROM orders AS o, customers
AS c WHERE o.custid = c.custid GROUP BY o.custid; For the query to
be legal in SQL92, the name column must be omitted from the select
list or named in the GROUP BY clause.
SQL99 and later permits such nonaggregates per optional feature T301
if they are functionally dependent on GROUP BY columns: If such a
relationship exists between name and custid, the query is legal. This
would be the case, for example, were custid a primary key of
customers.
You either need to disable the only_full_group_by sql mode (it is part of strict sql mode as well), or use any_value() function in the select list for non-aggregated fields that are not in the group by clause.
SELECT name, ANY_VALUE(address), MAX(age) FROM t GROUP BY name;
Related
I am trying to convert this SQL query to Eloquent in Laravel
Convert SQL code to Eloquent
SELECT
session_id,
SUM(points) AS total_points
FROM
(
SELECT
session_id,
spent_points AS points
FROM
session_details
WHERE
session_id IN
(
" - Meagevy6y9ukbmFXvB7",
" - Meak6dG9iqvHWfAGQvy"
)
UNION ALL
SELECT
session_id,
price_points
FROM
template_sales
WHERE
session_id IN
(
" - Meagevy6y9ukbmFXvB7",
" - Meak6dG9iqvHWfAGQvy"
)
)
t
GROUP BY
session_id
my code in Laravel but not working
$ids = ["-Meagevy6y9ukbmFXvB7","-Meak6dG9iqvHWfAGQvy"];
$query = DB::table('session_details')
->select('session_id',DB::raw('SUM(points) AS total_points FROM ( SELECT session_id, spent_points AS points FROM session_details
WHERE session_id IN ("'.$ids.'") UNION ALL SELECT session_id,price_points FROM template_sales WHERE session_id IN ("'.$ids.'") ) t GROUP BY session_id'))
->get();
I'd advise you to use Eloquent models & Eloquent relationships to make the query more readable.
Execute the following in your terminal to create a new model:
php artisan make:model SessionDetail
Open the file that Laravel has generated for you in /app/Models (or whatever folders your models are in), and set the table in the model by putting the following property into the model class: public $table = "session_details";
If your model does not use or have Laravel timestamps which are usually created_at & updated_at, you can also use this property to disable them in the model: public $timestamps = false;
After that, create generate another model by execute the following command in your terminal:
php artisan make:model TemplateSale
Follow the same instructions again but this time change the table name to template_sales
After you have done that, head into your SessionDetail model and make a relationship to the TemplateSale model using the following code (this must be in the model class beneath the properties):
public function template_sales() {
return $this->hasMany(TemplateSale::class);
}
After that, you can replace your query with this line of code:
$query = \App\Models\SessionDetail::select("session_id", "SUM(points) as total_points")->whereIn("session_id", $ids)->get();
To get the template sales from that query, you have to use $query->template_sales;
If I got anything wrong, please tell me & I'll fix it ASAP
There is documentation available for all the operations in your query.
For selected columns use select('column1', 'column2', ...)
For selected aggregate columns use selectRaw('sum(column) as column')
For WHERE column IN (...) use whereIn('column', $array)
For subquery tables, use Closures or Builder classes (DB::table(fn($q) => ... , alias) or DB::table($builder, alias))
For UNION ALL use unionAll() with the same syntax as subquery tables.
Option 1: Closures
$ids = ["-Meagevy6y9ukbmFXvB7","-Meak6dG9iqvHWfAGQvy"];
$query = DB::table(function ($sub) use ($ids) {
$sub->select('session_id', 'spent_points as points')
->from('session_details')
->whereIn('session_id', [1,2])
->unionAll(function ($union) use ($ids) {
$union->select('session_id', 'price_points')
->from('template_sales')
->whereIn('session_id', $ids);
});
}), 't')
->select('session_id')
->selectRaw('sum(points) as total_points')
->groupBy('session_id')
->get();
Option 2: Builder (or translating the subqueries from the inside-out)
$ids = ["-Meagevy6y9ukbmFXvB7","-Meak6dG9iqvHWfAGQvy"];
$union = DB::table('template_sales')
->select('session_id', 'price_points')
->whereIn('session_id', $ids);
$sub = DB::table('session_details')
->select('session_id', 'spent_points as points')
->whereIn('session_id', $ids)
->unionAll($union);
$query = DB::table($sub, 't')
->select('session_id')
->selectRaw('sum(points) as total_points')
->groupBy('session_id')
->get();
Pick whichever you prefer. Both evaluate to the same query you posted.
Problem
I am trying to make a scope in my model with this SQL query:
SELECT *
FROM ro_container_events
WHERE (container_id, location_timestamp, id)
IN (
SELECT distinct container_id, MAX(location_timestamp) AS lts, MAX(id) AS rce_id
FROM ro_container_events
GROUP BY container_id
)
The model name is ContainerEvent and the name of the table in my database is ro_container_events.
Also, I know that the SQL query is valid because I ran it in my MySQL administration tool (HeidiSQL), and it returns the good rows.
What I've tried
My scope (in my ContainerEvent model) currently looks like this:
public function scopeLatestEventForContainers($query)
{
return $query->select(DB::raw('
SELECT *
FROM ro_container_events
WHERE (container_id, location_timestamp, id)
IN (
SELECT distinct container_id, MAX(location_timestamp) AS lts, MAX(id) AS rce_id
FROM ro_container_events
GROUP BY container_id
)'
)
);
}
But it doesn't return any rows?
My research
I've been searching about this topic for a while but can't seem to find what is wrong with my scope...
The Laravel documentation says that we can use :
DB::raw('...')
In order to make a specific SQL query.
And I've seen on some other threads that I should be able to make a scope with the following :
return $query->select(DB::raw('...');
try Eloquent:
//as your where in select this must return just one row with 3 field
$in = ContainerEvent::distinct('container_id', 'location_timestamp', 'id')->where(fn($q) => {
'id' => ContainerEvent::max('id')
'location_timestamp' => ContainerEvent::max('location_timestamp')
])->get();
but it may be wrong because there is ambitious in db structure.
I just try your SQL syntax using Laravel query builder, and the result using method toSql() returns the exact thing that you need.
SELECT * FROM ro_container_events WHERE (container_id, location_timestamp, id) IN (
SELECT distinct container_id, MAX(location_timestamp) AS lts, MAX(id) AS rce_id
FROM ro_container_events
GROUP BY container_id
)
Try this function:
public function scopeLatestEventForContainers($query)
{
return $query->whereIn(\DB::raw('(container_id, location_timestamp, id)'), function ($q) {
$q->distinct()->select(\DB::raw('
container_id,
MAX(location_timestamp) AS lts,
MAX(id) AS rce_id FROM `ro_container_events`
'))->groupBy('container_id');
});
}
Hope that's help :)
Best,
vreedom18
I have a query which joins tables from two different databases. It is already showing results but then I wanted to show only unique results because some results are redundant. So I added a GROUP BY to get only the unique results but an error appears.
This is my code:
public function search_results_accommodations($location,$from_date,$to_date,$bedroom,$guests)
{
$this->db->select('*, akzapier.bookings.id as BOOKING_ID, akzapier.properties.id as PROPERTY_ID, ci_alexandrohomes.assigned_property.ID as ASSIGNED_PROPERTY_ID, ci_alexandrohomes.listings.ID as LISTING_ID');
$this->db->from('akzapier.bookings');
$this->db->join('akzapier.properties', 'akzapier.properties.id=akzapier.bookings.property_id', 'inner');
$this->db->join('ci_alexandrohomes.assigned_property', 'ci_alexandrohomes.assigned_property.property_id=akzapier.properties.id', 'inner');
$this->db->join('ci_alexandrohomes.listings', 'ci_alexandrohomes.listings.ID=ci_alexandrohomes.assigned_property.listing_id');
$this->db->where('akzapier.bookings.check_in !=', $from_date);
$this->db->where('akzapier.bookings.check_out !=', $to_date);
$this->db->where('ci_alexandrohomes.listings.city', $location);
$this->db->where('ci_alexandrohomes.listings.bedrooms', $bedroom);
$this->db->where('ci_alexandrohomes.listings.guests', $guests);
$this->db->group_by('akzapier.properties.id', 'ASC')
$query = $this->db->get();
return $query->result();
}
The error doesn't show up in the page so I converted it to SQL to see the real deal:
SELECT * akzapier.bookings.id as BOOKING_ID, akzapier.properties.id as PROPERTY_ID, ci_alexandrohomes.assigned_property.ID as ASSIGNED_PROPERTY_ID, ci_alexandrohomes.listings.ID as LISTING_ID
FROM akzapier.bookings
INNER JOIN akzapier.properties ON akzapier.properties.id=akzapier.bookings.property_id
INNER JOIN ci_alexandrohomes.assigned_property ON ci_alexandrohomes.assigned_property.property_id=akzapier.properties.id
INNER JOIN ci_alexandrohomes.listings ON ci_alexandrohomes.listings.ID=ci_alexandrohomes.assigned_property.listing_id
WHERE akzapier.bookings.check_in != '2019-09-21'
AND akzapier.bookings.check_out != '2019-09-30'
AND ci_alexandrohomes.listings.city = ‘1’
AND ci_alexandrohomes.listings.bedrooms = '2'
AND ci_alexandrohomes.listings.guests = '4'
GROUP BY akzapier.bookings.property_id ASC
ERROR SAYS:
1055 - Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'akzapier.bookings.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
In general, when you use GROUP BY, your SELECT statement must contain either aggregates (such as MAX(...), COUNT(...), etc.) or the columns must appear in GROUP BY. You have selected all of the fields, non-aggregated, with the asterisk *. In this case, it's complaining about the field akzapier.bookings.id, which is neither aggregated, nor in your GROUP BY.
If you actually want unique values, try SELECT DISTINCT, which will drop duplicate rows from the result.
This is my model function in laravel blade. In this i have two table "admins" and "candidates" using join query i join the two table. But While running my code I probably get this below mentioned
error.
if ( Input::has('recordcreator') and $request->input('recordcreator') != NULL){
$querys->join('admins','admins.adminid','=','candidates.createdby');
$querys->where('admins.firstname', 'LIKE', '%'. $request->input('recordcreator') .'%')->orWhere('admins.lastname', 'LIKE', '%'. $request->input('recordcreator') .'%')->where('admins.status','1');
}
SQLSTATE[23000]: Integrity constraint violation: 1052 Column
'firstname' in order clause is ambiguous (SQL: select * from
rsi_candidates inner join rsi_admins on rsi_admins.adminid =
rsi_candidates.createdby where rsi_admins.lastname LIKE %ic%
and rsi_admins.status = 1 or rsi_admins.firstname LIKE %ic%
group by rsi_candidates.candidateid order by firstname asc limit
20 offset 0)
As i see your laravel error you may add the table name in front of your firstname column in orderby clause
Or you may also try this solution
open your database.php file located in config directory
Config->database.php in your mysql database array add this.
'strict' => false,
I have the following 4 SQL tables:
studios
integer id
string name
integer last_movie_id
movies
integer id
integer studio_id
string type
actor_movie
integer movie_id
integer actor_id
actors
integer id
string name
I'm trying to build a query that gives me for a specific actor, the list of the studios he worked for, including the last movie of the studio (wether the actor played in it or not):
name of the studio
date of the last movie from this studio
type of the last movie from this studio
My code is the following (in ActorController):
$actor = \App\Actor::findOrFail($id);
$studios = \DB::table('actor_movie')
->leftjoin('movies', 'actor_movie.movie_id', '=', 'movies.id')
->leftjoin('studios', 'studios.id', '=', 'movies.studio_id')
->select(
'studios.name',
'studios.last_movie_date'
\DB::raw('(SELECT movies.type FROM movies WHERE movies.id = studios.last_movie_id') as type')
->where('actor_movie.actor_id', $actor->id)
->groupBy('studios.name', 'studios.last_movie_date', 'type')
->get();
But I have the following error:
SQLSTATE[42000]: Syntax error or access violation: 1055 Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column [...] this is incompatible with sql_mode=only_full_group_by
I don't want to set the sql_strict_mode to false in order to circumvent this error. I would like to understand what's wrong with my query. I tried using aggregation functions like "ANY_VALUE" but nothing worked out.
You have written the query wrongly - specifically
\DB::raw('(SELECT movies.type FROM movies WHERE movies.id = studios.last_movie_id') as type which is in select statement. This query gives you value(s) not COLUNM NAME which should be in select statement like select col1Name, col2Name, .... from .....
As you did not provide full table structures & relation(i.e-you mentioned studios.last_movie_date in query but not in question) it is very hard for anyone to give you correct query but the below should work fine -
$actor = \App\Actor::findOrFail($id);
$studios = \DB::table('actor_movie')
->leftjoin('movies', 'actor_movie.movie_id', '=', 'movies.id')
->leftjoin('studios', 'studios.id', '=', 'movies.studio_id')
->leftjoin('movies as mov2', 'studios.last_movie_id', '=', 'mov2.id')
->select(
'studios.name',
'studios.last_movie_date',
'mov2.type'
)
->where('actor_movie.actor_id', $actor->id)
->groupBy('studios.name', 'studios.last_movie_date', 'type')
->get();
For other queries I think you got the idea.
How would I do it -
create a migration for below query -
create view actors_studio as
select am.actor_id, mov.studio_id, std.name as studio_name, std.last_mov_id, mov2.type as last_mov_type
from actor_movie as am
left join movies as mov on am.movie_id = mov.id
left join studios as std on std.id = mov.studio_id
left join movies as mov2 on std.last_movie_id = mov2.id
group by actor_id, studio_id, studio_name, last_mov_id, last_mov_type
order by actor_id, studio_id;
Then run the migrate command and then create a model for view actors_studio i.e VwActorStudios and then a simple query
$studios = VwActorStudios::where('actor_id', $actor->id)->get();