SQL statement for GROUP BY - mysql

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.

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?

Query using group by month

I have problem with the query using group by month. This query returns total_revenue per month. but if month of year doesn't contain any data then total_revnue is increased unnecessarily.
SELECT COUNT(CT.cumTxnReportId),
CT.cumTxnReportId,
CT.ticketNum,
DATE_FORMAT(CT.exitDateTimeUtc,'%m-%Y'),
sum(netAmount) AS total_revenue,
D.name,
HOUR(CT.entranceDateTimeUtc) AS entryHour,
HOUR(CT.exitDateTimeUtc) AS exitHour,
CT.entranceDateTimeUtc,
CT.exitDateTimeUtc,
CT.netAmount AS netAmount,
CT.grossAmount,
CT.discountAmount,
CT.rate,
CT.txnType,
CT.ticketType,
CT.txnNum,
CT.numDiscounts
FROM Parkloco.ParkingArea PA
JOIN IParcPro.Device D ON PA.id = D.parkingAreaId
JOIN Parkloco.RateCard RC ON PA.id = RC.parkingAreaId
JOIN IParcPro.CumTxn CT ON D.id = CT.deviceId
WHERE PA.uuid = '27d842c1-7057-11e6-a0eb-1245b0d35d23'
AND (CT.txnType = 'Allowed'
OR CT.txnType = 'Add'
OR CT.txnType = 'Normal'
OR CT.txnType = 'Offline'
OR CT.txnType = 'Repay')
AND ((CT.entranceDateTimeUtc >= '2016-08-01 00:00:00'
AND CT.exitDateTimeUtc <= '2017-04-31 23:59:59'))
AND (RC.state = 'active'
OR RC.state = 'archived')
AND RC.fromDateTimeUtc <= '2017-04-31 23:59:59'
AND (RC.thruDateTimeUtc IS NULL
OR RC.thruDateTimeUtc >= '2016-08-01 00:00:00')
AND (TIMESTAMPDIFF (SECOND, CT.entranceDateTimeUtc, CT.exitDateTimeUtc) >= '0' * 60)
AND (TIMESTAMPDIFF (SECOND, CT.entranceDateTimeUtc, CT.exitDateTimeUtc) < '1441' * 60)
AND CT.numDiscounts=0
AND CT.ticketNum !=0
GROUP BY DATE_FORMAT(CT.exitDateTimeUtc,'%m-%Y')
but when I am increasing the range month - at that point of time I am getting unneccessary increment in total_revenue
SELECT COUNT(CT.cumTxnReportId),
CT.cumTxnReportId,
CT.ticketNum,
DATE_FORMAT(CT.exitDateTimeUtc,'%m-%Y'),
sum(netAmount) AS total_revenue,
D.name,
HOUR(CT.entranceDateTimeUtc) AS entryHour,
HOUR(CT.exitDateTimeUtc) AS exitHour,
CT.entranceDateTimeUtc,
CT.exitDateTimeUtc,
CT.netAmount AS netAmount,
CT.grossAmount,
CT.discountAmount,
CT.rate,
CT.txnType,
CT.ticketType,
CT.txnNum,
CT.numDiscounts
FROM Parkloco.ParkingArea PA
JOIN IParcPro.Device D ON PA.id = D.parkingAreaId
JOIN Parkloco.RateCard RC ON PA.id = RC.parkingAreaId
JOIN IParcPro.CumTxn CT ON D.id = CT.deviceId
WHERE PA.uuid = '27d842c1-7057-11e6-a0eb-1245b0d35d23'
AND (CT.txnType = 'Allowed'
OR CT.txnType = 'Add'
OR CT.txnType = 'Normal'
OR CT.txnType = 'Offline'
OR CT.txnType = 'Repay')
AND ((CT.entranceDateTimeUtc >= '2016-08-01 00:00:00'
AND CT.exitDateTimeUtc <= '2017-07-31 23:59:59'))
AND (RC.state = 'active'
OR RC.state = 'archived')
AND RC.fromDateTimeUtc <= '2017-07-31 23:59:59'
AND (RC.thruDateTimeUtc IS NULL
OR RC.thruDateTimeUtc >= '2016-08-01 00:00:00')
AND (TIMESTAMPDIFF (SECOND, CT.entranceDateTimeUtc, CT.exitDateTimeUtc) >= '0' * 60)
AND (TIMESTAMPDIFF (SECOND, CT.entranceDateTimeUtc, CT.exitDateTimeUtc) < '1441' * 60)
AND CT.numDiscounts=0
AND CT.ticketNum !=0
GROUP BY DATE_FORMAT(CT.exitDateTimeUtc,'%m-%Y')
output such as :
can anyone help me on this? Thanks in advance if you could let me know.
Despite MySQL allow this weird group by rules, in my opinion, you should to avoid use it. I explain, usually, all select clause non aggregate fields should appear on group by clause:
select a,b,c, sum(z)
from t
group by a,b,c
vs
select a,b,c, sum(z)
from t
group by a #<--- MySQL allow this!
Then, if b and c are not in group by, how MySQL figure up the right fields to be selected? Like this on <5.6:
The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate.
In my opinion, in your query has no sense: Look entryHour and total_revenue. One is for an entry the other one is for all month.
I guess you should to rethink the hole sql statement. Because the result of this one is incoherent.
Also, remember this is not 'code revision service'. Please, read how to create a Minimal, Complete, and Verifiable example in order your question also help other users.

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 get SUM() of COUNT() column in MySQL

i have the following query, tried using case statement
SELECT t.inst_id, t.inst_username, tcm.city_name,
SUM(CASE WHEN psb.pms_student_bucket_id IS NULL THEN 1 ELSE 0 END) AS not_assiged,
SUM(CASE WHEN COUNT(psb.pms_student_bucket_id) BETWEEN 1 AND 50 THEN 1 ELSE 0 END) AS '1-50',
SUM(CASE WHEN COUNT(psb.pms_student_bucket_id) > 50 THEN 1 ELSE 0 END) AS ' > 50'
FROM tbl_si_di t
JOIN tbl_city_master tcm ON tcm.city_id = t.city_ref_id
JOIN tbl_si_students tss ON tss.inst_ref_id = t.inst_id
LEFT JOIN pms_student_bucket psb ON psb.user_ref_id = tss.user_ref_id
GROUP BY t.inst_id;
I need SUM of pms_student_bucket_id column when their COUNT is '1-50' and '>50'. Right now this query is saying Invalid use of group function.
How would I SUM on COUNT of "pms_student_bucket_id" equals/between some value in mysql?
you could put it in a subquery
SELECT inst_id, inst_username, city_name,
SUM(pms_student_bucket_id IS NULL ) AS not_assiged,
SUM(num_bucket >=10 AND num_bucket <= 20) AS '10 - 20'
SUM(num_bucket <= 50) AS '1-50',
SUM(num_bucket > 50) AS ' > 50'
FROM
( SELECT t.inst_id, t.inst_username, tcm.city_name,psb.pms_student_bucket_id,
COUNT(psb.pms_student_bucket_id) as num_bucket
FROM tbl_si_di t
JOIN tbl_city_master tcm ON tcm.city_id = t.city_ref_id
JOIN tbl_si_students tss ON tss.inst_ref_id = t.inst_id
LEFT JOIN pms_student_bucket psb ON psb.user_ref_id = tss.user_ref_id
GROUP BY t.inst_id
)t1
GROUP BY inst_id;
note you can use the boolean values returned to do what you want.. aka you don't need a case statement, the boolean value returns a 1 or 0 which can then be summed..

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