Is it possible to use a named select in the where clause? - mysql

I have a semi-complicated select statement that's building a custom "column" in a query, and the results need to be filtered by the results of this column. Is there some way to refer to this column in a predicate? You'll see what I would like to do commented out in the where clause, filtering on 'On Sale'.
select
p.prod_id,
case
when p.subtitle is null then p.title
else concat(p.title, ': ', p.subtitle)
end as 'Title',
p.issue as 'Issue',
e.abbrv as 'Editor',
p.jobnum as 'Job Number',
p.price as 'Price',
ship.due_date as 'Ship Date',
case
when pi.onsale_minus_ship_date is not null then ship.due_date + interval pi.onsale_minus_ship_date day
else
case
when prs.days_for_shipping != 0 then ship.due_date + interval prs.days_for_shipping day
else ship.due_date + interval 7 day
end
end as 'On Sale',
sale.due_date as 'Bookstore On Sale'
from products p
join schedules ship on ship.prod_id = p.prod_id and ship.milestone = 49
left join schedules sale on sale.prod_id = p.prod_id and sale.milestone = 647
left join editors e on find_in_set(e.id, p.editor)
left join printing_info pi on pi.prod_id = p.prod_id
left join printers prs on prs.id = pi.printer
where p.prod_type in (2, 3, 5, 6) -- physical, non comics (trades, hc, etc.)
--and 'On Sale' >= '$start_date' + interval 2 month
--and 'On Sale' <= '$end_date' + interval 2 month
order by ship.due_date asc, p.title asc

You can do your filtering in the HAVING clause - unfortunately, you can't refer to column aliases in the WHERE clause.
HAVING `On Sale` >= '$start_date' + interval 2 month
AND `On Sale` <= '$end_date' + interval 2 month
http://dev.mysql.com/doc/refman/5.0/en/problems-with-alias.html

Related

Convert mysql query to sqlite

I am new to sqlite3.I am currently using mysql. But I will be migrating it to sqlite3.
I am calculating month end balance
SELECT c.country,
Date_format(Last_day(Str_to_date(dt.date, '%m/%d/%Y')), '%Y/%m/%d')
AS Month_End_Balance,
Sum(dt.amount) AS in_Euro
FROM deposit_transactions AS dt
LEFT JOIN customers AS c
ON c.customer_id = dt.customer_id
GROUP BY c.country,
Month_End_Balance
order by Month_End_Balance desc
Need help in converting it to sqlite
Question
Need help in Last_Day function alternative in sqlite
Refer below query for SQLITE -
Please make adjustment as per needed date format.
with new_dep_trx as (
select deposit_id,customer_id,
transaction_type,amount,currency,
case when (length(dt.date)=8 or length(dt.date)=9) and instr(substr(dt.date,1,2),'/')>0
then
date(substr(dt.date,length(dt.date)-3,4)||'-0'||substr(dt.date,1,1)||'-0'||substr(dt.date,3,1))
when length(dt.date)=9 and instr(substr(dt.date,1,2),'/')=0
then
date(substr(dt.date,length(dt.date)-3,4)||'-'||substr(dt.date,1,2)||'-0'||substr(dt.date,4,1))
else
date(substr(dt.date,length(dt.date)-3,4)||'-'||substr(dt.date,1,2)||'-'||substr(dt.date,4,2)) end date_col
from deposit_transactions dt
)
select
c.country,
strftime('%Y-%m',dt.date_col) as month,
strftime('%m/%d/%Y',date(dt.date_col,'start of month','+1 month','-1 day')) as last_day_of_month,
strftime('%Y/%m/%d',date(dt.date_col,'start of month','+1 month','-1 day')) as last_day_of_month_your_format,
SUM(
dt.amount *
(CASE WHEN dt.currency = 'GBP' THEN .85 ELSE 1 END) *
(CASE WHEN dt.transaction_type = 'pay_in' THEN 1 ELSE -1 END)
) amount_eur
FROM new_dep_trx dt
LEFT JOIN customers c ON c.customer_id = dt.customer_id
GROUP BY c.country, last_day_of_month_your_format;
Modified DB fiddle.
First, you must update the column date of the table deposit_transactions so that it has the format YYYY-mm-dd the only text date format that you can use with SQLite's datetime functions:
UPDATE deposit_transactions
SET date = SUBSTR(date, -4) || '-' ||
printf('%02d', date + 0) || '-' ||
printf('%02d', SUBSTR(date, INSTR(date, '/') + 1, 2) + 0);
Now, you can use the function date() to get the last day of each month with:
date(date, 'start of month', '+1 month', '-1 day')
So, your query should be:
SELECT c.country,
date(dt.date, 'start of month', '+1 month', '-1 day') AS Month_End_Balance,
SUM(dt.amount *
CASE WHEN dt.currency = 'GBP' THEN .85 ELSE 1 END *
CASE WHEN dt.transaction_type = 'pay_in' THEN 1 ELSE -1 END
) AS amount_eur
FROM deposit_transactions AS dt LEFT JOIN customers AS c
ON c.customer_id = dt.customer_id
GROUP BY c.country, Month_End_Balance
ORDER BY Month_End_Balance DESC;
If you want to format the dates of the resultset to mm/dd/YYYY and sort properly by the date and not the formatted date which would sort incorrectly:
SELECT c.country,
strftime('%m/%d/%Y', date(dt.date, 'start of month', '+1 month', '-1 day')) AS Month_End_Balance,
SUM(dt.amount *
CASE WHEN dt.currency = 'GBP' THEN .85 ELSE 1 END *
CASE WHEN dt.transaction_type = 'pay_in' THEN 1 ELSE -1 END
) AS amount_eur
FROM deposit_transactions AS dt LEFT JOIN customers AS c
ON c.customer_id = dt.customer_id
GROUP BY c.country, date(dt.date, 'start of month', '+1 month', '-1 day')
ORDER BY date(dt.date, 'start of month', '+1 month', '-1 day') DESC;
See the demo.

MySQL subquery - using alias from SELECT in WHERE conditions (no CTE)

I'm trying to find out where/how to implement subquery in below trimmed down example.
The issue is that I need to add hours depending on time zones and then return those fields.
At the same time though, I need to filter by the same fields, and for it to be accurate, I need them to already be adjusted to the correct time zone.
Can you please give me an advice on how I can work this out?
SELECT
od.ID AS 'Id',
CASE WHEN tzp.ZoneId='Etc/GMT-1'
THEN ADDDATE(od.declaredStartTime, INTERVAL 1 HOUR)
ELSE od.declaredStartTime END AS 'Collection date\\time',
CASE WHEN tzd.ZoneId='Etc/GMT-1'
THEN ADDDATE(od.declaredEndTime, INTERVAL 1 HOUR)
ELSE od.declaredEndTime END AS 'Delivery date\\time'
FROM orders od
LEFT OUTER JOIN time_zone AS tzp
ON (tzp.ID = od.pickupTimeZone_ID)
LEFT OUTER JOIN time_zone AS tzd
ON (tzd.ID = od.dropTimeZone_ID)
WHERE
od.IS_DELETED != '1'
-- AND 'Delivery date\\time' >= '2019-06-30' --<< This won't work because
-- AND 'Delivery date\\time' <= '2019-08-01' --<< SELECT is the last operation
I unfortunately have no option to use CTE, as our MySQL is 5.6 and CTE is apparently available from 8.0
Edited: to make the code sample valid
Edit: full working version of the command
SELECT
od.ID AS 'Id',
od.referenceNumber AS 'Reference number',
od.secondReferenceNumber AS 'Second reference number',
od.status AS 'Status',
ADDDATE(od.created, INTERVAL 1 HOUR) AS 'Creation date/time', -- BST not accounted for!
st.name AS 'Shipment type',
IFNULL(sp.name, '') AS 'Supplier',
ls.name AS 'Collection location',
CASE WHEN tzp.ZoneId='Etc/GMT-1'
THEN ADDDATE(od.declaredStartTime, INTERVAL 1 HOUR)
ELSE od.declaredStartTime END AS 'Collection date\\time',
ld.name AS 'Delivery location',
CASE WHEN tzd.ZoneId='Etc/GMT-1'
THEN ADDDATE(od.declaredEndTime, INTERVAL 1 HOUR)
ELSE od.declaredEndTime END AS 'Delivery date\\time',
od.supplierRate AS 'Supplier rate (pounds)',
IFNULL(od.alternativeSupplierRate, '') AS 'Supplier alternative rate (pounds)',
od.supplierTransportTotal AS 'Supplier total rate (pounds)',
od.customerRate AS 'Customer rate (pounds)',
IFNULL(od.alternativeCustomerRate, '') AS 'Customer alternative rate (pounds)',
od.customerTransportTotal AS 'Customer total rate (pounds)',
IFNULL(od.quantity, '') AS 'Quantity',
IFNULL(od.weight, '') AS 'Weight',
IFNULL(od.palletQuantity, '') AS 'Pallet quantity',
IFNULL(od.kmsTravelled, '') AS 'Kms travelled',
IFNULL(od.carbonFootprint, '') AS 'Carbon footprint',
IFNULL(od.performanceScore, '') AS 'Performance score',
od.fuel_surcharge AS 'Fuel surcharge (pounds)',
od.caf_surcharge AS 'CAF surcharge (pounds)',
IFNULL(od.supplierFuelSurcharge, '') AS 'Supplier fuel surcharge (pounds)',
IFNULL(od.supplierCAFSurcharge, '') AS 'Supplier CAF surcharge (pounds)',
od.vehiclesRegistrationInformation AS 'Vehicles registration information',
od.trailerIsChecked AS 'Trailer is checked',
od.checkedTrailerNumber AS 'Trailer number',
od.additionalPickupsNumber AS 'Number of additional pickups',
od.additionalDropsNumber AS 'Number of additional drops',
IFNULL(od.customerRateFromPricingMatrix, '') AS 'Customer rate from pricing matrix',
IFNULL(od.supplierRateFromPricingMatrix, '') AS 'Supplier rate from pricing matrix',
IF(el.shipmentLateType IS NULL, 'FALSE', 'TRUE') AS 'Is late', -- NULL or late type
IFNULL(rc.name, '') AS 'Late category',
od.comment AS 'Comments',
od.customerComment AS 'Customer comments',
od.supplierComment AS 'Supplier comments',
IF(od.rejectedByCustomer, 'TRUE', 'FALSE') AS 'Rejected by customer', -- dbck
IFNULL(uc.name, '') AS 'Unallocation reason',
IFNULL(us2.name, '') AS 'Unallocation user',
IFNULL(ur.created, '') AS 'Unallocation date/time',
IFNULL(sp2.name, '') AS 'Unallocation supplier',
IFNULL(od.supplierRankPosition, '') AS 'Supplier\'s position',
IFNULL(od.supplierRank, '') AS 'Supplier\'s rank',
IFNULL(od.highestRankOnAllocation, '') AS 'Highest rank',
IF(od.proveOfDelivery IS NULL, 'FALSE', 'TRUE') AS 'POD attached', -- NULL or filename
IFNULL(od.supplierInvoiceNumber, '') AS 'Supplier Sage invoice number',
IFNULL(od.customerInvoiceNumber, '') AS 'Customer Sage invoice number',
IFNULL(od.purchaseOrderNumber, '') AS 'Purchase orders batch number',
IFNULL(vc.name, '') AS 'Vehicle',
IFNULL(od.invoicingKmsTravelled, '0') AS 'Actual Kms travelled',
IFNULL(od.duration, '0') AS 'Actual duration',
IFNULL(vc.location, '') AS 'Vehicle location', -- loc verify
IFNULL(tp.idling, '0') AS 'Idling time',
IFNULL(tp.fuelUsed, '0') AS 'Fuel used',
IF(od.hasDeliveryDiscrepancy, 'TRUE', 'FALSE') AS 'Has delivery discrepancy',
IFNULL(od.deliveryDiscrepancyReasonID, '') AS 'Delivery discrepancy reason Id',
IFNULL(dc1.title, '') AS 'Delivery discrepancy reason name',
IFNULL(od.deliveryDiscrepancyComment, '') AS 'Delivery discrepancy comment',
IF(od.hasHazardousGoods, 'TRUE', 'FALSE') AS 'Has hazardous goods',
IFNULL(od.surchargeReasonId, '') AS 'Surcharge reason Id',
IFNULL(dc2.title, '') AS 'Surcharge reason name',
od.bookingReference AS 'Booking reference',
od.driverName AS 'Driver',
CASE
WHEN ISNULL(wr.passed) THEN 'Not started'
WHEN SUM(CASE WHEN wr.passed=0 THEN 1 ELSE 0 END)=0
THEN 'Passed'
ELSE 'Failed'
END AS 'Walkaround Check status',
SUM(CASE
WHEN wr.passed=0 THEN 1 ELSE 0
END) AS 'Walkaround Check failed count'
FROM orders od
LEFT OUTER JOIN shipment_type AS st
ON (st.ID = od.shipmentType_ID)
LEFT OUTER JOIN shipment_unallocate_reason AS ur
ON (ur.shipment_ID = od.ID)
LEFT OUTER JOIN unallocate_reason_codes AS uc
ON (uc.ID = ur.reasonCode_ID)
LEFT OUTER JOIN user AS us2
ON (us2.ID = ur.user_ID)
LEFT OUTER JOIN suppliers AS sp
ON (sp.ID = od.supplier_ID)
LEFT OUTER JOIN suppliers AS sp2
ON (sp2.ID = ur.supplier_ID)
LEFT OUTER JOIN walk_around_check AS wc
ON (wc.shipment_id = od.ID)
LEFT OUTER JOIN walk_around_check_result AS wr
ON (wr.check_id = wc.ID)
LEFT OUTER JOIN walk_around_check_item AS wi
ON (wr.check_item_id = wi.id)
LEFT OUTER JOIN landmark AS ld
ON (ld.ID = od.destination_id)
LEFT OUTER JOIN landmark AS ls
ON (ls.ID = od.source_id)
LEFT OUTER JOIN landmark AS lw
ON (lw.ID = wc.location_id)
LEFT OUTER JOIN time_zone AS tzp
ON (tzp.ID = od.pickupTimeZone_ID)
LEFT OUTER JOIN time_zone AS tzd
ON (tzd.ID = od.dropTimeZone_ID)
LEFT OUTER JOIN user AS us
ON (us.ID = wc.driver_id)
LEFT OUTER JOIN shipment_event AS el
ON (el.Shipment_ID = od.ID) -- AND se.shipmentLateType = 'DELIVERY_LATE'
LEFT OUTER JOIN late_reason_codes AS rc
ON (rc.ID = el.reasonCode_ID)
LEFT OUTER JOIN dictionary AS dc1
ON (dc1.id = od.deliveryDiscrepancyReasonID)
LEFT OUTER JOIN dictionary AS dc2
ON (dc2.id = od.surchargeReasonId)
LEFT OUTER JOIN trips AS tp
ON (tp.shipment_ID = od.ID)
LEFT OUTER JOIN vehicle AS vc
ON (vc.id = tp.vehicle_ID)
WHERE od.IS_DELETED != '1'
-- NOTE: Have to compare dates incl. timezone changes!!!
-- AND 'Collection date\\time' >= '2019-06-30'
-- AND 'Collection date\\time' <= '2019-08-01'
-- AND 'Delivery date\\time' >= '2019-06-30'
-- AND 'Delivery date\\time' <= '2019-08-01'
GROUP BY od.ID -- required in conjunction with SUM to not return single row
ORDER BY od.ID ASC
You must repeat the CASE statement in the WHERE clause:
AND CASE
WHEN tzd.ZoneId='Etc/GMT-1'THEN ADDDATE(od.declaredEndTime, INTERVAL 1 HOUR)
ELSE od.declaredEndTime
END BETWEEN '2019-06-30' AND '2019-08-01'
instead of:
AND 'Delivery date\\time' >= '2019-06-30'
AND 'Delivery date\\time' <= '2019-08-01'
Another way of doing this is using a HAVING clause instead of WHERE but this would need to nest your query as a subquery because you already use GROUP BY:
SELECT Id, `Collection date\\time`, `Delivery date\\time`
FROM (
SELECT
od.ID AS 'Id',
CASE WHEN tzp.ZoneId='Etc/GMT-1'
THEN ADDDATE(od.declaredStartTime, INTERVAL 1 HOUR)
ELSE od.declaredStartTime END AS `Collection date\\time`,
CASE WHEN tzd.ZoneId='Etc/GMT-1'
THEN ADDDATE(od.declaredEndTime, INTERVAL 1 HOUR)
ELSE od.declaredEndTime END AS `Delivery date\\time`,
FROM orders od
LEFT OUTER JOIN time_zone AS tzp
ON (tzp.ID = od.pickupTimeZone_ID)
LEFT OUTER JOIN time_zone AS tzd
ON (tzd.ID = od.dropTimeZone_ID)
HAVING
od.IS_DELETED != '1'
AND `Delivery date\\time` >= '2019-06-30' --<< This won't work because
AND `Delivery date\\time` <= '2019-08-01' --<< SELECT is the last operation
)
GROUP....
Note: since you use the GROUP BY clause what is the point of these calculated columns in the select list? Your statement should not even run. Or maybe it's not the exact code you really have.

Case When Exists In Subquery

I am trying to check whether an ID is present in a subquery. I have ran just the subquery and it produces a list of all the ID's which have a fee against it, so what I want to do is check whether the ID in the main query is present in the subquery. If it's present then return 1, else return 0.
This is an easy query but I have no idea where i'm going wrong, I tried using exists rather than in but this does not work either.
case when debtor._rowid in (
select distinct note.debtorid from note
left join debtor on note.debtorid = debtor._rowid
left join fee on fee.debtorid = debtor._rowid
where fee.type = "Enforcement" and note.type = "Stage")
then 1 else 0 end) as `Enforcement`
Below is the entire code, when I remove the above code from the main query below, it works perfectly, so there's something wrong in my case statement.
with cte_1
as
(
select
debtor._rowid as casref
,concat(date_format(date_sub(debtor._createddate, interval 3 month), '%y'), '/', date_format(date_add(debtor._createddate, interval 9 month), '%y')) as `F/Y`
,date_format(debtor._createddate, '%M %Y') as `Loaded Month`
,ifnull(concat(date_format(date_sub(debtor.offence_date, interval 3 month), '%y'), '/', date_format(date_add(debtor.offence_date, interval 9 month), '%y')),'-') as `LO F/Y`
,coalesce(date_format(debtor.offence_date,'%M %Y'),'-') as `Liability Order Month`
,scheme.name as `Scheme`
,branch.name as `Branch`
,count(debtor._rowid) as `Cases Received`
,count(debtor.linkid) as `LinkID`
,(case
when concat(date_format(date_sub(debtor._createddate, interval 3 month), '%y'), '/', date_format(date_add(debtor._createddate, interval 9 month), '%y'))
= ifnull(concat(date_format(date_sub(debtor.offence_date, interval 3 month), '%y'), '/', date_format(date_add(debtor.offence_date, interval 9 month), '%y')),'-')
then 1 else 0 end ) as `Same Year`
, case when debtor._rowid in (
select distinct note.debtorid from note
left join debtor on note.debtorid = debtor._rowid
left join fee on fee.debtorid = debtor._rowid
where fee.type = "Enforcement"
and note.type = "Stage")
then 1 else 0 end) as `Enforcement`
from debtor
left join clientscheme on debtor.clientschemeID = clientscheme._rowid
left join scheme on clientscheme.schemeID = scheme._rowid
left join branch on clientscheme.branchID = branch._rowid
left join fee on debtor._rowid = fee.debtorid
left join note on debtor._rowid = note.debtorid
where clientscheme.branchID in (1,10,24)
and debtor._createddate >= '2017-04-01'
group by debtor._rowid
)
,
cte_2
as
(
select
`F/Y`
,`Loaded Month`
,`LO F/Y`
,`Liability Order Month`
,`Scheme`
,`Branch`
,sum(`Cases Received`) as `Case Count`
,sum(`LinkID`) as `Linked Accounts`
,sum(`Same Year`) as `In Year LO`
,sum(Enforcement) as `Enforcement Applied`
from cte_1
group by
`Loaded Month`
,`Liability Order Month`
,`Scheme`
, `Branch`
)
select
`F/Y`
,`Loaded Month`
,`LO F/Y`
,`Liability Order Month`
,`Scheme`
,`Branch`
,`Case Count`
,`Linked Accounts`
,round((`Linked Accounts`/`Case Count`),2) * 100 as `% of Linked Accounts`
,round((`In Year LO`/`Case Count`),2) * 100 as `In Year LO's`
,`Enforcement Applied`
from cte_2
It appears that you want to logically check if, for a given record in the result set, a _noteid value from the debtor table matches to a debtors from the note table. You could rephrase your query as follows:
SELECT
(d._rowid = n.debtorid) AS `Enforcement Allocated`
FROM note n
LEFT JOIN debtor d
ON n.debtorid = d._rowid
LEFT JOIN fee f
ON f.debtorid = d._rowid
WHERE
f.type = 'Enforcement' AND n.type = 'Stage';
Note that since the output of your CASE expression is just 1 or 0, you may take advantage of that MySQL allows boolean expressions as values.

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 - Too many calls to subquery

The below query is fairly slow, in terms of the subquery selection for the "skill name". When I run a profile against the SQL execution I am getting far too many queries per line from the ACDCallinformation table against the sub query for skillname.
What is the best way of optimising this SQL query or is there a MySQL tool to help with checking on costs for a SQL query and optimising the script?
SELECT
CASE
WHEN(
SELECT
COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`)
FROM acdcallinformation ag
WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL AND DATEofcall = DATE(NOW()) AND ag.skillid = acdcallinformation.skillid
) IS NULL
THEN
0
ELSE
(
SELECT COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`)
FROM acdcallinformation ag
WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL AND DATEofcall= DATE(NOW()) AND ag.skillid = acdcallinformation.skillid)
END AS 'Lost Calls',
CASE WHEN COUNT(acdcallinformation.idleonqueue) IS NULL THEN 0 ELSE COUNT(acdcallinformation.idleonqueue) END AS 'Total Calls',
CASE WHEN COUNT(acdcallinformation.`ANSWERTIME`) IS NULL THEN 0 ELSE COUNT(acdcallinformation.`ANSWERTIME`) END AS 'Answered',
(
SELECT
skillinfo.skillname
FROM skillinfo
WHERE skillinfo.pkey = acdcallinformation.skillid
) AS Skill,
SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time',
SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time'
FROM `acdcallinformation` acdcallinformation
WHERE DATEOFCALL = DATE(NOW())
GROUP BY skill;
Not sure the best way show data:
ACDCALLINFORMATION - number of rows currently 3028
INSTIME PKEY DATEOFCALL CONNECTTIME FIRSTRING SKILLID
2012-07-19 14:50:16 19985 2012-07-19 14:50:16 14:50:16 5
SKILLINFO - Average number of rows is 5-10
INSTIME PKEY SKILLNAME
2012-07-01 13:12:01 1 Calls Outgoing
2012-07-01 13:12:01 2 Call Centre
2012-07-01 13:12:01 3 Accounts
2012-07-01 13:12:01 4 Reception
This is the output expected:
"Lost Calls" "Total Calls" "Answered" "Skill" "Average Answer Time" "Average Talk Time"
"1" "2" "1" "Accounts" "00:00:04" "00:00:01"
"0" "5" "5" "Service" "00:00:07" "00:01:20"
Try this, is using inner joins to improve performance and avoid unnecessary subquerys
SELECT
COALESCE(ag.skillcount, 0) AS 'Lost Calls',
CASE WHEN COUNT(acdcallinformation.idleonqueue) IS NULL THEN 0 ELSE COUNT(acdcallinformation.idleonqueue) END AS 'Total Calls',
CASE WHEN COUNT(acdcallinformation.`ANSWERTIME`) IS NULL THEN 0 ELSE COUNT(acdcallinformation.`ANSWERTIME`) END AS 'Answered',
si.skillname AS Skill,
SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time',
SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time'
FROM `acdcallinformation` acdcallinformation
LEFT JOIN (
SELECT skillid, COUNT(`PKEY`) - COUNT(`ANSWERTIME`) skillcount
FROM acdcallinformation
WHERE (`COMPLETED`) = 1 AND answertime IS NULL AND DATEofcall = DATE(NOW())
) ag ON AND ag.skillid = acdcallinformation.skillid
LEFT JOIN skillinfo si ON si.pkey = acdcallinformation.skillid
WHERE DATEOFCALL = DATE(NOW())
GROUP BY si.skillname;
It looks like you're trying to ensure that NULLs are converted to 0s. Thus:
SELECT
IFNULL(
(SELECT COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`) FROM acdcallinformation ag
WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL
AND DATEofcall = DATE(NOW()) AND ag.skillid = acdcallinformation.skillid
), 0) AS 'Lost Calls',
IFNULL(COUNT(acdcallinformation.idleonqueue), 0) AS 'Total Calls',
IFNULL(COUNT(acdcallinformation.`ANSWERTIME`),0) AS 'Answered',
(
SELECT
skillinfo.skillname
FROM skillinfo
WHERE skillinfo.pkey = acdcallinformation.skillid
) AS Skill,
SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time',
SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time'
FROM `acdcallinformation` acdcallinformation
WHERE DATEOFCALL = DATE(NOW())
GROUP BY skill;
Although, it might be easier to just convert those NULLs to zeros using the language that consumes this data... just a thought.
Also, my reading of the docs for COUNT make me think that it will never return NULL, thus:
SELECT
(SELECT COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`) FROM acdcallinformation ag
WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL
AND DATEofcall = DATE(NOW()) AND ag.skillid = acdcallinformation.skillid
) AS 'Lost Calls',
COUNT(acdcallinformation.idleonqueue) AS 'Total Calls',
COUNT(acdcallinformation.`ANSWERTIME`) AS 'Answered',
(
SELECT
skillinfo.skillname
FROM skillinfo
WHERE skillinfo.pkey = acdcallinformation.skillid
) AS Skill,
SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time',
SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time'
FROM `acdcallinformation` acdcallinformation
WHERE DATEOFCALL = DATE(NOW())
GROUP BY skill;
Finally, I think you can handle your second query with a JOIN
SELECT
IFNULL(
(SELECT COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`) FROM acdcallinformation ag
WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL
AND DATEofcall = DATE(NOW()) AND ag.skillid = acdcallinformation.skillid
), 0) AS 'Lost Calls',
IFNULL(COUNT(acdcallinformation.idleonqueue), 0) AS 'Total Calls',
IFNULL(COUNT(acdcallinformation.`ANSWERTIME`),0) AS 'Answered',
skillinfo.skillname AS Skill,
SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time',
SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time'
FROM `acdcallinformation` acdcallinformation
INNER JOIN skillinfo ON skillinfo.pkey = acdcallinformation.skillid
WHERE DATEOFCALL = DATE(NOW())
GROUP BY skill;
Try this query. The whole query is just a guess but it would be batter if you provided some data. Also i have used id as primary key you need to replace it with your own key. Avoid using subqueries instead use joins they are much faster. Here is the query.
SELECT
IF(l.LDifference IS NULL,0,r.RDifference) AS 'Lost Calls',
IF(COUNT(acdcallinformation.idleonqueue) IS NULL , 0 , COUNT(acdcallinformation.idleonqueue))AS 'Total Calls',
IF(COUNT(acdcallinformation.`ANSWERTIME`) IS NULL,0,COUNT(acdcallinformation.`ANSWERTIME`))AS 'Answered',
(SELECT skillinfo.skillname FROM skillinfo WHERE skillinfo.pkey = acdcallinformation.skillid) AS Skill,
SEC_TO_TIME(AVG(TIME_TO_SEC(a.answertime)- TIME_TO_SEC(a.firstringonqueue))) AS 'Average Answer Time',
SEC_TO_TIME(AVG(TIME_TO_SEC(a.IDLEONQUEUE) - TIME_TO_SEC(a.answertime))) AS 'Average Talk Time'
FROM acdcallinformation as a
INNER JOIN(
SELECT
(COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`)) as `LDifference`
FROM acdcallinformation ag
WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL AND DATEofcall = DATE(NOW()) AND ag.skillid = acdcallinformation.skillid
) as l ON l.id = a.id
INNER JOIN(
SELECT (COUNT(ag.`PKEY`) - COUNT(ag.`ANSWERTIME`)) as `RDifference`
FROM acdcallinformation ag
WHERE (ag.`COMPLETED`) = 1 AND answertime IS NULL AND DATEofcall= DATE(NOW()) AND ag.skillid = acdcallinformation.skillid
) as r ON r.id = a.id
WHERE a.DATEOFCALL = DATE(NOW())
GROUP BY skill;
Try This.
Use INNER JOIN, IF() and try to avoid unnecessary subqueries.
SELECT IFNULL(ag.skillcount, 0) AS 'Lost Calls', COUNT(info.idleonqueue) AS 'Total Calls',
COUNT(info.ANSWERTIME) AS 'Answered', si.skillname AS Skill,
SEC_TO_TIME(AVG(TIME_TO_SEC(answertime)- TIME_TO_SEC(firstringonqueue))) AS 'Average Answer Time',
SEC_TO_TIME(AVG(TIME_TO_SEC(IDLEONQUEUE) - TIME_TO_SEC(answertime))) AS 'Average Talk Time'
FROM acdcallinformation AS info
INNER JOIN (
SELECT skillid, COUNT(PKEY)-COUNT(ANSWERTIME) skillcount
FROM acdcallinformation
WHERE COMPLETED = 1 AND DATEofcall = DATE(NOW()) AND answertime IS NULL
) ag ON ag.skillid = info.skillid
INNER JOIN skillinfo si ON si.pkey = info.skillid
WHERE DATEOFCALL = DATE(NOW())
GROUP BY si.skillname;