How To Write A Query With A CTE And A Left Join - mysql

I am trying to have a calendar table with my CTE and set it up so that my dates in my query display like this
Jan 18Jan 19Feb 18Feb 19
Now this is my DDL and this is the query I attempted, but in MySql Workbench I'm getting the error that there is an error in my sql somewhere.
This is the exact error:
Query Error: Error: ER_PARSE_ERROR: 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 'RECURSIVE cte_months_to_pull AS ( SELECT DATE_FORMAT(#start_date, '%Y-%m-01'' at line 1
Can someone assist?
If it's easier this is a SQL Fiddle of everything http://sqlfiddle.com/#!9/300f9d/1
CREATE TABLE PrevYear (
`EmployeeNumber` char(8) NOT NULL,
`SaleAmount` int DEFAULT NULL,
`SaleDate` date NOT NULL,
`EmployeeName` char(17) NOT NULL
);
CREATE TABLE CurrentYear (
`EmployeeNumber` char(8) NOT NULL,
`SaleAmount` int DEFAULT NULL,
`SaleDate` date NOT NULL,
`EmployeeName` char(17) NOT NULL
);
INSERT INTO CurrentYear
VALUES ('ea12', '100', '2019-01-10', 'Maggie Samuels');
INSERT INTO CurrentYear
VALUES ('ea12', '199', '2019-01-13', 'Sam Stoner');
INSERT INTO CurrentYear
VALUES ('ea12', '100', '2019-03-01', 'Jake Jolel');
INSERT INTO CurrentYear
VALUES ('ls22', '100', '2019-05-01', 'Maggie Samuels');
INSERT INTO PrevYear
VALUES ('ea12', '100', '2018-01-10', 'Maggie Samuels');
INSERT INTO PrevYear
VALUES ('ea12', '199', '2018-01-13', 'Sam Stoner');
INSERT INTO PrevYear
VALUES ('ea12', '100', '2018-03-01', 'Sam Stoner');
INSERT INTO PrevYear
VALUES ('ls22', '100', '2018-05-01', 'Maggie Samuels');
And this is the query I try:
SET #start_date = '20190102';
SET #number_of_months = 12;
WITH RECURSIVE
cte_months_to_pull AS (
SELECT DATE_FORMAT(#start_date, '%Y-%m-01')
- INTERVAL #number_of_months MONTH AS month_to_pull
UNION ALL
SELECT month_to_pull + INTERVAL 1 MONTH
FROM cte_months_to_pull
WHERE month_to_pull < #start_date + INTERVAL #number_of_months - 2 MONTH
)
SELECT Date_format(saledate, '%m-%Y') AS Month,
employeename,
Sum(saleamount) AS IA
FROM currentyear
WHERE employeename = 'Maggie Samuels'
GROUP BY Date_format(saledate, '%m-%Y'), employeename
UNION ALL
SELECT Date_format(saledate, '%m-%Y') AS Month,
employeename,
Sum(saleamount) AS IA
FROM prevyear
WHERE employeename = 'Maggie Samuels'
GROUP BY Date_format(saledate, '%m-%Y'), employeename
LEFT JOIN cte_months_to_pull (
Select DATE_Format(month_to_pull, '%b %y')
FROM cte_months_to_pull
) AS YRS ON month_to_pull = saledate
ORDER BY MONTH(month_to_pull), YEAR(month_to_pull)

As I can see you are using MySQL version older than 8.0 which doesn't support RECURSIVE CTEs. I have tried your query with some minor updates on 8.0 and it worked fine -
WITH RECURSIVE
cte_months_to_pull AS (
SELECT DATE_FORMAT(#start_date, '%Y-%m-01')
- INTERVAL #number_of_months MONTH AS month_to_pull
UNION ALL
SELECT month_to_pull + INTERVAL 1 MONTH
FROM cte_months_to_pull
WHERE month_to_pull < #start_date + INTERVAL #number_of_months - 2 MONTH
)
SELECT YRS.months_to_pull
,T.employeename
,COALESCE(T.IA, 0) IA
FROM (SELECT DATE_Format(month_to_pull, '%b-%Y') months_to_pull
FROM cte_months_to_pull
ORDER BY months_to_pull
) AS YRS
LEFT JOIN (SELECT Date_format(saledate, '%b-%Y') AS `Month`
,employeename
,Sum(saleamount) AS IA
FROM CurrentYear
WHERE employeename = 'Maggie Samuels'
GROUP BY Date_format(saledate, '%b-%Y'), employeename
UNION ALL
SELECT Date_format(saledate, '%b-%Y')
,employeename
,Sum(saleamount)
FROM PrevYear
WHERE employeename = 'Maggie Samuels'
GROUP BY Date_format(saledate, '%b-%Y'), employeename) T
ON YRS.months_to_pull = T.`Month`
ORDER BY month(STR_TO_DATE(CONCAT('01-',months_to_pull), '%d-%b-%Y'))
,YEAR(STR_TO_DATE(CONCAT('01-',months_to_pull), '%d-%b-%Y'))
Here is the Fiddle
Since there is no expected output, I have only tried till running the query.

Related

How to get difference or delta of counts entries of each days with window functions?

I have a table with few fields like id, country, ip, created_at. Then I am trying to get the deltas between total entry of one day and total entry of the next day.
CREATE TABLE session (
id int NOT NULL AUTO_INCREMENT,
country varchar(50) NOT NULL,
ip varchar(255),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);
INSERT INTO `session` (`id`, `country`, `ip`, `created_at`) VALUES
('1', 'IN', '10.100.102.11', '2021-04-05 20:26:02'),
('2', 'IN', '10.100.102.11', '2021-04-05 19:26:02'),
('3', 'US', '10.120.102.11', '2021-04-17 10:26:02'),
('4', 'US', '10.100.112.11', '2021-04-16 12:26:02'),
('5', 'AU', '10.100.102.122', '2021-04-12 19:36:02'),
('6', 'AU', '10.100.102.122', '2021-04-12 18:20:02'),
('7', 'AU', '10.100.102.122', '2021-04-12 23:26:02'),
('8', 'US', '10.100.102.2', '2021-04-16 21:33:01'),
('9', 'AU', '10.100.102.122', '2021-04-18 20:46:02'),
('10', 'AU', '10.100.102.111', '2021-04-04 13:19:12'),
('11', 'US', '10.100.112.11', '2021-04-16 12:26:02'),
('12', 'IN', '10.100.102.11', '2021-04-05 15:26:02'),
('13', 'IN', '10.100.102.11', '2021-04-05 19:26:02');
Now I have written this query to get the delta
SELECT T1.date1 as date, IFNULL(T1.cnt1-T2.cnt2, T1.cnt1) as delta from (
select TA.dateA as date1, MAX(TA.countA) as cnt1 from (
select DATE(created_at) AS dateA, COUNT(*) AS countA
FROM session
GROUP BY DATE(created_at)
UNION
select DISTINCT DATE(DATE(created_at)+1) AS dateA, 0 AS countA
FROM session
) as TA
group by TA.dateA
) as T1
LEFT OUTER JOIN (
select DATE(DATE(created_at)+1) AS date2,
COUNT(*) AS cnt2
FROM session
GROUP BY DATE(created_at)
) as T2
ON T1.date1=T2.date2
ORDER BY date;
http://sqlfiddle.com/#!9/4f5fd26/60
Then I am getting the results as
date delta
2021-04-04 1
2021-04-05 3
2021-04-06 -4
2021-04-12 3
2021-04-13 -3
2021-04-16 3
2021-04-17 -2
2021-04-18 0
2021-04-19 -1
Now, is there any place of improvements/optimizes on it with/or window functions? (I am zero with SQL, still playing around).
Try a shorter version
with grp as (
SELECT t.dateA, SUM(t.cnt) AS countA
FROM session,
LATERAL (
select DATE(created_at) AS dateA, 1 as cnt
union all
select DATE(DATE(created_at)+1), 0 as cnt
) t
GROUP BY dateA
)
select t1.dateA as date, IFNULL(t1.countA-t2.countA, t1.countA) as delta
from grp t1
left join grp t2 on DATE(t2.dateA + 1) = t1.dateA
order by t1.dateA
db<>fiddle

Custom output from a mysql query

Consider the following schema:
CREATE TABLE `Result` (
`startDate` date NOT NULL,
`description` varchar(45) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
`value` decimal(15,4) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO `Result`
(`startDate`,
`description`,
`value`)
VALUES
('2020-09-01' ,'Allowance' ,4000),
('2020-09-01' ,'Salary' ,1500),
('2020-10-01' ,'Allowance' ,2000),
('2020-10-01' ,'Salary' ,3000),
('2020-10-01' ,'Deduction' ,-200);
Given a date,the result should show as the total for a description & startdate and the difference between the date selected and previous date(month). So if October was the month selected the result of the query should show as,
description SeptemberTotal OctoberTotal Variance
Allowance 4000 2000 -2000
Salary 1500 3000 1500
Deduction 0 -200 -200
My attempt using a union & a pivot,
SELECT #selectDate:='2020-10-01'; -- set desired date
SELECT
t.month,
t.description,
Gross,
from (
SELECT
DATE_FORMAT(pi.startDate, '%b/%y') AS 'Month',
SUM(pi.value) AS gross,
description
FROM
Result pi
WHERE
pi.startDate = DATE_SUB(#selectDate, INTERVAL 1 MONTH) -- select previous month
GROUP BY description
UNION SELECT
DATE_FORMAT(pi.startDate, '%b/%y') AS 'Month',
SUM(pi.value) AS gross,
description
FROM
Result pi
WHERE
pi.startDate = #selectDate
GROUP BY description) t
GROUP BY t.Month,t.description
;
which gives the result as,
Month description Gross
Sep/20 Allowance 4000
Sep/20 Salary 1500
Oct/20 Allowance 2000
Oct/20 Salary 3000
Oct/20 Deduction -200
which is not exactly what the requirement is. I have tried a pivot query as well, that too is not showing the output as required.
db-fiddle
SET #m1 := '2020-09-01';
SET #m2 := '2020-10-01';
SELECT Result.Description,
COALESCE(SUM(CASE WHEN Result.startDate = #m1 THEN value END), 0) Total1,
COALESCE(SUM(CASE WHEN Result.startDate = #m2 THEN value END), 0) Total2,
COALESCE(SUM(CASE WHEN Result.startDate = #m2 THEN value END), 0) -
COALESCE(SUM(CASE WHEN Result.startDate = #m1 THEN value END), 0) Variance
FROM ( SELECT #m1 startDate UNION ALL SELECT #m2 ) baseDates
LEFT JOIN Result USING (startDate)
GROUP BY Result.Description
fiddle
select
description,
sum(if(startDate between '2020-11-01 00:00:00' and '2020-11-31 23:59:59' ,value,0)) 1st,
sum(if(startDate between '2020-12-01 00:00:00' and '2020-12-31 23:59:59',value,0)) 2nd,
sum(if(startDate between '2020-11-01 00:00:00' and '2020-11-31 23:59:59' ,value,0))-sum(if(created_at between '2020-12-01 00:00:00' and '2020-12-31 23:59:59',value,0)) Variance
from
Result
where startDate between '2020-11-01 00:00:00' and '2020-12-31 23:59:59' group by description

sql select from table with date range condition

I have this table
CREATE TABLE `tarif` (
`tarif_id` int(11) NOT NULL AUTO_INCREMENT,
`start_tarif` date NOT NULL,
`end_tarif` date NOT NULL,
`day_tarif` varchar(50) NOT NULL,
PRIMARY KEY (`tarif_id`)
);
INSERT INTO `tarif` VALUES (1, '2019-02-01', '2019-02-10', '10'),
(2, '2019-02-11', '2019-02-20', '20'),
(3, '2019-02-21', '2019-02-28', '10'),
(4, '2019-03-01', '2019-02-10', '15');
How can I get day_tarif between 2019-02-05 and 2019-02-15 and calculate the sum = day_tarif * number of date betwwen 2019-02-05 and 2019-02-15
You can use the following solution using DATEDIFF:
SELECT SUM((DATEDIFF(LEAST(`end_tarif`, '2019-02-15'), GREATEST(`start_tarif`, '2019-02-05')) + 1) * `day_tarif`) AS sumCustom
FROM `tarif`
WHERE `end_tarif` >= '2019-02-05' AND `start_tarif` <= '2019-02-15'
demo on dbfiddle.uk
You can use the following solution using SUM and COUNT:
SELECT SUM(`day_tarif`) * COUNT(`tarif_id`)
FROM `tarif`
WHERE `end_tarif` >= '2019-02-05' AND `start_tarif` <= '2019-02-15'
select SUM(day_tarif) * COUNT(tarif_id)
from tarif
where end_date >= '2019-02-05' AND start_date <= '2019-02-15'
This question has been asked several times, or something like it. Be sure to search StackOverflow before asking a duplicate: SQL query to select dates between two dates
You can try following code:
WITH cte AS(
SELECT *
FROM tablename
WHERE end_tarif >= '2019-02-05' AND start_tarif <= '2019-02-15'
)
SELECT day_tarif, day_tarif * COUNT(tarif_id) AS 'SUM'
FROM cte
group by day_tarif;
I think this might be what you are after...
Declare #tarif as table (
tarif_id int NOT NULL ,
start_tarif date NOT NULL,
end_tarif date NOT NULL,
day_tarif varchar(50) NOT NULL
);
INSERT INTO #tarif VALUES (1, '2019-02-01', '2019-02-10', '10'),
(2, '2019-02-11', '2019-02-20', '20'),
(3, '2019-02-21', '2019-02-28', '10'),
(4, '2019-03-01', '2019-02-10', '15');
-- Declare parameters
Declare #paramstart date, #paramend date
Set #paramstart='2019-02-05'
Set #paramend='2019-02-15'
-- Set up loop
Declare #mincount int, #maxcount int, #myval int, #curstart date, #curend date,#curtarif int, #mytarif int
Set #mincount=(Select MIN(tarif_id) from #tarif where end_tarif >= '2019-02-05' AND start_tarif <= '2019-02-15')
Set #maxcount=(Select Max(tarif_id) from #tarif where end_tarif >= '2019-02-05' AND start_tarif <= '2019-02-15')
Set #mytarif=0
-- Do loop
WHile #mincount<=#maxcount
BEGIN
Set #curstart=(Select start_tarif from #tarif where tarif_id=#mincount)
Set #curend=(Select end_tarif from #tarif where tarif_id=#mincount)
Set #curtarif=(Select cast(day_tarif as int) from #tarif where tarif_id=#mincount)
IF #paramstart between #curstart and #curend
BEGIN
Set #mytarif=#mytarif+((DATEDIFF(day,#paramstart,#curend)+1) * #curtarif)
END
IF #paramend between #curstart and #curend
BEGIN
Set #mytarif=#mytarif+((DATEDIFF(day,#curstart,#paramend)+1) * #curtarif)
END
IF #paramstart not between #curstart and #curend and #paramend not between #curstart and #curend
BEGIN
Set #mytarif=#mytarif+((DATEDIFF(day,#curstart,#curend)+1) * #curtarif)
END
Set #mincount=#mincount+1
END
Select #mytarif as tarif
Try this!
select day_tarif , sum(day_tarif * count (*)) as sum
where start_tarif between '2019/02/05' and '2019/02/15' and end_tarif between '2019/02/05' and '2019/02/15'
group by day_tarif;

MySQL query help require

I am using MySQL database. I have employee leave table which having information about employee leave.
Please find table details:
CREATE TABLE IF NOT EXISTS `APPLY_LEAVE` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`EMP_ID` varchar(100) NOT NULL,
`TYPE_OF_LEAVE` varchar(100) NOT NULL,
`DAYS` varchar(100) NOT NULL,
`REASON` varchar(200) NOT NULL,
`START_DATE` date NOT NULL,
`END_DATE` date NOT NULL,
`STATUS` tinyint(2) NOT NULL,
`CREATED_ON` date NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;
--
-- Dumping data for table `APPLY_LEAVE`
--
INSERT INTO `APPLY_LEAVE` (`ID`, `EMP_ID`, `TYPE_OF_LEAVE`, `DAYS`, `REASON`, `START_DATE`, `END_DATE`, `STATUS`, `CREATED_ON`) VALUES
(1, 'EMP001', 'SL', '2', 'Sick Leave', '2018-11-30', '2018-12-01', 1,'2018-11-06'),
(2, 'EMP002', 'EL', '1', 'Personal', '2018-12-13', '2018-12-13', 1,'2018-11-09'),
(3, 'EMP003', 'CL', '2', 'Casual Leave due to Birthday', '2018-08-31', '2018-09-01', 1,'2018-08-20'),
(4, 'EMP001', 'CL', '3', 'Casual Leave', '2018-12-04', '2018-12-06', 1,'2018-11-27'),
(5, 'EMP002', 'SL', '4', 'Sick Leave', '2018-09-10', '2018-09-13', 1,'2018-10-04'),
(6, 'EMP003', 'SL', '3', 'Sick Leave', '2018-10-30', '2018-11-01', 1,'2018-11-25');
Require Output:
I want to generate Report/excel to receive information as month wise employee leave data based on leave type i.e (Month wise, Leave type data)
Format should be below:
Requirement: I want MySQL query to fetch attached result month wise, Leave type data(SL/CL/EL) which took by Employee.
Query tries:
SELECT EMP_ID,
SUM(CASE WHEN TYPE_OF_LEAVE = 'EL' AND MONTH( START_DATE ) =11 THEN DAYS ELSE 0 END ) AS EL_NOV,
SUM(CASE WHEN TYPE_OF_LEAVE = 'CL' AND MONTH( START_DATE ) =11 THEN DAYS ELSE 0 END ) AS CL_NOV,
SUM(CASE WHEN TYPE_OF_LEAVE = 'SL' AND MONTH( START_DATE ) =11 THEN DAYS ELSE 0 END ) AS SL_NOV,
SUM(CASE WHEN TYPE_OF_LEAVE = 'LOP' AND MONTH( START_DATE ) =11 THEN DAYS ELSE 0 END ) AS LOP_NOV,
SUM(CASE WHEN TYPE_OF_LEAVE = 'EL' AND MONTH( START_DATE ) =12 THEN DAYS ELSE 0 END ) AS EL_DEC,
SUM(CASE WHEN TYPE_OF_LEAVE = 'CL' AND MONTH( START_DATE ) =12 THEN DAYS ELSE 0 END ) AS CL_DEC,
SUM(CASE WHEN TYPE_OF_LEAVE = 'SL' AND MONTH( START_DATE ) =12 THEN DAYS ELSE 0 END ) AS SL_DEC,
SUM(CASE WHEN TYPE_OF_LEAVE = 'LOP' AND MONTH( START_DATE ) =12 THEN DAYS ELSE 0 END ) AS LOP_DEC
FROM APPLY_LEAVE
GROUP BY EMP_ID
Facing Issue:
I.e One employee look leave on Friday and Saturday (i.e EMP001 took SL on 2018-11-30 to 2018-12-01) (Friday is month of last date and Saturday is first date of month and I am inserting single record into table. When employee applied leave from application. Here result should be
EMP001 - SL
November - 1 leave
December - 1 leave
How can I write this MySQL query?
Dear Dipti Kindly find below query for required result.
SELECT
*
FROM
(
SELECT
EMP_ID,
START_DATE as date_day,
TYPE_OF_LEAVE,
SUM(
if(
MONTH(START_DATE) <> MONTH(END_DATE),
(
day(
last_day(START_DATE)
)+ 1 - day(START_DATE)
),
days
)
) as DAYS
FROM
APPLY_LEAVE
GROUP BY
MONTH(START_DATE),
MONTH(END_DATE),
EMP_ID
UNION ALL
SELECT
EMP_ID,
END_DATE as date_day,
TYPE_OF_LEAVE,
SUM(
if(
MONTH(START_DATE) <> MONTH(END_DATE),
DAY(END_DATE),
0
)
) as DAYS
FROM
APPLY_LEAVE
GROUP BY
MONTH(START_DATE),
MONTH(END_DATE),
EMP_ID
) as a
WHERE
a.DAYS > 0;

How to sort the the sql table considering the month an year column

I have a year column that contains things string value like 2015,2014, 2013, 2012, etc. A month column that displays string value like January, Feburary,...December. I need to run a select that sort the table considering the both Year and month column. Can anyone provide some input? Is the any system procedures to do it easily.
I want the output based on the Year and Month descending Order.
You can do something like:
order by cast(year as int) desc
,case when month = 'January' then 1
when month = 'February' then 2
....
when month = 'December' then 12
end desc
Try this
ORDER BY CAST(month+year AS DATE) DESC
Try like this
DECLARE #table TABLE
(
year VARCHAR(10),
month VARCHAR(20)
)
INSERT INTO #table
VALUES ('2015',
'January'),
('2015',
'March'),
('2015',
'February'),
('2015',
'April'),
('2015',
'May'),
('2014',
'January'),
('2014',
'February'),
('2014',
'March'),
('2014',
'May'),
('2014',
'April'),
('2016',
'January'),
('2016',
'February'),
('2016',
'March'),
('2016',
'May'),
('2016',
'April')
SELECT *
FROM #table
ORDER BY CONVERT(INT, year) desc,
Datepart(mm, Cast(month + ' 1900' AS DATETIME)) desc
You need to translate the month name into number for using a translation reference table and store values
jan-1,feb-2,march-3 like wise
Then try
SELECT * FROM tblName ORDER BY yearField DESC, monthField DESC