Case When Exists In Subquery - mysql

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.

Related

SQL how to create aggregate of two aggregated columns for each row

I'm working on a big query in SQL (MySQL 5.7) to calculate aggregated columns based on raw values in my table. I've created several aggregated columns (see attached screenshot and SQL) and I now need to create a conversion_percent column for each tlp_aff_id in my query.
This conversion_percent should be a division of the aggregated JoinedSessions.total_sessions and COUNT(Report.tlp_aff_id) as leads_total.
My current SQL:
SELECT
# Application details
Report.tlp_aff_id,
# Revenue
JoinedRevenue.total_revenue,
# Commission
JoinedCommission.total_commission,
# Profit
JoinedProfit.total_profit,
# Sessions
JoinedSessions.total_sessions,
# Submits
COUNT(Report.tlp_aff_id) as total_submits,
# Leads
COUNT(Report.tlp_aff_id) as leads_total,
SUM(case when Report.application_result = 'Accepted' then 1 else 0 end) leads_accepted,
SUM(case when Report.application_result = 'Rejected' then 1 else 0 end) leads_rejected
# Conversion percent
# JoinedConversion.conversion_percent
FROM
tlp_payout_report_minute AS Report
INNER JOIN
(
SELECT
JoinedRevenue.tlp_aff_id,
JoinedRevenue.minute_rounded_timestamp,
SUM(commission) AS total_revenue
FROM
tlp_payout_report_minute AS JoinedRevenue
WHERE
JoinedRevenue.minute_rounded_timestamp >= 1664841600
AND
JoinedRevenue.minute_rounded_timestamp <= 1664927999
GROUP BY
JoinedRevenue.tlp_aff_id
) AS JoinedRevenue
ON JoinedRevenue.tlp_aff_id = Report.tlp_aff_id
INNER JOIN
(
SELECT
ReportCommission.tlp_aff_id,
ReportCommission.seller_code,
ReportCommission.minute_rounded_timestamp,
SUM(commission) AS total_commission
FROM
tlp_payout_report_minute AS ReportCommission
WHERE
ReportCommission.minute_rounded_timestamp >= 1664841600
AND
ReportCommission.minute_rounded_timestamp <= 1664927999
AND
ReportCommission.seller_code != 44
GROUP BY
ReportCommission.tlp_aff_id
) AS JoinedCommission
ON JoinedCommission.tlp_aff_id = Report.tlp_aff_id
INNER JOIN
(
SELECT
ReportProfit.tlp_aff_id,
ReportProfit.seller_code,
ReportProfit.application_result,
ReportProfit.minute_rounded_timestamp,
SUM(commission) AS total_profit
FROM
tlp_payout_report_minute AS ReportProfit
WHERE
ReportProfit.minute_rounded_timestamp >= 1664841600
AND
ReportProfit.minute_rounded_timestamp <= 1664927999
AND
ReportProfit.application_result = 'Accepted'
AND
ReportProfit.seller_code = 44
GROUP BY
ReportProfit.tlp_aff_id
) AS JoinedProfit
ON JoinedProfit.tlp_aff_id = Report.tlp_aff_id
INNER JOIN
(
SELECT
Conversion.aff_id,
Conversion.conversion_type,
COUNT(Conversion.ip_address) as total_sessions
FROM
tlp_conversions AS Conversion
WHERE
Conversion.conversion_time >= '2022-10-04 00:00:00'
AND
Conversion.conversion_time <= '2022-10-04 23:59:59'
AND
Conversion.aff_id IS NOT NULL
AND
Conversion.conversion_type = 2
GROUP BY
Conversion.aff_id
) AS JoinedSessions
ON JoinedSessions.aff_id = Report.tlp_aff_id
WHERE
Report.minute_rounded_timestamp >= 1664841600
AND
Report.minute_rounded_timestamp <= 1664927999
GROUP BY
Report.tlp_aff_id
ORDER BY
JoinedRevenue.total_revenue DESC
I'm thinking something along the lines of:
INNER JOIN
(
...
) AS JoinedConversion
ON JoinedConversion.aff_id = Report.tlp_aff_id
But I don't think this is necessary for conversion_percent.
What's the right approach here?

Join not summing CASE WHEN

What I'm looking to do is show my current performance for this month, compared with expected scheduled wins to come in and then display the total expected amount, by product type.
For clarity, I have two sub-products that I'm grouping under the same name.
My issue is that for my 'Charged' amount, it's keeping the two sub-products separate, where as the 'Scheduled' amount is working fine.
The table should look like:
Type | Charged | Scheduled | Expected
A 3 2 5
B 1 1 2
What's actually showing is:
Type | Charged | Scheduled | Expected
A 2 1 3
A 1 1 2
B 1 1 2
The code is as follows:
select
t2.product,
t1.Charged,
t2.Scheduled,
t1.charged + t2.scheduled as 'expected'
from(
select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as 'Type',
SUM(charged) as 'Scheduled'
from
table
where
month(date) = month(now())
and
year(date) = year(now())
and status like 'scheduled'
group by 1
order by 2 desc) t2 join
(select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as 'Type',
sum(charged) as 'Charged'
FROM table
WHERE (status = 'Complete'
AND str_to_date(concat(date_format(date, '%Y-%m'), '-01'), '%Y-%m-%d') = str_to_date(concat(date_format(now(), '%Y-%m'), '-01'), '%Y-%m-%d'))
GROUP BY user_type
ORDER BY user_type ASC) as t1 on t1.type = t2.type
I appreciate I might not be explaining this incredibly well (and that my code is probably quite clunky - I'm still fairly new!) so any help/direction would be appreciated.
Thanks!
Just some suggestion
you have a column product in main select but you have type in subquery and not product
you should not use sigle quote around column name
ad you have group by user_type but you need group by type for charged
select
t2.type,
t1.Charged,
t2.Scheduled,
t1.charged + t2.scheduled as 'expected'
from(
select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as Type,
SUM(charged) as Scheduled
from
table
where
month(date) = month(now())
and
year(date) = year(now())
and status like 'scheduled'
group by 1
order by 2 desc) t2 join
(select
case
when user_type = 'a1' then 'a'
when user_type = 'a2' then 'a'
else 'b'
end as Type,
sum(charged) as Charged
FROM table
WHERE (status = 'Complete'
AND str_to_date(concat(date_format(date, '%Y-%m'), '-01'), '%Y-%m-%d') = str_to_date(concat(date_format(now(), '%Y-%m'), '-01'), '%Y-%m-%d'))
GROUP BY Type
ORDER BY Type ASC) as t1 on t1.type = t2.type

Total the aggregate in Select Case Statement

Is there a way to get a total from the aggregate in each select case statement? The following gives me the correct total by listing the total for each month in a column but I would like to have a single total for each case statement.
SELECT SUM(
CASE WHEN dbo.bill_t_ARTransaction.TransactionDate BETWEEN '2000-01-01' AND '2016-12-31' THEN dbo.bill_t_ARTransaction.Amount ELSE 0 END
) AS 'Dec16'
, SUM(
CASE WHEN dbo.bill_t_ARTransaction.TransactionDate BETWEEN '2000-01-01' AND '2017-01-31' THEN dbo.bill_t_ARTransaction.Amount ELSE 0 END
) AS 'JAN17'
FROM dbo.bill_t_ARTransaction
INNER JOIN dbo.bill_t_TripTicket ON (
dbo.bill_t_ARTransaction.RunNumber = dbo.bill_t_TripTicket.RunNumber
)
INNER JOIN dbo.med_m_Company ON (
dbo.bill_t_TripTicket.CompanyCode = dbo.med_m_Company.CompanyCode
)
WHERE dbo.bill_t_TripTicket.CompanyCode = '105'
AND dbo.bill_t_ARTransaction.TransactionDate BETWEEN '2000-01-01' AND '2017-01-31'
GROUP BY dbo.bill_t_ARTransaction.TransactionDate
I just formated your SQL - so it will be more readable for human beings - and put it in a Subselect.
In the outer Select i just added the SUM of the two calculated columns
SELECT sums.*
,SUM(sums.DEC16 + sums.JAN17) AS TOTAL_SUM
FROM (
SELECT SUM( CASE WHEN dbo.bill_t_ARTransaction.TransactionDate
BETWEEN '2000-01-01'
AND '2016-12-31'
THEN dbo.bill_t_ARTransaction.Amount
ELSE 0
END
) AS DEC16
,SUM( CASE WHEN dbo.bill_t_ARTransaction.TransactionDate
BETWEEN '2000-01-01'
AND '2017-01-31'
THEN dbo.bill_t_ARTransaction.Amount
ELSE 0
END
) AS JAN17
FROM dbo.bill_t_ARTransaction
INNER JOIN dbo.bill_t_TripTicket
ON dbo.bill_t_ARTransaction.RunNumber = dbo.bill_t_TripTicket.RunNumber
INNER JOIN dbo.med_m_Company
ON dbo.bill_t_TripTicket.CompanyCode = dbo.med_m_Company.CompanyCode
WHERE dbo.bill_t_TripTicket.CompanyCode = '105'
AND dbo.bill_t_ARTransaction.TransactionDate BETWEEN '2000-01-01' AND '2017-01-31'
GROUP BY dbo.bill_t_ARTransaction.TransactionDate
) sums
I think you just want to get right of the GROUP BY:
SELECT SUM(CASE WHEN ba.TransactionDate BETWEEN '2000-01-01' AND '2016-12-31'
THEN ba.Amount ELSE 0
END) AS Dec16,
SUM(CASE WHEN ba.TransactionDate BETWEEN '2000-01-01' AND '2017-01-31'
THEN ba.Amount ELSE 0
END) AS JAN17
FROM dbo.bill_t_ARTransaction ba INNER JOIN
dbo.bill_t_TripTicket bt
ON ba.RunNumber = bt.RunNumber INNER JOIN
dbo.med_m_Company c
ON bt.CompanyCode = c.CompanyCode
WHERE bt.CompanyCode = '105' AND
ba.TransactionDate BETWEEN '2000-01-01' AND '2017-01-31';
Note the other changes I made to the query:
I removed the single quotes from the column aliases. Single quotes should only be used for string and date values (using them for column aliases is allowed but can cause confusion).
The tables are given aliases.
The column names are qualified with the aliases (the query is easier to write and to read.
Note that '105' should not have quotes, if CompanyCode is numeric.
I think the query can be simplified to:
SELECT SUM(CASE WHEN ba.TransactionDate BETWEEN '2000-01-01' AND '2016-12-31'
THEN ba.Amount ELSE 0
END) AS Dec16,
SUM(CASE WHEN ba.TransactionDate BETWEEN '2000-01-01' AND '2017-01-31'
THEN ba.Amount ELSE 0
END) AS JAN17
FROM dbo.bill_t_ARTransaction ba INNER JOIN
dbo.bill_t_TripTicket bt
ON ba.RunNumber = bt.RunNumber
WHERE bt.CompanyCode = 105 AND
ba.TransactionDate BETWEEN '2000-01-01' AND '2017-01-31';
The Company table does not appear to be being used.
You can do this:
SELECT DATENAME(MONTH, Transaction.TransactionDate) + RIGHT(YEAR(Transaction.TransactionDate), 2) AS MonthYear
, SUM(Transaction.Amount) AS Amount
FROM dbo.bill_t_ARTransaction Transaction
INNER JOIN dbo.bill_t_TripTicket Ticket
ON Transaction.RunNumber = Ticket.RunNumber
INNER JOIN dbo.med_m_Company Company
ON Ticket.CompanyCode = Company.CompanyCode
WHERE Ticket.CompanyCode = '105'
AND Transaction.TransactionDate >= '2000-01-01'
AND Transaction.TransactionDate < '2017-02-01'
GROUP BY DATENAME(MONTH, Transaction.TransactionDate) + RIGHT(YEAR(Transaction.TransactionDate), 2)
Assuming dbo.bill_t_ARTransaction.TransactionDate is a datetime(2): Are you aware that in 'DEC16' you are missing transaction from (as an example) 2016-12-31 11:00:00? BETWEEN is inclusive on both sides, and dates are defaulted to midnight (00:00:00) if no time component is defined. I altered the WHERE clause accordingly. I also added aliases to help the readability.

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.

most recent entry made in table bases on one year interval mysql

Using the following sqlfiddle here How would I find the most recent payment made between the months of 2012-04-1 and 2012-03-31 using the case statement as in the previous queries
I tried this:
max(case when py.pay_date >= STR_TO_DATE(CONCAT(2012, '-04-01'),'%Y-%m-%d') and py.pay_date <= STR_TO_DATE(CONCAT(2012, '-03-31'), '%Y-%m-%d') + interval 1 year then py.amount end) CURRENT_PAY
However the answer I am getting is incorrect, where the actual answer should be:(12, '2012-12-12', 20, 1)
Please Provide me with some assistance, thank you.
Rather than a CASE inside your MAX() aggregate, that condition belongs in the WHERE clause. This joins against a subquery which pulls the most recent payment per person_id by joining on MAX(pay_date), person_id.
SELECT payment.*
FROM
payment
JOIN (
SELECT MAX(pay_date) AS pay_date, person_id
FROM payment
WHERE pay_date BETWEEN '2012-04-01' AND DATE_ADD('2012-03-31', INTERVAL 1 YEAR)
GROUP BY person_id
) maxp ON payment.person_id = maxp.person_id AND payment.pay_date = maxp.pay_date
Here is an updated fiddle with the ids corrected in your table (since a bunch of them were 15). This returns record 18, for 2013-03-28.
Update
After seeing the correct SQL fiddle... To incorporate the results of this query into your existing one, you can LEFT JOIN against it as a subquery on p.id.
select p.name,
v.v_name,
sum(case when Month(py.pay_date) = 4 then py.amount end) april_amount,
(case when max(py.pay_date)and month(py.pay_date)= 4 then py.amount else 0 end) max_pay_april,
sum(case
when Month(py.pay_date) = Month(curdate())
then py.amount end) current_month_amount,
sum(case
when Month(py.pay_date) = Month(curdate())-1
then py.amount end) previous_month_amount,
maxp.pay_date AS last_pay_date,
maxp.amount AS last_pay_amount
from persons p
left join vehicle v
on p.id = v.person_veh
left join payment py
on p.id = py.person_id
/* LEFT JOIN against the subquery: */
left join (
SELECT MAX(pay_date) AS pay_date, amount, person_id
FROM payment
WHERE pay_date BETWEEN '2012-04-01' AND DATE_ADD('2012-03-31', INTERVAL 1 YEAR)
GROUP BY person_id, amount
) maxp ON maxp.person_id = p.id
group by p.name,
v.v_name