Where do I begin Looping statements in MySQL Workbench - mysql

I need to have this query run 12 times (previous 12 months) and append the results to a table. I am not very good with looping, looking for input. I am just not sure where to put my counter variables or any other looping statements. I think I may need two variables to loop because of the Previous Month First Day and Last day variables.
SET #PM_FD = last_day(curdate() - interval 2 month) + interval 1 day;
SET #PM_LD = last_day(curdate() - interval 1 month);
insert into sandbox.metrics_history
SELECT
'CHI' as Company
,count(*) as Result
,'SSRM10' as Metric_ID
,'PONoReqLine' as Metric_Name
, MONTHNAME(#PM_FD) as Month, year(#PM_FD) as Year
FROM
poline pol
INNER JOIN
purchorder po ON pol.company = po.company
AND pol.po_number = po.po_number
AND pol.po_release = po.po_release
AND pol.po_code = po.po_code
LEFT JOIN
polinesrc src ON pol.company = src.company
AND pol.po_number = src.po_number
AND pol.po_release = src.po_release
AND pol.line_nbr = src.line_nbr
AND pol.po_code = src.po_code
LEFT JOIN
buyer byr ON pol.buyer_code = byr.buyer_code
WHERE
pol.buyer_code != 'POC'
AND src.company IS NULL
AND po.po_date >= #PM_FD
AND po.po_date <= #PM_LD
ORDER BY pol.company , pol.po_number , pol.line_nbr

Related

Where do I put the sum function to sum sick leave days in this query?

The query shown below gives me the number of leave days excluding Saturday and Sunday. My problem is I don't know where to put the sum() function to sum all leave days.
SELECT DATEDIFF(eleave.resume_date , eleave.leave_date) as total_days , (
ABS(DATEDIFF(eleave.leave_date, eleave.resume_date)) -
ABS(DATEDIFF(ADDDATE(eleave.leave_date, INTERVAL 1 -
DAYOFWEEK(eleave.leave_date) DAY) , ADDDATE(eleave.resume_date, INTERVAL
1 - DAYOFWEEK(eleave.resume_date) DAY))) / 7 * 2 -
(DAYOFWEEK(IF(eleave.resume_date < eleave.leave_date , leave.resume_date,
eleave.leave_date)) = 1) - (DAYOFWEEK(IF(eleave.resume_date >
eleave.leave_date, eleave.resume_date, eleave.leave_date)) = 7)) as
leavedays , employee.emp_lname , type.typ_name as leavetype ,
education.qualification , education.program FROM employee INNER JOIN eleave on employee.emp_num = eleave.emp_num LEFT JOIN type ON type.typ_id =
eleave.typ_id left join education ON employee.emp_num = education.emp_num
WHERE employee.emp_num = 4774 AND type.typ_name = 'Sick leave'
You should do the sum per employee to get total leaves for each employee
it will look like this
SELECT employee.emp_num,
SUM (ABS(DATEDIFF(eleave.leave_date, eleave.resume_date)) - ABS(DATEDIFF(ADDDATE(eleave.leave_date, INTERVAL 1 - DAYOFWEEK(eleave.leave_date) DAY) , ADDDATE(eleave.resume_date, INTERVAL 1 - DAYOFWEEK(eleave.resume_date) DAY))) / 7 * 2 - (DAYOFWEEK(IF(eleave.resume_date < eleave.leave_date , leave.resume_date, eleave.leave_date)) = 1) - (DAYOFWEEK(IF(eleave.resume_date > eleave.leave_date, eleave.resume_date, eleave.leave_date)) = 7)) as leavedays
FROM employee INNER JOIN eleave on employee.emp_num = eleave.emp_num
LEFT JOIN type ON type.typ_id = eleave.typ_id left join education
ON employee.emp_num = education.emp_num
WHERE employee.emp_num = 4774 AND type.typ_name = 'Sick leave'
GROUP BY employee.emp_num

MySql - Loop through dates in a month for each row

I have 2 tables. One is Jobs (master) table & other is Allocations (transactions) table. For each Job, I need to print the number of allocations done on each day in a month.
I need to print the number of allocations in the following format.
I tried this using while loop in a stored procedure. But it did not help.
BEGIN
SET #start_day = DATE('2018-11-01');
SET #end_day = DATE_ADD(#start_day, INTERVAL 30 DAY);
SELECT
job_id into #job_id
FROM
jobs
WHERE
job_post_date BETWEEN #start_day AND #end_day;
WHILE(#start_day < #end_day) DO
SELECT COUNT(allocation_id) FROM allocations WHERE allocations_job_id =
#job_id AND allocations_assigned_date = #start_day;
SET #start_day = DATE_ADD(#start_day, INTERVAL 1 DAY);
END WHILE;
END
If you can create dynamic sql by looping for all the number of days for the month in question, that should solve your problem.
Dynamic sql should look like
select j.job_name,
sum(case when a.allocations_assigned_date = '2019-01-01'
then 1 else 0 end) as "1-JAN-2019",
sum(case when a.allocations_assigned_date = '2019-01-02'
then 1 else 0 end) as "2-JAN-2019",
sum(case when a.allocations_assigned_date = '2019-01-31'
then 1 else 0 end) as "31-JAN-2019"
from jobs j inner join allocations a
on j.job_id = a.allocations_job_id
group by j.job_name

Combining two mysql query returns ok instead of rows

I have a query in which I return some information regarding an invoice, I take that invoice and compare it to another table "payment" to see if that invoice (fullamount -fullpaid) exists in the other table and if it does some function should not run in my backend code.
SELECT a.status, a.rf_reference, a.payor_orig_id , a.gross_amount + a.type_related_amount as fullamount,
a.gross_paid + a.type_related_paid as fullpaid
FROM invoice a
where a.type = 3 and
a.status in (10, 30) and
a.UPDATE_DT is null
having fullamount > fullpaid
order by a.ORIG_ID;
The above query returns
status| rf_reference | payor_orig_id | fullamount | fullpaid
30 RF123456 212 1000 200
So now I take the above information and pass it onto another query to see if a row field matches.
I pass it on like this
select *
from payment
where
payor_orig_id = 212 and
rf_reference = RF123456 and
payment_amount = (1000-200) and
status = 10 and
INSERT_DT BETWEEN DATE_SUB(NOW(), INTERVAL 185 DAY) AND NOW() and
UPDATE_DT IS NULL;
So now the above code will return a row by which basically I do not run my backend function.
Since this are two separate query I would like to combine them to one where I make sure that I add a having statement and check that ONLY rows are returned where there is no match between the invoice and payment table.
SELECT a.status, a.rf_reference, a.payor_orig_id , a.gross_amount + a.type_related_amount as fullamount,
a.gross_paid + a.type_related_paid as fullpaid,
(select b.payment_amount
from payment b
where
b.payor_orig_id = a.payor_orig_id and
b.rf_reference = a.rf_reference and
b.status = 10 and
b.INSERT_DT BETWEEN DATE_SUB(NOW(), INTERVAL 185 DAY) AND NOW() and
b.UPDATE_DT IS NULL) as payment_amount
FROM invoice a
where a.type = 3 and
a.status in (10, 30) and
a.UPDATE_DT is null
having fullamount > fullpaid and
(fullamount - fullpaid ) <> payment_amount
order by a.ORIG_ID;
The above query returns "OK" which is odd since I am not sure how to debug it.
Try seeing if the other table exists or not using NOT EXIST
SELECT a.* ,
a.gross_amount + a.type_related_amount as fullamount,
a.gross_paid + a.type_related_paid as fullpaid
FROM invoice a
where a.type = 3 and
a.status in (10, 30) and
a.UPDATE_DT is null and
NOT EXISTS ( select *
from payment
where
payor_orig_id = a.payor_orig_id and
rf_reference = a.rf_reference and
payment_amount = ((a.gross_amount + a.type_related_amount) - (a.gross_paid + a.type_related_paid)) and
status = 10 and
INSERT_DT BETWEEN DATE_SUB(NOW(), INTERVAL 185 DAY) AND NOW() and
UPDATE_DT IS NULL )
having fullamount > fullpaid
order by a.ORIG_ID;

How to query last 2 business days only

I'm running a query that pulls the correct information I'm looking for, but I need it to pull the last 2 business days rather than the last 2 days. This comes into play when it's Monday and my results show information for Monday and Sunday rather than Monday and Friday. How can I change my query to pull in business days only?
USE [LetterGeneration]
SELECT g.LetterGenerationPrintJobId
,CAST(t.[TemplateKey] AS VarChar) AS LetterCode
,convert(char(12),r.CreatedDate,101) AS CreatedDate
,s.LetterGenerationStatusId AS Status
,s.StatusKey AS StatusDesc
,count(g.LetterGenerationId) as LetterCount
,c.BankingDateYorN
FROM [LetterGenerationTemplateRequest] AS r
INNER JOIN [LetterGenerationTemplate] AS t
ON t.[LetterGenerationTemplateId] = r.LetterGenerationTemplateId
INNER JOIN LetterGeneration g
ON g.LetterGenerationTemplateRequestId = r.LetterGenerationTemplateRequestId
INNER JOIN LetterGenerationStatus s
ON g.LetterGenerationStatusId = s.LetterGenerationStatusId
INNER JOIN Enterprise..Calendar C
ON c.BeginDate = g.LetterDate
WHERE ((DATEDIFF(d, r.CreatedDate, GETDATE()) = 0) OR (DATEDIFF(d, r.CreatedDate, GETDATE()) = 1))
--BankingDateYorN = 1
--AND RelativeTimeValue_BusinessDates =-1
AND t.[TemplateKey] NOT LIKE '%PLTV1%'
AND s.LetterGenerationStatusId NOT LIKE '4'
AND s.LetterGenerationStatusId NOT LIKE '16'
AND s.LetterGenerationStatusId NOT LIKE '19'
AND s.LetterGenerationStatusId NOT LIKE '20'
AND s.LetterGenerationStatusId NOT LIKE '38'
GROUP BY r.[LetterGenerationTemplateRequestId]
,r.LetterGenerationTemplateId
,g.Lettergenerationprintjobid
,t.[TemplateKey]
,r.[Loan_no]
,r.CreatedDate
,r.[CreatedBy]
,s.LetterGenerationStatusId
,s.StatusKey
,c.BankingDateYorN
ORDER BY r.CreatedDate DESC
UPDATE: I've recently discovered how to join a calendar table to my current query. The calendar query has a column called BusinessDayYorN with 1's for a business day and 0's for weekends and holidays. I've also updated the old query to now include the join.
select *
from LetterGenerationTemplateRequest
where createddate >= (
getdate() -
case datename(dw,getdate())
when 'Tuesday' then 5
when 'Monday' then 4
else 3
end
)
--and datename(dw,createdDate) not in ('Saturday','Sunday',datename(dw,getdate()))
and datename(dw,createdDate) not in ('Saturday','Sunday')
;
Assuming that you always want to include the last two non-weekend days you can try this:
; with aux as (
select diff = case
when datename(weekday, getdate()) in ('Tuesday', 'Wednesday ', 'Thursday', 'Friday') then 1
else
case datename(weekday, getdate())
when 'Saturday' then 2
when 'Sunday' then 3
when 'Monday' then 4
end
end
)
SELECT --r.[LetterGenerationTemplateRequestId]
--,r.LetterGenerationTemplateId
g.LetterGenerationPrintJobId
,CAST(t.[TemplateKey] AS VarChar) AS LetterCode
,r.[Loan_no]
,convert(char(12),r.CreatedDate,101) AS CreatedDate
-- ,g.ModifiedDate
-- ,convert(varchar(18), g.ModifiedDate - r.CreatedDate, 108) AS TimeSpan
,s.LetterGenerationStatusId AS Status
,s.StatusKey AS StatusDesc
,count(g.LetterGenerationId) as LetterCount
FROM [LetterGenerationTemplateRequest] AS r
INNER JOIN [LetterGenerationTemplate] AS t
ON t.[LetterGenerationTemplateId] = r.LetterGenerationTemplateId
INNER JOIN LetterGeneration g
ON g.LetterGenerationTemplateRequestId = r.LetterGenerationTemplateRequestId
INNER JOIN LetterGenerationStatus s
ON g.LetterGenerationStatusId = s.LetterGenerationStatusId
WHERE
DATEDIFF(day, r.CreatedDate, GETDATE()) <= (select diff from aux)
AND t.[TemplateKey] NOT LIKE '%PLTV1%'
AND s.LetterGenerationStatusId NOT LIKE '4'
AND s.LetterGenerationStatusId NOT LIKE '16'
AND s.LetterGenerationStatusId NOT LIKE '19'
AND s.LetterGenerationStatusId NOT LIKE '20'
AND s.LetterGenerationStatusId NOT LIKE '38'
GROUP BY r.[LetterGenerationTemplateRequestId]
,r.LetterGenerationTemplateId
,g.Lettergenerationprintjobid
,t.[TemplateKey]
,r.[Loan_no]
,r.CreatedDate
-- ,g.ModifiedDate
,r.[CreatedBy]
,s.LetterGenerationStatusId
,s.StatusKey
ORDER BY r.CreatedDate DESC
The CTE aux returns a dataset with only one record and only one field, the value of which is the number of days you need to go back in your WHERE statement.

Limit to two results for each individual ID

How would I return two results for each individual meeting.id?
I've tried things like Row_Count() and Rank() but they seem to cause syntax errors.
This is my query which I need adapting to show the two results per meeting.id.
SELECT meeting_appointment.* FROM `meeting`
INNER JOIN meeting_appointment ON (
meeting_appointment.meeting_id = meeting.id AND meeting_appointment.pupil_id = 0 AND meeting_appointment.guardian_id = 0 AND meeting_appointment.deleted = 0
)
WHERE (
meeting.grade_id = "-1" OR meeting.grade_id IN ('87')
)
AND meeting.startTime < '2016-10-06 14:00:00' + INTERVAL 1 HOUR AND meeting.startTime > '2016-10-06 14:00:00' - INTERVAL 1 HOUR
GROUP by meeting_appointment.id
ORDER BY meeting_appointment.startTime ASC
If your meeting_appointment.id are consecutive then this should show the first two
meeting_appointment.id per meeting.id:
SELECT meeting_appointment.* FROM `meeting`
INNER JOIN meeting_appointment ON (meeting_appointment.meeting_id = meeting.id)
WHERE (meeting.grade_id = "-1" OR meeting.grade_id IN ('87'))
AND meeting.startTime < '2016-10-06 14:00:00' + INTERVAL 1 HOUR AND meeting.startTime > '2016-10-06 14:00:00' - INTERVAL 1 HOUR
AND meeting_appointment.pupil_id = 0
AND meeting_appointment.guardian_id = 0
AND meeting_appointment.deleted = 0
AND NOT EXISTS
(SELECT 1 FROM meeting_appointment ma WHERE ma.id<meeting_appointment.id-1
AND ma.pupil_id = 0
AND ma.guardian_id = 0
AND ma.deleted = 0
)
GROUP by meeting_appointment.id
ORDER BY meeting_appointment.startTime ASC
If you meeting_appointment.id are just random or non-numeric then it might need some tweaks but in order to code those teaks we'll need to know a bit more about the schema.
You don't have an aggregate function, so GROUP BY is redundant. Remove that, and add a LIMIT clause, and you should be good to go:
SELECT ma.*
FROM meeting m
INNER JOIN meeting_appointment ma ON ma.meeting_id = m.id
WHERE m.grade_id IN ('-1','87')
AND ma.pupil_id = 0
AND ma.guardian_id = 0
AND ma.deleted = 0
AND m.startTime < '2016-10-06 14:00:00' + INTERVAL 1 HOUR
AND m.startTime > '2016-10-06 14:00:00' - INTERVAL 1 HOUR
ORDER BY ma.startTime ASC
LIMIT 2;
You could also use BETWEEN for the datetime column.