Dynamic Pivot MYSQL incomplete SQL - php

I have been having trouble with a project i've been working on for about 5 weeks now, i've made various stackoverflow posts along the way and i'm almost at the final hurdle.
I was having issues with duplicated data searching weekly sums but I seem to have figured that out but now my statement isn't completing.
REf:
Weekly Sum Dynamic Pivot MYSQL
Here is a fiddle with the data.
http://sqlfiddle.com/#!9/a3610
$period = 'YEARWEEK';
$sql = "
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN (".$period."(date)) = ',
(".$period."(date)),
' THEN AMOUNT else 0 END) AS `',
(".$period."(date)),
'`'
)
ORDER BY date ASC ) AS `pivot_columns`
FROM record_offering
WHERE date BETWEEN ? AND ?
ORDER BY date ASC
";
$stmt = $pdo->prepare($sql);
$date_from = '2017-01-01';
$date_to = '2017-10-01';
$stmt->execute([$date_from, $date_to]);
$row = $stmt->fetch();
$stmt->closeCursor();
$pivot_columns = $row['pivot_columns'];
$sql = "
SELECT title AS `Service`, {$pivot_columns}
from record_offering t1
join setting_service ON t1.service_id = setting_service.id
WHERE t1.date BETWEEN ? AND ?
GROUP BY title asc WITH ROLLUP
";
$stmt = $pdo->prepare($sql);
$stmt->execute([$date_from, $date_to]);
$results = $stmt->fetchAll();
$stmt->closeCursor();
As you can see the last statement is incomplete:
SELECT title AS `Service`, SUM(CASE WHEN (YEARWEEK(date)) = 201635 THEN AMOUNT else 0 END) AS `201635`,
SUM(CASE WHEN (YEARWEEK(date)) = 201703 THEN AMOUNT else 0 END) AS `201703`,
SUM(CASE WHEN (YEARWEEK(date)) = 201709 THEN AMOUNT else 0 END) AS `201709`,
SUM(CASE WHEN (YEARWEEK(date)) = 201713 THEN AMOUNT else 0 END) AS `201713`,
SUM(CASE WHEN (YEARWEEK(date)) = 201715 THEN AMOUNT else 0 END) AS `201715`,
SUM(CASE WHEN (YEARWEEK(date)) = 201717 THEN AMOUNT else 0 END) AS `201717`,
SUM(CASE WHEN (YEARWEEK(date)) = 201718 THEN AMOUNT else 0 END) AS `201718`,
SUM(CASE WHEN (YEARWEEK(date)) = 201722 THEN AMOUNT else 0 END) AS `201722`,
SUM(CASE WHEN (YEARWEEK(date)) = 201723 THEN AMOUNT else 0 END) AS `201723`,
SUM(CASE WHEN (YEARWEEK(date)) = 201725 THEN AMOUNT else 0 END) AS `201725`,
SUM(CASE WHEN (YEARWEEK(date)) = 201726 THEN AMOUNT else 0 END) AS `201726`,
SUM(CASE WHEN (YEARWEEK(date)) = 201735 THEN AMOUNT else 0 END) AS `201735`,
SUM(CASE WHEN (YEARWEEK(date)) = 201736 THEN AMOUNT else 0 END) AS `201736`,
SUM(CASE WHEN (YEARWEEK(date)) = 201
from record_offering t1
join setting_service ON t1.service_id = setting_service.id
WHERE t1.`date` BETWEEN ? AND ?
GROUP BY title asc WITH ROLLUP
I have tried escaping the query in various ways but either the query completes and my data is duplicated or it doesn't compile at all.

GROUP_CONCAT has a limit off 1024 bytes.
Use
SET SESSION group_concat_max_len = ##max_allowed_packet
Before the GROUP_CONCAT query.

To my way of thinking, this (or something very like it) is ALL the sql you need for this problem. Everything else can, and should, be handled in the presentation layer.
SELECT YEARWEEK(x.date) yw
, x.title
, COALESCE(SUM(y.amount),0) total
FROM setting_service x
LEFT
JOIN record_offering y
ON y.service_id = x.id
GROUP
BY yw
, x.id;

Related

Issue with Dynamic Pivot Table

Having some issues with my query in that it generates the required data between the dates specified (on live it is 2 date fields (date_from and date_to), but the order of data is in the wrong order. e.g it will appear
JAN 2017 | MARCH 2017 | APRIL 2017 | FEB 2017
And the most baffling issue is that it generates data outside what you search. e.g if you search from 1-1-2017 to date, it adds December to the data but there is no data in the database containing december records.
The code used to generate the dynamic pivot chart is:
$sql = "
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN EXTRACT(".$period." FROM redeem_pledge) = ',
EXTRACT(".$period." FROM redeem_pledge),
' THEN AMOUNT else 0 END) AS `',
EXTRACT(".$period." FROM redeem_pledge),
'`'
)
) AS `pivot_columns`
FROM record_pledge
WHERE redeem_pledge BETWEEN ? AND ?
ORDER BY redeem_pledge asc
";
$stmt = $pdo->prepare($sql);
$date_from = $this->input->get('date_from') ? $this->input->get('date_from') : '2017-05-01';
$date_to = $this->input->get('date_to') ? $this->input->get('date_to') : date('Y-m-d');
$stmt->execute([$date_from, $date_to]);
$row = $stmt->fetch();
$stmt->closeCursor();
$pivot_columns = $row['pivot_columns'];
$sql = "
SELECT title AS `Pledge Purpose`, {$pivot_columns}
FROM record_pledge t1
JOIN setting_pledge_purpose ON t1.purpose_pledge = setting_pledge_purpose.id
WHERE t1.redeem_pledge BETWEEN ? AND ?
GROUP BY title asc WITH ROLLUP
";
$stmt = $pdo->prepare($sql);
$stmt->execute([$date_from, $date_to]);
$results = $stmt->fetchAll();
$stmt->closeCursor();
Image of Added data and unorganised data:
UPDATE echo of SQL- SEARCH FROM 01-01-2017(JAN) - 15-10-2017(OCT) :
SELECT title AS `Pledge Purpose`,
SUM(CASE WHEN EXTRACT(YEAR_MONTH FROM redeem_pledge) = 201701 THEN AMOUNT else 0 END) AS `201701`,
SUM(CASE WHEN EXTRACT(YEAR_MONTH FROM redeem_pledge) = 201702 THEN AMOUNT else 0 END) AS `201702`,
SUM(CASE WHEN EXTRACT(YEAR_MONTH FROM redeem_pledge) = 201704 THEN AMOUNT else 0 END) AS `201704`,
SUM(CASE WHEN EXTRACT(YEAR_MONTH FROM redeem_pledge) = 201705 THEN AMOUNT else 0 END) AS `201705`,
SUM(CASE WHEN EXTRACT(YEAR_MONTH FROM redeem_pledge) = 201706 THEN AMOUNT else 0 END) AS `201706`,
SUM(CASE WHEN EXTRACT(YEAR_MONTH FROM redeem_pledge) = 201708 THEN AMOUNT else 0 END) AS `201708`,
SUM(CASE WHEN EXTRACT(YEAR_MONTH FROM redeem_pledge) = 201709 THEN AMOUNT else 0 END) AS `201709`
FROM record_pledge t1 JOIN setting_pledge_purpose ON t1.purpose_pledge = setting_pledge_purpose.id
WHERE t1.redeem_pledge BETWEEN ? AND ? GROUP BY title asc WITH ROLLUP
You need to put the ORDER BY option in the GROUP_CONCAT function, not the SELECT query.
Also, you need to put quotes around the value returned by EXTRACT($period FROM redeem_pledge). If $period is a unit that returns multiple parts of the date, like YEAR_MONTH, it needs to be compared as a string because it will be 2017-01; if you don't quote it, it will be treated as a numeric subtraction.
$sql = "
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN EXTRACT(".$period." FROM redeem_pledge) = \"',
EXTRACT(".$period." FROM redeem_pledge),
'\" THEN AMOUNT else 0 END) AS `',
EXTRACT(".$period." FROM redeem_pledge),
'`'
)
ORDER BY redeem_pledge ASC) AS `pivot_columns`
FROM record_pledge
WHERE redeem_pledge BETWEEN ? AND ?
";

How to use union query in laravel

I have a query in core php but i want to convert into Laravel, i am trying to do but that is giving the error. I don't know the exact method which i can use for this
here is the core query
select mon,year,
combo,
registered,
forwardedbycmo,
clarification,
noaction,
disposed,mon_srno,undertaken
from monthly_activities
union
select extract('month' from actiondate) as mon,extract('year' from actiondate) as year,
extract('year' from actiondate)|| '-' ||to_char(to_timestamp (extract('month' from actiondate)::text, 'MM'), 'TMMon') as combo,
sum(case when actioncode = '00' then 1 else 0 end) as registered,
sum(case when actioncode = '4T' and fromorg='CMOFF' then 1 else 0 end) as forwardedbycmo,
sum(case when actioncode = '4D' and fromorg='CMOFF' then 1 else 0 end) as clarification,
sum(case when actioncode = '10' then 1 else 0 end) as noaction,
sum(case when actioncode = '50' then 1 else 0 end) as disposed,null as mon_srno,
sum(case when actioncode = '40' and fromorg='CMOFF' then 1 else 0 end) as undertaken
from actionhistory where extract(month from actiondate)=extract(month from current_date)
and extract(year from actiondate)=extract(year from current_date)
group by mon,year order by year,mon;
and laravel query is
$first = MonthlyActivity::select('mon','year','combo','registered','forwardedbycmo',
'clarification',
'noaction',
'disposed','mon_srno','undertaken')->get();
$second = MonthlyActivity::select(extract('month' from actiondate) as mon,extract('year' from actiondate) as year,
extract('year' from actiondate)|| '-' ||to_char(to_timestamp (extract('month' from actiondate)::text, 'MM'), 'TMMon') as combo,
sum(case when actioncode = '00' then 1 else 0 end) as registered,
sum(case when actioncode = '4T' and fromorg='CMOFF' then 1 else 0 end) as forwardedbycmo,
sum(case when actioncode = '4D' and fromorg='CMOFF' then 1 else 0 end) as clarification,
sum(case when actioncode = '10' then 1 else 0 end) as noaction,
sum(case when actioncode = '50' then 1 else 0 end) as disposed,null as mon_srno,
sum(case when actioncode = '40' and fromorg='CMOFF' then 1 else 0 end) as undertaken
from actionhistory where extract(month from actiondate)=extract(month from current_date)
and extract(year from actiondate)=extract(year from current_date))->groupBy('mon','year')->orderBy('year','mon')->get();
$result = $first->union($second);
//$users = DB::table('users')->whereNull('last_name')->union($first)->get();
return view('show',['monthly' => $result]);
As per your question title, here is union reference for Laravel.
But seeing your problem as you have used native sql functions such as extract(), I will advise you to run query as raw query.
For heads up:
You use: $var = DB::connection()->getPdo()->quote($var);
to escape any variable you are using in query.
Prepare your raw query and put it in a variable like:
$query = 'SELECT ....';
Then you run it like:
$result = DB::select($query,[]);
Dont forget to include DB class at top of controller:
use Illuminate\Support\Facades\DB;
I am sorry I cannot give a lot of time to actually make a laravel query for you, so I suggested here which might help you go on.Good Luck!

join a query multiply the results

I have this query and works great. it gives to me some kpis about time, related to an equipment x, and this work selecting the shifts to evaluate. I need to join the information comming from a second query, that works whit the same id of shifts to work, but when i Add the parameters of this query to the first one, the results becomes incremented, for instance, the sum of the firs values gives 24 (24 hrs) but when i relate this query with the second one, te results increments its values and i cant realize whats wrong.
The first query:
SELECT *
,(Efectivo+Demora+Reserva)/(Efectivo+Demora+Reserva+Mantencion+Averia+Accidente)*100 as [Disp_Fisica]
,IsNULL((Efectivo+Demora+Reserva)/NULLIF((Efectivo+Demora+Reserva+Mantencion+Averia),0),0)*100 as [Disp_Mecanica]
,IsNULL((Efectivo+Demora)/NULLIF((Efectivo+Demora+Reserva),0),0)*100 as [Uso_Disponib]
,IsNULL(Efectivo/NULLIF((Efectivo+Demora),0),0)*100 as [Efic_Operacional]
FROM (
SELECT hist_eqmtlist.unit,
hist_statusevents.eqmt,
sum(case when hist_statusevents.category =1 then (hist_statusevents.duration/3600) else 0 end) as [Efectivo],
sum(case when hist_statusevents.category =2 then (hist_statusevents.duration/3600) else 0 end) as [Demora],
sum(case when hist_statusevents.category =3 then (hist_statusevents.duration/3600) else 0 end) as [Reserva],
sum(case when hist_statusevents.category =4 then (hist_statusevents.duration/3600) else 0 end) as [Mantencion],
sum(case when hist_statusevents.category =5 then (hist_statusevents.duration/3600) else 0 end) as [Averia],
sum(case when hist_statusevents.category =6 then (hist_statusevents.duration/3600) else 0 end) as [Accidente]
FROM hist_eqmtlist ,hist_statusevents, hist_exproot
WHERE hist_eqmtlist.unit = 'Pala'
and hist_exproot.shiftindex between '29976' and '29977'
and hist_statusevents.shiftindex = hist_eqmtlist.shiftindex
and hist_statusevents.eqmt = hist_eqmtlist.eqmtid
and hist_exproot.shiftindex = hist_statusevents.shiftindex
GROUP BY hist_statusevents.eqmt, hist_eqmtlist.unit
) A
order by A.eqmt
the result given by this query:
unit eqmt Efectivo Demora Reserva Mantencion Averia Accidente Disp_Fisica Disp_Mecanica Uso_Disponib Efic_Operacional
Camion CAM336 16,8 2,6 4,35 0 0 0,2 99,0 100 81,8 86,4
the second query gives to me the tons by equipment
SELECT hist_exproot.shiftdate , hist_dumps.shiftindex, hist_dumps.truck, sum(hist_dumps.dumptons) AS 'Toneladas'
FROM Powerview.dbo.hist_dumps hist_dumps, Powerview.dbo.hist_exproot hist_exproot
WHERE hist_dumps.shiftindex = hist_exproot.shiftindex
GROUP BY hist_exproot.shiftdate, hist_dumps.truck, hist_dumps.shiftindex
ORDER BY shiftdate
The result given by this query is the amount of tons by shift by equip
shiftdate shiftindex truck Toneladas
2011-01-01 29950 CAM336 8964,00054931641
I Need to join both querys but when i do it, it multiply the values, how shoukd i build the relations?
Im trying adding this on the select part, under the sums:
and on where:
hist_exproot.shiftindex
and the query becomes this:
SELECT *
,(Efectivo+Demora+Reserva)/(Efectivo+Demora+Reserva+Mantencion+Averia+Accidente)*100 as [Disp_Fisica]
,IsNULL((Efectivo+Demora+Reserva)/NULLIF((Efectivo+Demora+Reserva+Mantencion+Averia),0),0)*100 as [Disp_Mecanica]
,IsNULL((Efectivo+Demora)/NULLIF((Efectivo+Demora+Reserva),0),0)*100 as [Uso_Disponib]
,IsNULL(Efectivo/NULLIF((Efectivo+Demora),0),0)*100 as [Efic_Operacional]
FROM (
SELECT hist_eqmtlist.unit,
hist_statusevents.eqmt,
sum(case when hist_statusevents.category =1 then (hist_statusevents.duration/3600) else 0 end) as [Efectivo],
sum(case when hist_statusevents.category =2 then (hist_statusevents.duration/3600) else 0 end) as [Demora],
sum(case when hist_statusevents.category =3 then (hist_statusevents.duration/3600) else 0 end) as [Reserva],
sum(case when hist_statusevents.category =4 then (hist_statusevents.duration/3600) else 0 end) as [Mantencion],
sum(case when hist_statusevents.category =5 then (hist_statusevents.duration/3600) else 0 end) as [Averia],
sum(case when hist_statusevents.category =6 then (hist_statusevents.duration/3600) else 0 end) as [Accidente],
sum (hist_dumps.dumptons) as [Toneladas]
FROM hist_eqmtlist ,hist_statusevents, hist_exproot,hist_dumps
WHERE hist_eqmtlist.unit = 'Pala'
and hist_exproot.shiftindex between '29976' and '29977'
and hist_statusevents.shiftindex = hist_eqmtlist.shiftindex
and hist_statusevents.eqmt = hist_eqmtlist.eqmtid
and hist_exproot.shiftindex = hist_statusevents.shiftindex
and hist_exproot.shiftindex = hist_dumps.shiftindex
GROUP BY hist_statusevents.eqmt, hist_eqmtlist.unit
) A
order by A.unit
and when i need to keep the resutls for the tons of and the indicators given by the querys when the arent together, the resutls turns into this when the sum of "efectivo, demora reserva and accidente" shouldn't result more than 24 (24 hrs)
unit eqmt Efectivo Demora Reserva Accidente Toneladas
Camion CAM336 9678 1498 2156 109 2342022

Closing percent MATH in MYSQL

I want to do math in a query, and was wondering if its better to do it in PHP or MYSQL.
Also, if I choose MYSQL can anyone help me with the query.
So far I have
SELECT COUNT(*) as total, booker, appdate,
SUM(CASE WHEN status='DNS' THEN 1 ELSE 0 END) book,
SUM(CASE WHEN status!='DNS' THEN 1 ELSE 0 END) tot
FROM appts WHERE WEEK(app_date)= WEEK(CURDATE()) GROUP BY booker
I want one more stat from this query.
I want to do book / (book+tot)
But obviously only if book!=0 or tot!=0, since obviously I don't want to divide anything by zero.
Is there a way to do this in a MYSQL query??
I want my output to be.....
book | 14
tot | 25
hold | 35%
Id also like to ORDER BY the hold percent from highest to lowest. Is this possible????
You can achieve what you ask for using a subquery, like this:
SELECT *, IF(book + tot, 100*book/(book + tot), NULL) AS hold
FROM (
SELECT COUNT(*) as total, booker, appdate,
SUM(status='DNS') book, SUM(status!='DNS') tot
FROM appts WHERE WEEK(app_date)= WEEK(CURDATE()) GROUP BY booker
) AS subquery
ORDER BY hold DESC
Note that in several places I'm using the fact that MySQL uses numbers for logical values. So you can sum up conditions without CASE, and you can write a formula for IF without <> 0 check.
Naive method:
SELECT
COUNT(*) as total,
SUM(CASE WHEN status='DNS' THEN 1 ELSE 0 END) book,
SUM(CASE WHEN status!='DNS' THEN 1 ELSE 0 END) tot,
IF( ( SUM(CASE WHEN status!='DNS' THEN 1 ELSE 0 END) +
SUM(CASE WHEN status='DNS' THEN 1 ELSE 0 END) ) = 0,
0,
SUM(CASE WHEN status='DNS' THEN 1 ELSE 0 END) /
( SUM(CASE WHEN status!='DNS' THEN 1 ELSE 0 END) +
SUM(CASE WHEN status='DNS' THEN 1 ELSE 0 END) )
) AS hold
FROM appts
WHERE WEEK(app_date) = WEEK(CURDATE()) GROUP BY booker
ORDER BY hold;
Or in order not to repeat your aliases, use a subquery:
SELECT *, IF (book + tot = 0, 0, book / (book + tot) * 100)
FROM (
SELECT
COUNT(*) as total,
SUM(CASE WHEN status='DNS' THEN 1 ELSE 0 END) book,
SUM(CASE WHEN status!='DNS' THEN 1 ELSE 0 END) tot,
FROM appts
WHERE WEEK(app_date) = WEEK(CURDATE()) GROUP BY booker
) AS subq
ORDER BY hold;
Or, more cleverly :) (book + tot = total)
SELECT *, IF (total = 0, 0, book / total * 100)
FROM (
SELECT
COUNT(*) as total,
SUM(CASE WHEN status='DNS' THEN 1 ELSE 0 END) book,
SUM(CASE WHEN status!='DNS' THEN 1 ELSE 0 END) tot,
FROM appts
WHERE WEEK(app_date) = WEEK(CURDATE()) GROUP BY booker
) AS subq
ORDER BY hold;
And, just for fun, the hackishly compactest form:
SELECT *, COALESCE(book / total * 100, 0) AS hold -- a division by 0 returns NULL
FROM (
SELECT
COUNT(*) total,
SUM(status='DNS') book, -- boolean "true" is internally integer "1"
SUM(status!='DNS') tot,
FROM appts
WHERE WEEK(app_date) = WEEK(CURDATE()) GROUP BY booker
) AS subq
ORDER BY hold;

select/sum of a column with different conditions in 1 query

$pos = select * from score_history where content_id = 6 && val = 1
$neg = select * from score_history where content_id = 6 && val = -1
i want to get the pos and neg scores in one query
but i dont want to use join
so perhaps some sort of IF/case statement ?
i've this but as you can guess it fails
SELECT count(*) as total ,
CASE
WHEN `val` = 1 THEN count(*) as `pos`
WHEN `val` = -1 THEN count(*) as `neg`
END
FROM score_history WHERE `content_id` = '46083' ";
is there any way to do this without using join or sub query ?
You can make use of the flexibility of MySQL to handle booleans and integers:
SELECT count(*) total, sum(val = 1) pos, sum(val = -1) neg
FROM score_history
WHERE content_id = '46083';
Whenever the condition is true it is a 1. Otherwise a 0. No CASE needed nor GROUP BY.
Close! A CASE statement doesn't return multiple columns, so you'll need 2 CASE statements and to wrap them in a SUM():
SELECT count(*) as total
,SUM(CASE WHEN `val` = 1 THEN 1 ELSE 0 END) as `pos`
,SUM(CASE WHEN `val` = -1 THEN 1 ELSE 0 END) as `neg`
FROM score_history WHERE `content_id` = '46083' ;
SELECT
SUM(CASE WHEN `val` = 1 THEN 1 ELSE O END) AS pos_count,
SUM(CASE WHEN `val` = -1 THEN 1 ELSE O END) AS neg_count
FROM score_history WHERE `content_id` = '46083';
Try this. Sorry I can't test, no database on this laptop.
select
count(*) as total,
sum(case val when 1 then 1 else 0 end) as pos,
sum(case val when -1 then 1 else 0 end) as neg
from score_history
where content_id = 6
Not sure if this is the best answer (and you would certainly want an index on your val column assuming there are many rows in the table) but this should certainly work - also assuming you only have 1 and -1 as values:
SELECT count(*), val from score_history where content_id = 6 group by val;
You were close; try the SUM function:
SELECT count(*) as total
, sum(CASE WHEN `val` = 1 THEN 1 ELSE 0 END) as `pos`
, sum(CASE WHEN `val` = -1 THEN 1 ELSE 0 END) as `neg`
FROM score_history
WHERE `content_id` = '46083';
select count(*)
from score_history
where content_id = 6 &&
(val = -1 or val=1)
group by val
I think this statement should work but I have tested on DBMS.
SELECT count(*) as total ,
count(case when val = 1 then 1 else null end) as pos,
count(case when val = -1 then 1 else null end) as neg
FROM score_history
WHERE `content_id` = '46083';
See SQLFIDDLE
Okay, a lot of these answers are close, but whenever you use an aggregate function you should use a group by.
SELECT count(*) as total
, (CASE WHEN `val` >= 0 THEN 'positive' ELSE 'negative' END) as interpreted_value
END
FROM score_history
WHERE `content_id` = '46083'
GROUP BY (CASE WHEN `val` >= 0 THEN 'positive' ELSE 'negative' END);
If you want to read up on how to use group by and aggregate functions here: https://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html

Categories