Nested select eloquent - mysql

Is it possible creating nested select in Eloquent using query builder? I have problem where i need to group and use sum on one column of my table but that column is displayed as debit or credit and depends on another column value. I need to group one more time to get both of the sums in one row.
$columns=array( DB::raw('left(archive_ledgers.account,3) as account'),
DB::raw('(case when archive_ledgers.booking_type=1 then sum(archive_ledgers.amount) else 0 end) as debit'),
DB::raw('(case when archive_ledgers.booking_type!=1 then sum(archive_ledgers.amount) else 0 end) as credit')
);
ArchiveLedger::where('accounts.account_type', '<=', $accounts['to'])
->join('accounts',function($join){
$join->on('archive_ledgers.account','=','accounts.account');
})
->join('orders',function($join){
$join->on('orders.id','=','archive_ledgers.order_id');
})
->where('archive_ledgers.account', '>=', $accounts['from'])
->where('document_date', '<=', Input::get('dateTo'))
->where('document_date', '>=', Input::get('dateFrom'))
->select($columns)
->orderBy('archive_ledgers.booking_type',DB::raw('left(archive_ledgers.account,3)'))
->groupBy('archive_ledgers.booking_type',DB::raw('left(archive_ledgers.account,3)'));
I need to do select on result given from this query
select `account`, sum(s1.owes) as owes, sum(s1.asks) as asks, s1.owes - s1.asks as total from (select left(archive_ledgers.account,3) as account, (case when archive_ledgers.booking_type=1 then sum(archive_ledgers.amount) else 0 end) as owes, (case when archive_ledgers.booking_type!=1 then sum(archive_ledgers.amount) else 0 end) as asks from `archive_ledgers` inner join `accounts` on `archive_ledgers`.`account` = `accounts`.`account` inner join `orders` on `orders`.`id` = `archive_ledgers`.`order_id` where `accounts`.`account_type` <= ? and `archive_ledgers`.`account` >= ? and `document_date` <= ? and `document_date` >= ? group by `archive_ledgers`.`booking_type`, left(archive_ledgers.account,3) order by `archive_ledgers`.`booking_type` desc) s1 group by `s1`.`account`)

Related

How to solve error "Subquery returns more than 1 row" in mysql?

i made a query such as the following in codeigniter
$query = $this->db->query('SELECT tanggal,
( SELECT COUNT(*) from absensi_siswa where kehadiran="alfa" GROUP BY tanggal ) AS alfa,
( SELECT COUNT(*) from absensi_siswa where kehadiran="izin" GROUP BY tanggal ) AS izin,
( SELECT COUNT(*) from absensi_siswa where kehadiran="hadir" GROUP BY tanggal ) AS hadir,
( SELECT COUNT(*) from absensi_siswa where kehadiran="sakit" GROUP BY tanggal ) AS sakit
FROM absensi_siswa GROUP BY tanggal');
return $query->result();
Your each sub clause (select count(*) .. ) returns morethan 1 rows, From your query i assume you need individual counts based on different values of kehadiran, you simplify your query as
SELECT tanggal,
SUM(CASE WHEN kehadiran="alfa" THEN 1 ELSE 0 END) AS alfa,
SUM(CASE WHEN kehadiran="izin" THEN 1 ELSE 0 END) AS izin,
SUM(CASE WHEN kehadiran="hadir" THEN 1 ELSE 0 END) AS hadir,
SUM(CASE WHEN kehadiran="sakit" THEN 1 ELSE 0 END) AS sakit
FROM absensi_siswa
GROUP BY tanggal
Using mysql you could make sum clause even shorter like sum(kehadiran="alfa") as alfa, Putting expression in sum(a=a) will return as a boolean 0/1 or true/false and you will get a count based on your expression

SQL query to DQL

I have a big query and I want to add a subquery to get the availability of accommodation-rooms.
This is the SQL subquery:
,(SELECT (CASE WHEN count(_days) > 0 THEN 'yes' ELSE 'No' END) as available
FROM
(
SELECT count(rtd.room_type_id) as _days
FROM room_type_day as rtd
WHERE rtd.date IN ('2018-06-20', '2018-06-21', '2018-06-22')
GROUP BY rtd.room_type_id
HAVING COUNT(rtd.room_type_id) = 3
) as sub) as availability
Can anyone tell me how to convert this SQL in DQL?
Thak you
EDIT
I try with this changes but the response is null every time:
,(SELECT (CASE WHEN count(rtd.roomType) > 0 THEN 'yes' ELSE 'no' END)
FROM AppBundle:RoomTypeDayCancelationConditionAccommodation as RTDCCA2
LEFT JOIN RTDCCA2.roomTypeDay as rtd
WHERE rtd.date IN ('2018-06-20', '2018-06-21', '2018-06-22')
GROUP BY rtd.roomType
HAVING COUNT(rtd.roomType) = 3
) as disponible

The sum amount from join tables is incorrect

I need to get the sum of the admin added into transactions. The transactions is splitted into multiple tables, and now i want to get a total from both tables separately. End up the results i'm getting is ridiculous high. I not sure is it my query multiplied the value somewhere.
User::select('user.name as name',
DB::raw('sum(CASE WHEN current.amount > 0 THEN current.amount END) as current_positive'),
DB::raw('sum(CASE WHEN current.amount < 0 THEN current.amount END) as current_negative'),
DB::raw('sum(CASE WHEN cash.amount > 0 THEN cash.amount END) as cash_positive'),
DB::raw('sum(CASE WHEN cash.amount < 0 THEN cash.amount END) as cash_negative')
)->leftjoin('current_transaction as current', 'current.created_by', '=', 'user.id')
->leftjoin('cash_transaction as cash', 'cash.created_by', '=', 'user.id')
->whereBetween('current.created_at', [$start_date->format('Y-m-d'), $end_date->format('Y-m-d')])
->whereBetween('cash.created_at', [$start_date->format('Y-m-d'), $end_date->format('Y-m-d')])
->where('user.type', 3)
->groupBy('user.name')
->get();
Update
I'm trying with the solution from M Khalid, and the following is the error message:
->mergeBindings($CUT)
Symfony\Component\Debug\Exception\FatalThrowableError: Type error: Argument 1 passed to Illuminate\Database\Query\Builder::mergeBindings() must be an instance of Illuminate\Database\Query\Builder
->mergeBindings($CUT->getBindings())
Symfony\Component\Debug\Exception\FatalThrowableError: Type error: Argument 1 passed to Illuminate\Database\Query\Builder::mergeBindings() must be an instance of Illuminate\Database\Query\Builder, array given
->addBinding($CUT)
ErrorException: Object of class Illuminate\Database\Eloquent\Builder could not be converted to string
You are getting wrong sum amount because of the left joins and each joined table may have more than one rows per user which makes the sum amount quite higher than the original one, to sort out this issue you need to calculation these sum amounts in indvidual sub clauses and then join these with main somewhat like
select users.name as name,cu.*,ca.*
from users
left join (
select created_by,
sum(CASE WHEN amount > 0 THEN amount END) as current_positive,
sum(CASE WHEN amount < 0 THEN amount END) as current_negative
from current_transaction
where created_at between :start_date and :end_date
group by created_by
) cu on users.id = cu.created_by
left join (
select created_by,
sum(CASE WHEN amount > 0 THEN amount END) as cash_positive,
sum(CASE WHEN amount < 0 THEN amount END) as cash_negative
from cash_transaction
where created_at between :start_date and :end_date
group by created_by
) ca on users.id = ca.created_by
where users.type = 3
To do above in laravel is quite complex like for each sub clause get the sql and query builder object and then use them in main query somewhat like below
// For current_transaction
$CUT = CurrentTransaction::query()
->select('created_by',
DB::raw('sum(CASE WHEN amount > 0 THEN amount END) as current_positive'),
DB::raw('sum(CASE WHEN amount < 0 THEN amount END) as current_negative')
)
->from('current_transaction')
->whereBetween('created_at', [$start_date->format('Y-m-d'), $end_date->format('Y-m-d')])
->groupBy('created_by');
$CUTSql = $CUT->toSql();
// For cash_transaction
$CAT = CashTransaction::query()
->select('created_by',
DB::raw('sum(CASE WHEN amount > 0 THEN amount END) as cash_positive'),
DB::raw('sum(CASE WHEN amount < 0 THEN amount END) as cash_negative')
)
->from('cash_transaction')
->whereBetween('created_at', [$start_date->format('Y-m-d'), $end_date->format('Y-m-d')])
->groupBy('created_by');
$CATSql = $CAT->toSql();
// Main query
User::select('user.name as name','cu.*','ca.*')
->leftjoin(DB::raw('(' . $CUTSql. ') AS cu'),function($join) use ($CUT) {
$join->on('user.id', '=', 'cu.created_by');
/* ->addBinding($CUT->getBindings());*/
})
->leftjoin(DB::raw('(' . $CATSql. ') AS ca'),function($join) use ($CAT) {
$join->on('user.id', '=', 'ca.created_by');
/* ->addBinding($CAT->getBindings()); */
})
->where('user.type', 3)
->mergeBindings($CUT) /* or try with ->mergeBindings($CUT->getBindings()) */
->mergeBindings($CAT) /* or try with ->mergeBindings($CAT->getBindings()) */
->get();

how to get SUM() of COUNT() column in MySQL

i have the following query, tried using case statement
SELECT t.inst_id, t.inst_username, tcm.city_name,
SUM(CASE WHEN psb.pms_student_bucket_id IS NULL THEN 1 ELSE 0 END) AS not_assiged,
SUM(CASE WHEN COUNT(psb.pms_student_bucket_id) BETWEEN 1 AND 50 THEN 1 ELSE 0 END) AS '1-50',
SUM(CASE WHEN COUNT(psb.pms_student_bucket_id) > 50 THEN 1 ELSE 0 END) AS ' > 50'
FROM tbl_si_di t
JOIN tbl_city_master tcm ON tcm.city_id = t.city_ref_id
JOIN tbl_si_students tss ON tss.inst_ref_id = t.inst_id
LEFT JOIN pms_student_bucket psb ON psb.user_ref_id = tss.user_ref_id
GROUP BY t.inst_id;
I need SUM of pms_student_bucket_id column when their COUNT is '1-50' and '>50'. Right now this query is saying Invalid use of group function.
How would I SUM on COUNT of "pms_student_bucket_id" equals/between some value in mysql?
you could put it in a subquery
SELECT inst_id, inst_username, city_name,
SUM(pms_student_bucket_id IS NULL ) AS not_assiged,
SUM(num_bucket >=10 AND num_bucket <= 20) AS '10 - 20'
SUM(num_bucket <= 50) AS '1-50',
SUM(num_bucket > 50) AS ' > 50'
FROM
( SELECT t.inst_id, t.inst_username, tcm.city_name,psb.pms_student_bucket_id,
COUNT(psb.pms_student_bucket_id) as num_bucket
FROM tbl_si_di t
JOIN tbl_city_master tcm ON tcm.city_id = t.city_ref_id
JOIN tbl_si_students tss ON tss.inst_ref_id = t.inst_id
LEFT JOIN pms_student_bucket psb ON psb.user_ref_id = tss.user_ref_id
GROUP BY t.inst_id
)t1
GROUP BY inst_id;
note you can use the boolean values returned to do what you want.. aka you don't need a case statement, the boolean value returns a 1 or 0 which can then be summed..

SQL statement for GROUP BY

I am really stucked with one sql select statement.
This is output/result which I get from sql statement below:
WHAT I need: I need to have columns assignedVouchersNumber and usedVouchersNumber together in one row by msisdn. So for example if you can see "msisdn" 723709656 there are two rows now.. one with assignedVouchersNumber = 1 and second with assignedVouchersNumber = 1 too.
But I need to have it in one row with assignedVouchersNumber = 2. Do you now where is the problem?
SELECT eu.msisdn,
eu.id as userId,
sum(case ev.voucherstate when '1' then 1 else 0 end) as assignedVouchersNumber,
sum(case ev.voucherstate when '2' then 1 else 0 end) as usedVouchersNumber,
ev.extra_offer_id,
ev.create_time,
ev.use_time,
ev.id as voucherId,
ev.voucherstate
FROM extra_users eu
JOIN (SELECT sn.msisdn AS telcislo,
stn.numberid
FROM stats_number sn
JOIN stats_target_number AS stn
ON ( sn.numberid = stn.numberid )
WHERE stn.targetid = 1) xy
ON eu.msisdn = xy.telcislo
JOIN extra_vouchers AS ev
ON ( eu.id = ev.extra_user_id )
WHERE ev.create_time BETWEEN '2012-07-23 00:00:00' AND '2013-08-23 23:59:59'
AND ev.use_time <= '2013-08-23 23:59:59'
AND ev.use_time >= '2012-07-23 00:00:00'
AND ev.voucherstate IN ( 1, 2 )
AND Ifnull(ev.extra_offer_id IN( 2335, 3195, 30538 ), 1)
GROUP BY eu.msisdn, ev.extra_offer_id, ev.voucherState
ORDER BY eu.msisdn ASC
You have two different extra_offer_id for same msisdn and VouchersNumber. Thats why you get two rows.
I got it... there should not be groupping by ev.voucherState in
GROUP BY eu.msisdn, ev.extra_offer_id, ev.voucherState
After then I have removed ev.voucherState it is working now.