How can I make this SQL query more elegant? - mysql

This is my MySQL query for getting count values...
SELECT 1 AS id,
'2016' AS Year,
MAX( IF( Month = '01', Count, 0 ) ) AS 'VAL01',
MAX( IF( Month = '02', Count, 0 ) ) AS 'VAL02',
MAX( IF( Month = '03', Count, 0 ) ) AS 'VAL03',
MAX( IF( Month = '04', Count, 0 ) ) AS 'VAL04',
MAX( IF( Month = '05', Count, 0 ) ) AS 'VAL05',
MAX( IF( Month = '06', Count, 0 ) ) AS 'VAL06',
MAX( IF( Month = '07', Count, 0 ) ) AS 'VAL07',
MAX( IF( Month = '08', Count, 0 ) ) AS 'VAL08',
MAX( IF( Month = '09', Count, 0 ) ) AS 'VAL09',
MAX( IF( Month = '10', Count, 0 ) ) AS 'VAL10',
MAX( IF( Month = '11', Count, 0 ) ) AS 'VAL11',
MAX( IF( Month = '12', Count, 0 ) ) AS 'VAL12',
SUM( Count ) AS Total
FROM ( SELECT DATE_FORMAT( created_at, '%m' ) AS Month,
COUNT( 1 ) AS Count
FROM reservations
WHERE DATE_FORMAT( created_at, '%Y' ) = '2016'
GROUP BY DATE_FORMAT( created_at, '%m' )
) AS T1
UNION
SELECT 2 AS id,
'2017' AS Year,
MAX( IF( Month = '01', Count, 0 ) ) AS 'VAL01',
MAX( IF( Month = '02', Count, 0 ) ) AS 'VAL02',
MAX( IF( Month = '03', Count, 0 ) ) AS 'VAL03',
MAX( IF( Month = '04', Count, 0 ) ) AS 'VAL04',
MAX( IF( Month = '05', Count, 0 ) ) AS 'VAL05',
MAX( IF( Month = '06', Count, 0 ) ) AS 'VAL06',
MAX( IF( Month = '07', Count, 0 ) ) AS 'VAL07',
MAX( IF( Month = '08', Count, 0 ) ) AS 'VAL08',
MAX( IF( Month = '09', Count, 0 ) ) AS 'VAL09',
MAX( IF( Month = '10', Count, 0 ) ) AS 'VAL10',
MAX( IF( Month = '11', Count, 0 ) ) AS 'VAL11',
MAX( IF( Month = '12', Count, 0 ) ) AS 'VAL12',
SUM( Count ) AS Total
FROM ( SELECT DATE_FORMAT( created_at, '%m' ) AS Month,
COUNT( 1 ) AS Count
FROM reservations
WHERE DATE_FORMAT( created_at, '%Y' ) = '2017'
GROUP BY DATE_FORMAT( created_at, '%m' )
) AS T2
It returns (for example)...
+----+------+-------+-------+-------+-------+-------+-------+-----+-------+-------+
| id | Year | VAL01 | VAL02 | VAL03 | VAL04 | VAL05 | VAL06 | ... | VAL12 | Total |
+----+------+-------+-------+-------+-------+-------+-------+-----+-------+-------+
| 1 | 2016 | 0 | 0 | 150 | 190 | 200 | 220 | ... | 160 | 1242 |
+----+------+-------+-------+-------+-------+-------+-------+-----+-------+-------+
| 2 | 2017 | 300 | 300 | 600 | 600 | 700 | 0 | ... | 0 | 2500 |
+----+------+-------+-------+-------+-------+-------+-------+-----+-------+-------+
My query has many problems. It can't use the year 2018 and it should use UNION again and again.
How can I make my SQL query more beautiful?

Try this
select
date_format(created_at,'%Y') as Year,
sum(case when date_format(created_at,'%m')='01' then 1 else 0 end) as val01,
sum(case when date_format(created_at,'%m')='02' then 1 else 0 end) as val02,
sum(case when date_format(created_at,'%m')='03' then 1 else 0 end) as val03,
sum(case when date_format(created_at,'%m')='04' then 1 else 0 end) as val04,
sum(case when date_format(created_at,'%m')='05' then 1 else 0 end) as val05,
sum(case when date_format(created_at,'%m')='06' then 1 else 0 end) as val06,
sum(case when date_format(created_at,'%m')='07' then 1 else 0 end) as val07,
sum(case when date_format(created_at,'%m')='08' then 1 else 0 end) as val08,
sum(case when date_format(created_at,'%m')='09' then 1 else 0 end) as val09,
sum(case when date_format(created_at,'%m')='10' then 1 else 0 end) as val10,
sum(case when date_format(created_at,'%m')='11' then 1 else 0 end) as val11,
sum(case when date_format(created_at,'%m')='12' then 1 else 0 end) as val12,
count(*) as total
from reservations
group by date_format(created_at,'%Y')

You can try with below query
SELECT YEAR,SUM(VAL01) VAL02,SUM(VAL02) VAL02,SUM(VAL03) VAL3
FROM
(
SELECT date_format(created_at,'%Y') YEAR,
CASE WHEN date_format(created_at,'%m')=1 THEN 1 ELSE 0 END AS VAL01,
CASE WHEN date_format(created_at,'%m')=2 THEN 1 ELSE 0 END AS VAL02,
CASE WHEN date_format(created_at,'%m')=3 THEN 1 ELSE 0 END AS VAL03
FROM reservations
)t
GROUP BY YEAR
Hope this will helps.

If this had been Oracle, you could have used Function based index on column Created_at and using the same function (EXTRACT in this case) in your query:
CREATE INDEX IDX1 ON RESERVATIONS (EXTRACT(YEAR FROM CREATED_AT))
Since, this is MySQL, try using this as alternative. Make sure after creating the index, it is getting used by verifying the Query plan.

You could try this query
SELECT id,
Year,
SUM(CASE WHEN Month='01' THEN MonthCount ELSE 0 END) AS 'VAL01',
SUM(CASE WHEN Month='02' THEN MonthCount ELSE 0 END) AS 'VAL02',
SUM(CASE WHEN Month='03' THEN MonthCount ELSE 0 END) AS 'VAL03',
SUM(CASE WHEN Month='04' THEN MonthCount ELSE 0 END) AS 'VAL04',
SUM(CASE WHEN Month='05' THEN MonthCount ELSE 0 END) AS 'VAL05',
SUM(CASE WHEN Month='06' THEN MonthCount ELSE 0 END) AS 'VAL06',
SUM(CASE WHEN Month='07' THEN MonthCount ELSE 0 END) AS 'VAL07',
SUM(CASE WHEN Month='08' THEN MonthCount ELSE 0 END) AS 'VAL08',
SUM(CASE WHEN Month='09' THEN MonthCount ELSE 0 END) AS 'VAL09',
SUM(CASE WHEN Month='10' THEN MonthCount ELSE 0 END) AS 'VAL10',
SUM(CASE WHEN Month='11' THEN MonthCount ELSE 0 END) AS 'VAL11',
SUM(CASE WHEN Month='12' THEN MonthCount ELSE 0 END) AS 'VAL12',
SUM(MonthCount) AS Total
FROM (
SELECT
CASE
WHEN Date_format(created_at, '%Y') = '2016' THEN 1
WHEN Date_format(created_at, '%Y') = '2017' THEN 2
WHEN Date_format(created_at, '%Y') = '2018' THEN 3
END AS id,
Date_format(created_at, '%Y') AS Year,
Date_format(created_at, '%m') AS Month,
COUNT(1) AS MonthCount
FROM reservations
WHERE Date_format(created_at, '%Y') IN ('2016', '2017', '2018')
GROUP BY Date_format(created_at, '%Y-%m')) AS ByMonth
GROUP BY id, Year

Related

inner join with pivot condition sql

I currently have the following sql query:
SELECT
name,
SUM( IF( MONTH(date) = 1, amount, 0) ) AS jan,
SUM( IF( MONTH(date) = 2, amount, 0) ) AS feb,
SUM( IF( MONTH(date) = 3, amount, 0) ) AS mar,
SUM( IF( MONTH(date) = 4, amount, 0) ) AS apr,
SUM( IF( MONTH(date) = 5, amount, 0) ) AS mei,
SUM( IF( MONTH(date) = 6, amount, 0) ) AS jun,
SUM( IF( MONTH(date) = 7, amount, 0) ) AS jul,
SUM( IF( MONTH(date) = 8, amount, 0) ) AS agu,
SUM( IF( MONTH(date) = 9, amount, 0) ) AS sep,
SUM( IF( MONTH(date) = 10, amount, 0) ) AS okt,
SUM( IF( MONTH(date) = 11, amount, 0) ) AS nov,
SUM( IF( MONTH(date) = 12, amount, 0) ) AS des,
SUM( amount ) AS total
FROM iuran_detail
WHERE (
date BETWEEN '$date_first' AND '$date_last'
) GROUP BY name;
I want modification with 2 tables sql, user table (along with group) and amount.
I want to combine the inner join or another way, to get the following result (e.g by group A):
thanks
Note: If joining tables it is best practice to prefix EVERY column reference with either the source table name OR by an alias given to the source table (I use the latter). "date" is a terrible column name as it is also used by SQL itself and can cause confusion, so below I have used the MySQL backticks to reference that column, in SQL-Server you could use [date] instead.
SELECT
u.name,
SUM( IF( MONTH(`date`) = 1, d.amount, 0) ) AS jan,
SUM( IF( MONTH(`date`) = 2, d.amount, 0) ) AS feb,
SUM( IF( MONTH(`date`) = 3, d.amount, 0) ) AS mar,
SUM( IF( MONTH(`date`) = 4, d.amount, 0) ) AS apr,
SUM( IF( MONTH(`date`) = 5, d.amount, 0) ) AS mei,
SUM( IF( MONTH(`date`) = 6, d.amount, 0) ) AS jun,
SUM( IF( MONTH(`date`) = 7, d.amount, 0) ) AS jul,
SUM( IF( MONTH(`date`) = 8, d.amount, 0) ) AS agu,
SUM( IF( MONTH(`date`) = 9, d.amount, 0) ) AS sep,
SUM( IF( MONTH(`date`) = 10, d.amount, 0) ) AS okt,
SUM( IF( MONTH(`date`) = 11, d.amount, 0) ) AS nov,
SUM( IF( MONTH(`date`) = 12, d.amount, 0) ) AS des,
SUM( d.amount ) AS total
FROM table_trx d
INNER JOIN table_user u on d.user_id = u.id
WHERE (
d.`date` BETWEEN '$`date`_first' AND '$`date`_last'
) GROUP BY u.name;
Personally I do not like using IF() as an alternative to the more standard CASE expression as it is more broadly supported in SQL databases.
SELECT
u.name,
SUM( case when MONTH(`date`) = 1 then d.amount else 0 end ) AS jan,
SUM( case when MONTH(`date`) = 2 then d.amount else 0 end ) AS feb,
SUM( case when MONTH(`date`) = 3 then d.amount else 0 end ) AS mar,
SUM( case when MONTH(`date`) = 4 then d.amount else 0 end ) AS apr,
SUM( case when MONTH(`date`) = 5 then d.amount else 0 end ) AS mei,
SUM( case when MONTH(`date`) = 6 then d.amount else 0 end ) AS jun,
SUM( case when MONTH(`date`) = 7 then d.amount else 0 end ) AS jul,
SUM( case when MONTH(`date`) = 8 then d.amount else 0 end ) AS agu,
SUM( case when MONTH(`date`) = 9 then d.amount else 0 end ) AS sep,
SUM( case when MONTH(`date`) = 10 then d.amount else 0 end ) AS okt,
SUM( case when MONTH(`date`) = 11 then d.amount else 0 end ) AS nov,
SUM( case when MONTH(`date`) = 12 then d.amount else 0 end ) AS des,
SUM( d.amount ) AS total
FROM table_trx d
INNER JOIN table_user u on d.user_id = u.id
WHERE (
d.`date` BETWEEN '$`date`_first' AND '$`date`_last'
) GROUP BY u.name;

Combining multiple queries of single view in one query

I am struggling with multiple queries to combine in one query, all these are retrieving from single view for a same date range for all queries i.e.
SELECT business_id_fk,business_name,
IFNULL(count(orderid_customer),0) AS total,
IFNULL(sum(CASE WHEN order_status IN ('CRD') THEN 1 ELSE 0 END), 0) AS crude,
IFNULL(sum(CASE WHEN order_status IN ('UNA') THEN 1 ELSE 0 END), 0) AS unassigned,
IFNULL(sum(CASE WHEN order_status IN ('DEL' , 'MCP' , 'CMP') THEN 1 ELSE 0 END), 0) AS delivered,
IFNULL(sum(CASE WHEN order_status IN ('CNL') THEN 1 ELSE 0 END), 0) AS cancelled,
IFNULL(sum(CASE WHEN order_status IN ( 'CAP' , 'CAD' , 'RSP' , 'RSD') THEN 1 ELSE 0 END), 0) AS postponed
FROM view_orders where order_datetime between '2017-10-01 00:00:00' and '2017-10-05 23:59:59' and parent_id IS NULL
AND business_id_fk is not null GROUP BY business_id_fk ORDER BY total DESC;
The above query is to get business wise count
SELECT business_id_fk,business_name,
IFNULL(count(orderid_customer),0) AS total,
IFNULL(sum(CASE WHEN order_status IN ('CRD') THEN 1 ELSE 0 END), 0) AS crude,
IFNULL(sum(CASE WHEN order_status IN ('UNA') THEN 1 ELSE 0 END), 0) AS unassigned,
IFNULL(sum(CASE WHEN order_status IN ('DEL' , 'MCP' , 'CMP') THEN 1 ELSE 0 END), 0) AS delivered,
IFNULL(sum(CASE WHEN order_status IN ('CNL') THEN 1 ELSE 0 END), 0) AS cancelled,
IFNULL(sum(CASE WHEN order_status IN ( 'CAP' , 'CAD' , 'RSP' , 'RSD') THEN 1 ELSE 0 END), 0) AS postponed,
IFNULL(SUM(total), 0) AS 'Estimated_Revenue',
IFNULL(SUM(CASE WHEN order_status IN ('DEL' , 'MCP', 'CMP') THEN total ELSE 0 END), 0) AS 'Generated_Revenue'
FROM view_orders
WHERE order_datetime between '2017-10-01 00:00:00' and '2017-10-05 23:59:59'
AND parent_id IS NULL
AND business_id_fk is null
ORDER BY total DESC;
This is to get the count of business_id_fk having null values
select ((SELECT ifnull(count(*),0) as 'total' FROM view_orders where (sla_del_datetime > delivery_datetime) and order_datetime between '2017-10-01 00:00:00' and '2017-10-05 23:59:59' and parent_id is null group by business_id_fk) / (SELECT ifnull(count(*),0) as 'total' FROM view_orders where order_status in('DEL','MCP','CMP') order_datetime between '2017-10-01 00:00:00' and '2017-10-05 23:59:59' and and parent_id is null group by business_id_fk)*100) as 'sla_adherence';
This query is to get sla_adherence of each business wise and non business
select ((SELECT ifnull(count(*),0) as 'total' FROM view_orders where order_status in('DEL','MCP','CMP') order_datetime between '2017-10-01 00:00:00' and '2017-10-05 23:59:59' and and parent_id is null group by business_id_fk) / (SELECT ifnull(count(*),0) as 'total' FROM view_orders where order_datetime between '2017-10-01 00:00:00' and '2017-10-05 23:59:59' and parent_id is null group by business_id_fk)*100) as 'strike_rate';
This query is to get strike_rate of each business wise and non business
I want the result like
business_id_fk||business_name||total||crude||unassigned||delivered||cancelled||postponed||Estimated_Revenue||Generated_Revenue||sla_adherence||strike_rate
1||abc||10||1||..|..|..|..|..|..|..|..|..|x.xx|x.xx
.
.
.
null(non business)||null||..|..|. . . . . . |x.xx|x.xx
each business wise rows for all the above fields and for non business.
Thank you in advance.
One first query is multi-row, the second seems to be a single value. So if those assumptions are right you could try a cross join:
SELECT
business_id_fk
, business_name
, IFNULL(count(orderid_customer),0) AS total
, IFNULL(sum(CASE WHEN order_status IN ('CRD') THEN 1 ELSE 0 END), 0) AS crude
, IFNULL(sum(CASE WHEN order_status IN ('UNA') THEN 1 ELSE 0 END), 0) AS unassigned
, IFNULL(sum(CASE WHEN order_status IN ('DEL' , 'MCP' , 'CMP') THEN 1 ELSE 0 END), 0) AS delivered
, IFNULL(sum(CASE WHEN order_status IN ('CNL') THEN 1 ELSE 0 END), 0) AS cancelled
, IFNULL(sum(CASE WHEN order_status IN ( 'CAP' , 'CAD' , 'RSP' , 'RSD') THEN 1 ELSE 0 END), 0) AS postponed
, cj.sla_adherence
FROM view_orders
CROSS JOIN (
SELECT (
(SELECT ifnull(count(*),0) FROM view_orders where (sla_del_datetime > delivery_datetime) and parent_id is null )
/
(SELECT ifnull(count(*),0) FROM view_orders where order_status in('DEL','MCP','CMP') and parent_id is null )*100.0
) as 'sla_adherence'
) cj
WHERE order_datetime between '2017-08-01 00:00:00' and '2017-10-01 23:59:59'
GROUP BY business_id_fk, business_name, cj.sla_adherence
ORDER BY total DESC
;

Multiple Select with same table in MySQL doesn't work

i'm having a problem with my database's multiple selection. I need to do a select that returns a kind of table with some processed data, and it need to be ordered by day of month. To do this, i'm using multiple select's issue of mysql. This is my code:
SELECT
(SELECT COUNT( * ) FROM `table` WHERE `type`=1 AS 'Total',
(SELECT COUNT( * ) FROM `table` WHERE `type`=1 and `status` = 0 and `status_cancel` = 0 AS 'Open',
(SELECT COUNT( * ) FROM `table` WHERE `type`=1 and `status_cancel` = 1 AS 'Cancel',
(SELECT COUNT( * ) FROM `table` WHERE `type`=1 and `date_finish` is not null and `status_cancel` = 0 AS 'Finish',
(SELECT COUNT( * ) FROM `table` WHERE `type`=1 and `result` >= 0 and `date_finish` is not null and `status_cancel` = 0 AS 'Win',
(SELECT COUNT( * ) FROM `table` WHERE `type`=1 and `result` < 0 and `date_finish` is not null and `status_cancel` = 0 AS 'Loss'
Now it's returning the total of all rows in my table, but i can't do this return it grouped by day, help me, please!
The result must be like this:
Result that i need
I think you are saying how can I group by date when my queries are all amounting to sub queries. I would change it to be case statements for that. Try something like this
SELECT DATE(date_finish) as `date`, count(*) as 'Total',
SUM(CASE WHEN `status` = 0 AND `status_cancel` = 0 THEN 1 ELSE 0 END) as 'Open',
SUM(CASE WHEN `status_cancel` = 1 THEN 1 ELSE 0 END) as 'Cancel',
SUM(CASE WHEN `date_finish` is not null and `status_cancel` = 0 THEN 1 ELSE 0 END) as 'Finish',
SUM(CASE WHEN `result` >= 0 AND `date_finish` is not null AND `status_cancel` = 0 THEN 1 ELSE 0 END) as 'Win',
SUM(CASE WHEN `result` < 0 AND `date_finish` is not null AND `status_cancel` = 0 THEN 1 ELSE 0 END) as 'Loss'
from `table`
WHERE `type` = 1
group by DATE(date_finish)
TEST: http://rextester.com/HCRIU11065

Case in where condition to calculate individual totals

select
Count(*)
from gl
join TRVMAINDATA msit
on gl.TRANSACTIONID = msit.CHARGETRANSACTIONID
where gl.CREATEDDATETIME >= DATEADD(MONTH,-2,getdate()) AND
datediff(DAY,gl.BUSINESSPROCESSDATE,gl.CREATEDDATETIME)>=4 AND
gl.MARKETCODE In('535','532','056','050','039','036','034','033','030','029','027','023','022','021','018','015','012','011','010','009','007','006','005','002','001' ) and DATEADD(MONTH,DATEDIFF(MONTH,0,gl.CREATEDDATETIME),0) = '2/1/2017 12:00:00 AM'
The above code gives the total for market code mentioned.
But i also want the market code for other code which doesn't belog here. for example
select
Count(*)
from gl
join TRVMAINDATA msit
on gl.TRANSACTIONID = msit.CHARGETRANSACTIONID
where gl.CREATEDDATETIME >= DATEADD(MONTH,-6,getdate()) AND
datediff(DAY,gl.BUSINESSPROCESSDATE,gl.CREATEDDATETIME)>=0
and
DATEADD(MONTH,DATEDIFF(MONTH,0,gl.CREATEDDATETIME),0) = '2/1/2017 12:00:00 AM'
AND
(case
when gl.MARKETCODE In('535','532','056','050','039','036','034','033','030','029','027','023','022','021','018','015','012','011','010','009','007','006','005','002','001') then 'Proprietary'
when gl.MARKETCODE='037' then 'US'
else 'partner'
end )
Is it possible to calculate totals of other market codes in one sql query or do i have to calculate individually?
I think you would like to do count with case when in select statement like this:
SELECT COUNT(CASE
WHEN gl.marketcode IN ( '535', '532', '056', '050',
'039', '036', '034', '033',
'030', '029', '027', '023',
'022', '021', '018', '015',
'012', '011', '010', '009',
'007', '006', '005', '002', '001' ) THEN 1
ELSE NULL
END) AS `Proprietary`,
COUNT(CASE
WHEN gl.marketcode = '037' THEN 1
ELSE NULL
END) AS `US`,
COUNT(CASE
WHEN gl.marketcode NOT IN ( '535', '532', '056', '050',
'039', '036', '034', '033',
'030', '029', '027', '023',
'022', '021', '018', '015',
'012', '011', '010', '009',
'007', '006', '005', '002',
'001', '037' ) THEN 1
ELSE NULL
END) AS `partner`
FROM gl
JOIN trvmaindata msit
ON gl.transactionid = msit.chargetransactionid
WHERE gl.createddatetime >= DATEADD(month, -2, GETDATE())
AND DATEDIFF(day, gl.businessprocessdate, gl.createddatetime) >= 4
AND DATEADD(month, DATEDIFF(month, 0, gl.createddatetime), 0) = '2/1/2017 12:00:00 AM'

SQL subquery in query

I have a table with payment:
worker_id, amount, payed, date
Table workers:
id, name, lname
I need to write SQL that will give me name, lname and sum for jun, july, august, september.
Name | Lname | Sum_JUN | Sum_JULY | Sum_AUG | Sum_SEP
I'm trying with subqueries but can't do it. Can you help me?
I created SQL (example). I will replace dates in PHP.
select w.name, w.lname,
sum(case when p.payed_date between '2014-06-01' and '2014-06-31' then p.amount else 0 end) `sum_june`,
sum(case when p.payed_date between '2014-07-01' and '2014-07-31' then p.amount else 0 end) `sum_july`,
sum(case when p.payed_date between '2014-08-01' and '2014-08-31' then p.amount else 0 end) `sum_august`,
sum(case when p.payed_date between '2014-09-01' and '2014-09-31' then p.amount else 0 end) `sum_september`,
sum(case when p.payed_date between '2014-10-01' and '2014-10-31' then p.amount else 0 end) `sum_november`
from worker w
left join worker_sum p on(w.id = p.worker_id)
group by w.id
You can use conditional aggregation for your desired sum,But this will give you the sum for months from all years exist in your table
select w.*,
sum(case when month(p.date) = 6 then p.amount else 0 end) `sum_june`,
sum(case when month(p.date) = 7 then p.amount else 0 end) `sum_july`,
sum(case when month(p.date) = 8 then p.amount else 0 end) `sum_august`,
sum(case when month(p.date) = 9 then p.amount else 0 end) `sum_september`
from workers w
left join payment p on(w.id = p.worker_id)
group by w.id