Total the aggregate in Select Case Statement - sql-server-2008

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.

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?

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.

SQL statement for GROUP BY

I am really stucked with one sql select statement.
This is output/result which I get from sql statement below:
WHAT I need: I need to have columns assignedVouchersNumber and usedVouchersNumber together in one row by msisdn. So for example if you can see "msisdn" 723709656 there are two rows now.. one with assignedVouchersNumber = 1 and second with assignedVouchersNumber = 1 too.
But I need to have it in one row with assignedVouchersNumber = 2. Do you now where is the problem?
SELECT eu.msisdn,
eu.id as userId,
sum(case ev.voucherstate when '1' then 1 else 0 end) as assignedVouchersNumber,
sum(case ev.voucherstate when '2' then 1 else 0 end) as usedVouchersNumber,
ev.extra_offer_id,
ev.create_time,
ev.use_time,
ev.id as voucherId,
ev.voucherstate
FROM extra_users eu
JOIN (SELECT sn.msisdn AS telcislo,
stn.numberid
FROM stats_number sn
JOIN stats_target_number AS stn
ON ( sn.numberid = stn.numberid )
WHERE stn.targetid = 1) xy
ON eu.msisdn = xy.telcislo
JOIN extra_vouchers AS ev
ON ( eu.id = ev.extra_user_id )
WHERE ev.create_time BETWEEN '2012-07-23 00:00:00' AND '2013-08-23 23:59:59'
AND ev.use_time <= '2013-08-23 23:59:59'
AND ev.use_time >= '2012-07-23 00:00:00'
AND ev.voucherstate IN ( 1, 2 )
AND Ifnull(ev.extra_offer_id IN( 2335, 3195, 30538 ), 1)
GROUP BY eu.msisdn, ev.extra_offer_id, ev.voucherState
ORDER BY eu.msisdn ASC
You have two different extra_offer_id for same msisdn and VouchersNumber. Thats why you get two rows.
I got it... there should not be groupping by ev.voucherState in
GROUP BY eu.msisdn, ev.extra_offer_id, ev.voucherState
After then I have removed ev.voucherState it is working now.

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

how to join after left join complex mysql queries

I have this query
SELECT
currency_code,
SUM(CASE WHEN TYPE = 'buy'THEN to_amount END ) AS BUY,
SUM(CASE WHEN TYPE = 'sell' THEN to_amount END ) AS SELL,
SUM(CASE WHEN TYPE = 'sell' THEN rate END ) AS SELL_RATE,
SUM(CASE WHEN TYPE = 'buy' THEN rate END ) AS BUY_RATE,
AVG(CASE WHEN TYPE = 'buy' THEN rate END ) AS AVG_BUY_RATE,
AVG(CASE WHEN TYPE = 'sell' THEN rate END ) AS AVG_SELL_RATE
FROM tb_currency
LEFT JOIN tb_bill
ON tb_currency.CURRENCY_ID = tb_bill.CURRENCY_ID
AND tb_bill.TYPE IN ('buy', 'sell')
AND date( DATE_TIME ) >= '2011-01-01'
AND date( DATE_TIME ) <= '2011-01-11'
GROUP BY currency_code
that will output this:
Right now i want to join this query with another table called tb_user
the tb_user have PK called user_id and the tb_bill that is use in the query above also have foreign key called user_id
tb_user
user_id (pk)| user_name | branch_id
tb_bill
bill_id (pk) | user_id (fk)|
Desired result should be the above picture plus one column branch_id.
If it doesnt have branch_id, return null.
I tried several times but still cant join it correctly. Hope you guys can help.
Thanks.
The three conditions in the join (the AND clauses) might be giving you trouble. Those three conditions are selection criteria, not join criteria.
Also, your use of CASE looks odd to me. I'm sure it works, but IF might be better suited for a one-condition function. In the below, if the fields are floating point rather than integer then replace the 0 with 0.0.
SELECT currency_code,
SUM(IF(TYPE = 'buy', to_amount, 0)) AS BUY,
SUM(IF(TYPE = 'sell', to_amount, 0)) AS SELL,
SUM(IF(TYPE = 'sell', rate, 0)) AS SELL_RATE,
SUM(IF(TYPE = 'buy', rate, 0)) AS BUY_RATE,
AVG(IF(TYPE = 'buy', rate, 0)) AS AVG_BUY_RATE,
AVG(IF(TYPE = 'sell', rate, 0)) AS AVG_SELL_RATE,
tb_user.whatever_field,
tb_user.whatever_other_field
FROM tb_currency
LEFT JOIN tb_bill ON tb_currency.CURRENCY_ID = tb_bill.CURRENCY_ID
LEFT JOIN tb_user ON tb_bill.user_id = tb_user.user_id
WHERE tb_bill.TYPE IN ('buy', 'sell')
AND date( DATE_TIME ) >= '2011-01-01'
AND date( DATE_TIME ) <= '2011-01-11'
GROUP BY currency_code, tb_user.user_id
Finally, all-cap field names look odd to my eye as well. Whatever works for you though.
add user_id to SELECT part
after
LEFT JOIN tb_bill ON tb_currency.CURRENCY_ID = tb_bill.CURRENCY_ID
place
LEFT JOIN tb_user ON tb_user.id = tb_bill.user_id
also you missing WHERE ( put instead first AND )
and
GROUP BY currency_code, user_id