How to do a proper SQL recursion? - mysql

I'm currently working on a little error-handling issue in the following query:
declare #DayCounter int
declare #rows int
set #DayCounter = -2
set #rows = 0
while #rows = 0
begin
select
coalesce(p.name, case when len(ol.action)>1 then substring(ol.action,1,40)+'<br />'+substring(ol.action,41,80) else ca.code end) as description
, convert(varchar,DateAdd(minute,
15 * ((60 * Datepart(hour, latest_payment_status_change) +
Datepart(Minute, latest_payment_status_change)+
Case When DatePart(second, latest_payment_status_change) < 30
Then 7 Else 8 End) / 15),
DateAdd(day, DateDiff(day, 0, latest_payment_status_change), 0)),126) as date_time
, sum(num_of_persons) as num_tickets
from tick_order o
join tick_orderline ol on ol.order_id=o.id
join (
select olt.id
from tick_orderline_type olt
join tick_case ca on ca.id=olt.case_id
join tick_client tcl on tcl.id = ca.client_id
where tcl.id = 'CLIENT_TEST'
union
select orderline_type_id
from rpt_client_orderline_type rcot
join tick_client tcl on tcl.id = rcot.client_id
where tcl.id = 'CLIENT_TEST'
union
select olt.id
from tick_orderline_type olt
join rpt_client_case rcc on rcc.case_id=olt.case_id
join tick_client cl on cl.id=rcc.client_id
where cl.id = 'CLIENT_TEST'
) olt2 on olt2.id=ol.orderline_type_id
join tick_orderline_type olt on olt.id=ol.orderline_type_id
join tick_case ca on ca.id=olt.case_id
join tick_client cl on cl.id=ca.client_id
join tick_user_case uc on uc.case_id=ca.id
join tick_user u on u.id=uc.user_id
left join tick_promotion_code pc on pc.id=o.promotion_code_id
left join tick_promotion p on p.id=pc.promotion_id
where o.latest_payment_status_change>=dateadd(day,#DayCounter,current_timestamp)
and ((o.latest_payment_status_code = 'ORDER_PAYMENT_OK')
or (o.latest_payment_status_change > dateadd(minute,30,current_timestamp)
and o.latest_payment_status_code = 'ORDER_PAYMENT_WAITING' ))
group by
coalesce(p.name, case when len(ol.action)>1 then substring(ol.action,1,40)+'<br />'+substring(ol.action,41,80) else ca.code end)
, convert(varchar,DateAdd(minute,
15 * ((60 * Datepart(hour, latest_payment_status_change) +
Datepart(Minute, latest_payment_status_change)+
Case When DatePart(second, latest_payment_status_change) < 30
Then 7 Else 8 End) / 15),
DateAdd(day, DateDiff(day, 0, latest_payment_status_change), 0)),126)
order by 1,2
set #DayCounter = #DayCounter - 1;
if ##rowcount <> 0
begin
set #rows = ##rowcount;
print #DayCounter;
end;
end;
The data retrieved is used for a diagram that shows sales per sale-type from the past two days. What I'm trying to achieve now is: When no sales have been made in the past two days (##rowcount = 0), check back a day further each time until data has been found.
The query as it stands now returns something like this (I tried getting the image working, but I somehow am unable to; have a link instead):
https://www.dropbox.com/s/0hculuegrc88c69/SQL_Result.png?dl=0
And it doesn't stop, because for some reason the #rows variable stays 0, despite the query clearly returning rows. Even when using print ##rowcount it returns rows.
SO how do I fix this? Should I use a completely different method?
-Zubaja

It would seem I did not quite understood what my employer meant. He didn't want the error-handling in the SQL, but rather on the website: When no data is found for the past two days, simply show an empty diagram.

Related

Where do I begin Looping statements in MySQL Workbench

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

Multiplying CASE row with different values

I'm creating a rating system. I have two tables hinne (rating) and hinnang (rating multiplier). I need to multiply the rating and then average the rating to know what rating I got out of all ratings by aine(subject).
Example:
All points need to be calculated in 0-100 point system.
So if my first rate is 25 and the rating multiplier is 4 then first rate (25/25)
4*25=100
If the second rate is 30 and multiplier 2 then second rate (30/50)
2*30=60
Now I need to average them like 100+60/2=80.
That should work in my SQL statement, but I got in trouble.
CASE
WHEN aine.nimetus = 'Füüsika I'
THEN hinne.tulemus * hindamine.kaal
ELSE 0
END
,0)
))
So, this is my pivot case statement. hindamine.kaal should be different value for each hinne.tulemus 25*4,50*2 BUT it doesn't work. It just uses multiplier value 4. How can I make this work?
The result of SQL: 150
The expected result: 100
Therefore here is my full SQL:
SELECT
tudeng.m_number,hindamine.kaal, ROUND(AVG(NULLIF(
CASE
WHEN aine.nimetus = 'Füüsika I'
THEN hinne.tulemus * hindamine.kaal
ELSE 0
END
,0)
))
AS FüüsikaI ,ROUND(AVG(NULLIF(
CASE
WHEN aine.nimetus = 'Kõrgem matemaatika I'
THEN hinne.tulemus * hindamine.kaal
ELSE 0
END
,0)
))
AS KõrgemmatemaatikaI ,ROUND(AVG(NULLIF(
CASE
WHEN aine.nimetus = 'Raalprojekteerimine'
THEN hinne.tulemus * hindamine.kaal
ELSE 0
END
,0)
))
AS Raalprojekteerimine ,ROUND(AVG(NULLIF(
CASE
WHEN aine.nimetus = 'Tehniline graafika'
THEN hinne.tulemus * hindamine.kaal
ELSE 0
END
,0)
))
AS Tehnilinegraafika , ROUND(AVG(NULLIF(
CASE
WHEN aine.nimetus = 'Ettevõteluse alused'
THEN hinne.tulemus * hindamine.kaal
ELSE 0
END
,0)
))
AS Ettevõtelusealused
FROM
tudeng
INNER JOIN
aine_tudeng
ON
tudeng.tudeng_id = aine_tudeng.tudeng_id
INNER JOIN
aine
ON
aine.aine_id = aine_tudeng.aine_id
INNER JOIN
hinne
ON
hinne.aine_tudeng_id=aine_tudeng.aine_tudeng_id
INNER JOIN
hindamine
ON
hindamine.hindamine_id=aine_tudeng.aine_id
GROUP BY
tudeng.m_number
I suppose your error is here:
ON hindamine.hindamine_id = aine_tudeng.aine_id
A hindamine (assessment/rating?) is something different from an aine (subject?), so you are mistaken in joining on these IDs.
(I have used Google translator to help me with the meanings.)

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.

SQL datetime less than NOW() returns invalid results

I am trying to run the following SQL statement
SELECT
P.team_id,
P.id,
M.id,
P.`end`,
UHT.user_id
FROM phase P
INNER JOIN module M
ON P.Module_id = M.id
JOIN user_has_team UHT
ON UHT.team_id = P.team_id
WHERE
M.module_type = 7 OR
M.module_type = 8 AND
P.end < NOW();
This result returns 180 rows. if i go to the bottom of these rows i get a result that looks like this:
'52', '130', '275', '2014-12-16 00:00:00', '49'
This is just 1 out of many.
First i thought "Hey maybe the server time is invalid"
But when i run SELECT NOW() i get the following result:
'2014-11-02 19:38:49'
So what could the problem be?
You need some more parenthesis because of operator precedence in SQL.
Try changing your WHERE clause from
WHERE M.module_type = 7 or M.module_type = 8 AND P.end < NOW();
to
WHERE (M.module_type = 7 or M.module_type = 8) AND P.end < NOW();
Look into operator precedence,your query is interpreted as:
WHERE (M.module_type = 7)
or
((M.module_type = 8)
AND P.end < NOW());
But you need
WHERE (M.module_type = 7 or M.module_type = 8) AND P.end < NOW();

Change MySQL query from WHERE to LEFT JOIN

I have a query which reports on Asterisk call usage and queue statistics. The query currently uses MySQL WHERE clauses to join the tables and filter the data.
This is how the query looks at present:
SELECT
c.name as 'Agent',
e.extended_number AS 'Extension',
COUNT(ql.`time`) AS 'Total Inbound Calls',
(SELECT
COUNT(1)
FROM
call_history ch,
ast_queue_mstatus qm
WHERE
ch.start >= (DATE_SUB(DATE(NOW()),
INTERVAL 10050 MINUTE))
AND ch.start <= (DATE(NOW()))
AND ch.calltype = 'out'
AND ch.flow = 'out'
AND ch.extension_number = qm.membername
AND qm.membername = e.extended_number
GROUP BY qm.membername) AS 'Total Outbound Calls',
FORMAT(SUM(CAST(ql.arg2 AS UNSIGNED)) / 60, 2) AS 'Total Inbound Duration',
FORMAT(AVG(CAST(ql.arg2 AS UNSIGNED)) / 60, 2) AS 'Avg Inbound Duration',
FORMAT(AVG(CAST(ql.arg1 AS UNSIGNED)) / 60, 2) AS 'Avg Caller Hold Time'
FROM
ast_queue_log ql,
ast_queue_mstatus qs,
client c,
extension e
WHERE
ql.queuename = '1234'
AND ql.`time` >= (DATE_SUB(DATE(NOW()), INTERVAL 7 DAY))
AND ql.`time` <= (DATE(NOW()))
AND (ql.event = 'COMPLETEAGENT'
OR ql.event = 'COMPLETECALLER'
OR ql.event = 'COMPLETETRANSFER')
AND RIGHT(ql.agent, 8) = qs.membername
AND qs.membername = e.extended_number
AND e.client_id = c.id
GROUP BY ql.agent
ORDER BY c.name;
The problem that I have is that I need to return outbound call results for queue members that have not received any inbound calls. At the moment if there are no records in ast_queue_log then the WHERE clause excludes the records, which means they do not get picked up by the scalar sub query.
How can I change this query so I get all call records from call_history where there is a corresponding record in ast_queue_mstatus?
Sorry if this is too complex or there is not enough info, I can try to simplify if this makes no sense!
It's time to ditch those old comma separated list of tables and use the join standard that was introduced in 1992 (yes). :-)
Because then you can use a LEFT JOIN. With this kind of join, the relationship is optional. That is, all rows in the first table are returned (if they match the conditions in the WHERE clause), but the joined table doesn't have to have records as well.
If it doesn't, a row is still returned, but the fields are filled with NULL value.
SELECT
c.name as 'Agent',
e.extended_number AS 'Extension',
COUNT(ql.`time`) AS 'Total Inbound Calls',
(SELECT
COUNT(1)
FROM
call_history ch
INNER JOIN ast_queue_mstatus qm
ON qm.membername = ch.extension_number
WHERE
ch.start >= (DATE_SUB(DATE(NOW()), INTERVAL 10050 MINUTE))
AND ch.start <= (DATE(NOW()))
AND ch.calltype = 'out'
AND ch.flow = 'out'
AND ch.extension_number = e.extended_number
GROUP BY
ch.extension_number) AS 'Total Outbound Calls',
FORMAT(SUM(CAST(ql.arg2 AS UNSIGNED)) / 60, 2) AS 'Total Inbound Duration',
FORMAT(AVG(CAST(ql.arg2 AS UNSIGNED)) / 60, 2) AS 'Avg Inbound Duration',
FORMAT(AVG(CAST(ql.arg1 AS UNSIGNED)) / 60, 2) AS 'Avg Caller Hold Time'
FROM
client c,
INNER JOIN extension e
ON e.client_id = c.id
INNER JOIN ast_queue_mstatus qs
ON qs.membername = e.extended_number
LEFT JOIN ast_queue_log ql
ON ql.queuename = '1234'
AND ql.`time` >= (DATE_SUB(DATE(NOW()), INTERVAL 7 DAY))
AND ql.`time` <= (DATE(NOW()))
AND (
ql.event = 'COMPLETEAGENT'
OR ql.event = 'COMPLETECALLER'
OR ql.event = 'COMPLETETRANSFER')
AND RIGHT(ql.agent, 8) = qs.membername
GROUP BY ql.agent
ORDER BY c.name;
You can use RIGHT JOIN as well to join the other way around (first table is optional). But in general this is harder to interpret.