general error: 2031 when uniting two queries (laravel datatables) - php

Those are the queries that form the collection to be pushed to the laravel datatables builder:
foreach (Session::get('trienios') as $trienio) {
$oeTrienios = $oeTrienios->where('data_trienio', $trienio->trienio)->whereHas('curso', function ($query) use ($trienio) {
$query->where('curso', $trienio->curso);
});
}
$union = Trienio::with('curso')->whereHas('curso', function ($query) use ($coordinatedCourse) {
$query->where('curso', $coordinatedCourse);
})->union($oeTrienios);
$trienios = \DB::table(\DB::raw("({$union->toSql()}) as x"))->select(['data_trienio']);
There is a tutorial on the official laravel-datatables site that "explains" how to work with united queries, however it is pretty vague and can't realistically explain anything, and besides, when I tried to add a code they had on the tutorial:
$trienios = \DB::table(\DB::raw("({$union->toSql()}) as x"))
It gives me the following error:
SQLSTATE[HY000]: General error: 2031 (SQL: select count(*) as aggregate from (select `data_trienio` from ((select * from `trienios` where exists (select * from `cursos` where `trienios`.`curso_id` = `cursos`.`id` and `curso` = ?)) union (select * from `trienios` where `data_trienio` = ? and exists (select * from `cursos` where `trienios`.`curso_id` = `cursos`.`id` and `curso` = ?) and `data_trienio` = ? and exists (select * from `cursos` where `trienios`.`curso_id` = `cursos`.`id` and `curso` = ?))) as x) count_row_table)
If I, however, attach the parameter ->get() to the ->union($oeTrienios), it will work just fine, however, the collection will come unorderable on the datatable.
How do I solve this issue? Any help would be very welcome.
P.S - Link to the demo: https://datatables.yajrabox.com/fluent/union

The subquery $union->toSql() has only sql code without parameters and you need call bindings. See here and code will be:
$trienios = \DB::table(\DB::raw("({$union->toSql()}) as x"))
->select(['data_trienio'])
->mergeBindings($union);

Related

How to convert raw SQL query to Laravel Query Builder

I need a following code to convert to Laravel query can any one help me with these.
SELECT id, `leave_name`, `total_leave_days`, leave_id, leave_taken_days FROM `leaves` AS t1 INNER JOIN ( SELECT leave_id, SUM(`leave_taken_days`) AS leave_taken_days FROM `leave_applications` WHERE user_id = 2 AND statuses_id = 2 GROUP BY leave_id ) AS t2 ON t1.id = t2.leave_id
I even tried but the output is not showing atall.
$user_leaves = DB::table('leaves')
->select('id', 'leave_name', 'total_leave_days', 'leave_id', 'leave_taken_days')
->join('leave_application', 'leave_application.leave_id', '=', 'leave.id')
->select('leave_application.leave_id', DB::raw("SUM(leave_taken_days) as leave_application.leave_taken_days"))
->where('user_id','=', 2)
->where('statuses_id','=', 2)
->get();
How can I solve this issue?
UPDATE
Relations between two models.
Leave Model
public function leave_application()
{
return $this->belongsTo(LeaveApplication::class, 'id' , 'leave_id');
}
Leave Application Model
public function leave()
{
return $this->belongsTo(Leave::class, 'leave_id', 'id');
}
Try this :
$user_leaves = Leave::select('leaves.id', 'leaves.leave_name', 'leaves.total_leave_days', 'leave_applications.leave_id', DB::raw('SUM(leave_applications.leave_taken_days) as leave_taken_days'))
->with('leave_application')
->whereHas('leave_application', function($q) {
$q->where('user_id', 2)
->where('statuses_id', 2);
})
->groupBy('leaves.id')
->get();
On this topic I would like to give my recommendations for some tools to help you out in the future.
SQL Statement to Laravel Eloquent to convert SQL to Laravel query builder. This does a decent job at low level queries. It also saves time when converting old code.
The other tool I use to view the query that is being run is Clock Work
I keep this open in a tab and monitor slow nasty queries or, also gives me perspective on how the query builder is writing SQL. If you have not use this extension I highly recommend getting and using it.
Actually I found my answer,
$user_leaves = DB::table('leaves as t1')
->select('t1.id', 't1.leave_name', 't1.total_leave_days', 't2.leave_id', 't2.leave_taken_days')
->join(DB::raw('(SELECT leave_id, SUM(leave_taken_days) AS leave_taken_days FROM leave_applications WHERE user_id = ' . $user_id . ' AND statuses_id = 2 GROUP BY leave_id) AS t2'), function ($join) {
$join->on('t1.id', '=', 't2.leave_id');
})
->get();
You can use DB:select("your query", params) and put your query and params (as an array (optional)
As below sample:
$result = DB:select("
SELECT id, `leave_name`, `total_leave_days`, leave_id, leave_taken_days
FROM `leaves` AS t1
INNER JOIN (
SELECT leave_id, SUM(`leave_taken_days`) AS leave_taken_days
FROM `leave_applications`
WHERE user_id = 2
AND statuses_id = 2
GROUP BY leave_id
) AS t2 ON t1.id = t2.leave_id" , $params
);
return response()->json($result);

How to get ranking using sub query in Laravel?

Version
Laravel : 7.28.3
mysql : Ver 14.14 Distrib 5.7.29, for osx10.15 (x86_64) using EditLine wrapper
Tables
contents (id)
content_views (id, content_id)
What I'm Trying To Do
I would like to get a rank of a content by how many it has content_views.
Code
App/Models/Content.php
/**
* Attribute of get rank by views
*
* #return Int
*/
public function getViewsRankingAttribute()
{
DB::statement(DB::raw('set #c=0'));
$result = collect(
DB::select('select rank from
(
select _ranking.*, #c:=#c+1 as rank from
(
select content_views.content_id, count(content_views.id) as views
from content_views
group by content_views.content_id
order by views desc
) as _ranking
) as ranking
where content_id = :contentId', [
'contentId' => $this->id
]))
->first();
return $result ? $result->rank : '-';
}
This is actually working in mysql 5.8 but it's not in 8.0.
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 'from ( select _ranking., #c:=#c+1 as rank from ' at line 1 (SQL: select rank from ( select _ranking., #c:=#c+1 as rank from ( select content_views.content_id, count(content_views.id) as views from content_views group by content_views.content_id order by views desc ) as _ranking ) as ranking where content_id = :contentId)
MySQL 8.0 introduced window functions, which make it much easier to perform ranking tasks. On the other hand, user variables are officially planned for deprecation in future versions.
Time to embrace the future! You can rewrite your query as follows:
select *
from (
select content_id, count(*) as views, rank() over(order by count(*) desc) rn
from content_views
group by content_id
) t
where content_id = :content_id
This actually worked!
public function getViewsRankingAttribute()
{
$rank = '-';
if ($this->views->isNotEmpty()) {
$views = $this->views->count();
$rank = Content::withCount('views')
->having('views_count', '>', $views)
->get()
->count();
$rank++;
}
return $rank;
}

WhereHas with WhereRaw on one to many relationship (Laravel)

I have an order table and an order_details table in my system.
Relationship between order table and order details table is one to many, means One order has many order details.
Now the problem is i am trying to filter the order with the quantity of items a that are stored in order_details table.
what i doing right know trying to access with whereHas
if ($request->has('quantity') && $request->quantity != null){
$query = $query->whereHas('orderDetails',function ($q) use ($request){
$q->whereRaw('SUM(Quantity) >= '.$request->quantity);
});
}
$orders = $query->orderBy('OrderID','desc')->get();
But it throws an error
General error: 1111 Invalid use of group function (SQL: select * from `orders` where `AddedToCart` = 0 and `PaymentSucceeded` = 1 and exists (select * from `order_details` where `orders`.`OrderID` = `order_details`.`OrderID` and SUM(Quantity) >= 12) order by `OrderID` desc)
I will be vary thankful if i get the solution
To be able to use sum function you need to group by data and as I see you are trying to group them by orderID.
An approach like this might help:
$ordersIDs = DB::table('orderDetails')
->groupBy('OrderID')
->havingRaw('SUM(Quantity)', '>=', 12)
->pluck('orderID')->toArray();
$orders = DB::table('orders')
->whereIn($ordersIDs)
->get();
The above code executes two SQL queries, you can mix them easily to make one.
Hope it helps.

SQL exists in Laravel 5 query builder

Good morning,
I've been trying for quite a lot of time to translate this query(which returns an array of stdClass) into query builder so I could get objects back as Eloquent models.
This is how the query looks like untranslated:
$anketa = DB::select( DB::raw("SELECT *
FROM v_anketa a
WHERE not exists (select 1 from user_poeni where anketa_id=a.id and user_id = :lv_id_user)
Order by redni_broj limit 1"
), array( 'lv_id_user' => $id_user,
));
I have tried this, but it gives a syntax error near the inner from in the subquery:
$anketa = V_anketa::selectRaw("WHERE not exists (select 1 from user_poeni where anketa_id=a.id and user_id = :lv_id_user)", array('lv_id_user' => $id_user,)
)->orderBy('redni_broj')->take(1)->first();
The problem is this exists and a subquery in it. I couldn't find anything regarding this special case.
Assume each table has an appropriate Eloquent model.
V_anketa is a view. The db is postgresql.
As far as the query goes I believe this should work:
$anketa = V_anketa::whereNotExists(function ($query) use ($id_user) {
$query->select(DB::raw(1))
->from('user_poeni')
->where('anketa.id', '=', 'a.id')
->where('user_id', '=', $id_user);
})
->orderBy('redni_broj')
->first();
but I'm not clear on what do you mean by "assuming every table has an Eloquent model" and "V_anketa" is a view...
Assuming the SQL query is correct, this should work:
$anketa = DB::select(sprintf('SELECT * FROM v_anketa a WHERE NOT EXISTS (SELECT 1 FROM user_poeni WHERE anketa_id = a.id AND user_id = %s) ORDER BY redni_broj LIMIT 1', $id_user));
If you want to get back an Builder instance you need to specify the table:
$anketa = DB::table('')->select('');
If you however, want to get an Eloquent Model instance, for example to use relations, you need to use Eloquent.

How to use "distinct on" with doctrine?

I try to use "distinct on" with doctrine but I get the following error:
Error: Expected known function, got 'on'
class Report extends EntityRepository
{
public function findForList() {
$queryBuilder = $this->createQueryBuilder('r');
$queryBuilder->select('distinct on (r.parentId)')
->orderBy('r.parentId')
->orderBy('r.date', 'DESC');
return $queryBuilder->getQuery()->getResult();
}
}
How could I implement the following query?
select distinct on (r.parent_id)
r.parent_id,
r.id,
r.name
from frontend.report r
order by r.parent_id, r.date desc;
Apparently it doesn't seem possible to do this with the query builder. I tried to rewrite my query in different ways:
select * from frontend.report r
where
r.id in (
select distinct
(select r3.id from frontend.report r3
where r3.parent_id = r.parent_id
order by r3.date desc limit 1) AS id
from frontend.report r2);
But doctrine doesn't support LIMIT:
public function findForList() {
$qb3 = $this->_em->createQueryBuilder();
$qb3->select('r3.id')
->from('MyBundle:frontend\report', 'r3')
->where('r3.parentId = r2.parentId')
->orderBy('r3.date', 'DESC')
//->setMaxResults(1)
;
$qb2 = $this->_em->createQueryBuilder();
$qb2->select('(' . $qb3->getDql() . ' LIMIT 1)')
->from('MyBundle:frontend\report', 'r2')
->distinct(); // groupBy('r2.parentId')
$queryBuilder = $this->createQueryBuilder('r');
$queryBuilder->where(
$queryBuilder->expr()->in('rlt.id', $qb2->getDql())
);
return $queryBuilder->getQuery()->getResult();
}
I think the only solution is to use native SQL queries.
This response is for someone that yet looking for the solution in similar cases.
If you need a sample "DISTINCT" you can use:
$queryBuinder->select('parentId')
->distinct('parentId');
but if you want a "DISTINCT ON" you should use Native SQL instead of Query Builder, check out the manual here: Doctrine Native SQL
Just remove the on word :
$queryBuilder->select('distinct r.parentId')
->orderBy('r.parentId')
->orderBy('r.date', 'DESC');

Categories