How to two select statements with sum functions in them - mysql

I'm trying to join these two tables and I get an error when I use any join. Union will work but returns all the values in two rows as opposed to 3 which I need. This is what I have so far and what it returns is below it. Any help would be great :)
select C.CustId ,SUM(Ia.Amount) as total
from Invoice I
inner join InvoiceAmtSummary Ia on I.GUIDInvoice=Ia.GUIDInvoice
inner join Customer C on C.GUIDCustomer=I.GUIDCustomer
WHERE DATEPART(mm, I.InvoiceDate) = DATEPART(mm, DATEADD(mm, -1, getdate()))
and DATEPART(yyyy, I.InvoiceDate) = DATEPART(yyyy, DATEADD(yyyy, -0, getdate()))
group by C.CustId
union
select C.CustId ,SUM(Ia.Amount) as total2
from Invoice I
inner join InvoiceAmtSummary Ia on I.GUIDInvoice=Ia.GUIDInvoice
inner join Customer C on C.GUIDCustomer=I.GUIDCustomer
WHERE DATEPART(mm, I.InvoiceDate) = DATEPART(mm, DATEADD(mm, -2, getdate()))
and DATEPART(yyyy, I.InvoiceDate) = DATEPART(yyyy, DATEADD(yyyy, -0, getdate()))
group by C.CustId
The returned result is below with the required result below that
Custid total
a 2
b 9
b 12
c 16
d 3
d 12
Custid total total2
a 2
b 9 12
c 16
d 3 12

select c.custid, c.total, c2.total2
from (select C.CustId ,SUM(Ia.Amount) as total
from Invoice I
inner join InvoiceAmtSummary Ia on I.GUIDInvoice=Ia.GUIDInvoice
inner join Customer C on C.GUIDCustomer=I.GUIDCustomer
WHERE DATEPART(mm, I.InvoiceDate) = DATEPART(mm, DATEADD(mm, -1, getdate()))
and DATEPART(yyyy, I.InvoiceDate) = DATEPART(yyyy, DATEADD(yyyy, -0, getdate()))
group by C.CustId) c
inner join
(select C.CustId ,SUM(Ia.Amount) as total2
from Invoice I
inner join InvoiceAmtSummary Ia on I.GUIDInvoice=Ia.GUIDInvoice
inner join Customer C on C.GUIDCustomer=I.GUIDCustomer
WHERE DATEPART(mm, I.InvoiceDate) = DATEPART(mm, DATEADD(mm, -2, getdate()))
and DATEPART(yyyy, I.InvoiceDate) = DATEPART(yyyy, DATEADD(yyyy, -0, getdate()))
group by C.CustId) c2 on c.custid = c2.custid

I think you are trying to have 2 columns of SUM of different Months. Here is what you can do:
Select C.CustId ,
Sum(Case DATEPART(mm, I.InvoiceDate)
When DATEPART(mm, DATEADD(mm, -1, getdate())) Then Ia.Amount
Else 0 End) As 'Total1',
Sum(Case DATEPART(mm, I.InvoiceDate)
When DATEPART(mm, DATEADD(mm, -2, getdate())) Then Ia.Amount
Else 0 End) As 'Total2'
from Invoice I
inner join InvoiceAmtSummary Ia on I.GUIDInvoice=Ia.GUIDInvoice
inner join Customer C on C.GUIDCustomer=I.GUIDCustomer
WHERE
DATEPART(yyyy, I.InvoiceDate) = DATEPART(yyyy, DATEADD(yyyy, -0, getdate()))
group by C.CustId
I am using Case to avoid the recursive join.

Since you are using MySQL, I would approach by implementing in-line sqlvariables (via my first "from" alias. The #FirstOfThisMonth is computed by subtracting whatever the current day of month is from the current date (less 1).
Ex: Today is April 3rd (3rd day of month). So, -3 = March 31... PLUS 1 brings back to April 1 of current month. Once that date is determined, I can now subtract an entire month for first of PREVIOUS month (March 1st), and subtract 2 months for Feb 1st.
Now that this complexity is done ONCE, we can use these variables instead of constant datepart extraction for month and year. Especially if you consider trying to do two months ago from Feb 10th. (First of current is Feb 1, one month back is Jan 1, two months back is Dec 1 of prior year).
NOW, I simply SUM() based on a case when condition.
if the Invoice Date is >= #TwoMonthsAgo AND LESS THAN #LastMonth, that goes into the two-months-ago total.
the next is Invoice Date >= #LastMonth AND LESS THAN #FirstOfThisMonth for last month's total.
Now, you have a rolling two-month window... If you wanted to go longer, just add another monthly variable reference and SUM() its criteria.
Finally the WHERE clause can only include invoices that are greater or equal to the #TwoMonthsAgo so it doesn't have to go through the entire database
select
C.CustId,
SUM( CASE when I.InvoiceDate >= #TwoMonthsAgo AND I.InvoiceDate < #LastMonth
then Ia.Amount else 0 end ) as TotalTwoMonthsAgo,
SUM( CASE when I.InvoiceDate >= #LastMonth AND I.InvoiceDate < #FirstOfThisMonth
then Ia.Amount else 0 end ) as TotalLastMonth
from
( select #FirstOfThisMonth := date_add( now(), INTERVAL -dayofmonth(now())+1 day ),
#LastMonth := date_add( #myDate, INTERVAL -1 month ),
#TwoMonthsAgo := date_add( #myDate, INTERVAL -2 month )) sqlvars,
Invoice I
inner join InvoiceAmtSummary Ia
on I.GUIDInvoice = Ia.GUIDInvoice
inner join Customer C
on C.GUIDCustomer = I.GUIDCustomer
WHERE
I.InvoiceDate >= #TwoMonthsAgo
AND I.InvoiceDate < #FirstOfThisMonth
group by
C.CustID

Related

MySQL get different SUMs based on dates

I'm trying to create a Sql query where I can sums for 4 different date range.
SELECT t.TenantID, t.TenantFName, t.TenantLName, u.UnitName,
TotalDebit, HousingDebit, TotalCredit, HousingCredit
FROM Tenants t
JOIN Units u ON t.UnitID = u.UnitID
LEFT JOIN (
Select
TenantID,
SUM(CASE WHEN TransactionTypeID = 1 AND ChargeTypeID != 6 THEN TransactionAmount ELSE 0 END) AS TotalDebit,
From TenantTransactions
Where TenantTransactionDate BETWEEN /* Here is my issue */
Group By TenantID
) sums ON sums.TenantID = t.TenantID
Where t.Prospect = 2
AND t.PropertyID = 10
I am trying to return 4 sums:
For the last 30 days
For last 30-60 days
For last 60-90 days
Greater than 90 days ago
Does it make sense?
Thanks
You're looking for totals based on the age of the transaction. We can get that by subtracting the transaction date from today to get a result in days:
datediff(now(), TenantTransactionDate)
You want to group that into period by 30 days, so we use integer division to get a period:
datediff(now(), TenantTransactionDate) DIV 30
And lastly, you want everything over 90 days grouped together:
if(datediff(now(), TenantTransactionDate)<90, datediff(now(), TenantTransactionDate) DIV 30, 3)
This last expression returns a value of 0, 1, 2 or 3. We can use that to group the transactions, and total them:
SELECT TenantID,
sum(TransactionAmount) as totalDebit,
if(datediff(now(), TenantTransactionDate)<90, datediff(now(), TenantTransactionDate) DIV 30, 3) as period
FROM TenantTransactions
where TransactionTypeID=1 and chargeTypeID != 6
group by tenantID, period
order by tenantID, period
Now you should be able to plug that back into your original query, and sort them:
SELECT t.TenantID, t.TenantFName, t.TenantLName, u.UnitName,
TotalDebit, HousingDebit, TotalCredit, HousingCredit
FROM Tenants t
JOIN Units u ON t.UnitID = u.UnitID
LEFT JOIN (
SELECT TenantID,
sum(TransactionAmount) as totalDebit,
if(datediff(now(), TenantTransactionDate)<90, datediff(now(), TenantTransactionDate) DIV 30, 3) as period
FROM TenantTransactions
where TransactionTypeID=1 and chargeTypeID != 6
group by tenantID, period
) sums ON sums.TenantID = t.TenantID
Where t.Prospect = 2
AND t.PropertyID = 10
order by t.tenantID, sums.period

How to add month name with 0 total if data not exist in mysql?

I need to fetch data of the user's from created_date to till current data by month name and year with each month's total price.
Try 1:
SELECT MONTHNAME(start_time) month, YEAR(start_time) year, SUM(price) total
FROM table t1
WHERE t1.id= 33
GROUP BY YEAR(start_time), MONTH(start_time);
Output:
month year total
July 2019 360
September 2019 2160
October 2019 360
Expected output:
All month name and total will be 0 if data not exist.
month year total
Jan 2018 0
Feb 2018 0
...
Dec 2018 0
Jan 2019 0
Feb 2019 0
Mar 2019 0
Apr 2019 0
May 2019 0
Jun 2019 0
July 2019 360
Aug 2019 0
Sep 2019 2160
Oct 2019 360
Nov 2019 0
Dec 2019 0
After some RND I found one way and I have also tried that but now works.
Try 2:
SELECT IFNULL(SUM(ri.price),0) AS total, m.month
FROM (
SELECT 'Jan' AS MONTH
UNION SELECT 'Feb' AS MONTH
UNION SELECT 'Mar' AS MONTH
UNION SELECT 'Apr' AS MONTH
UNION SELECT 'May' AS MONTH
UNION SELECT 'Jun' AS MONTH
UNION SELECT 'Jul' AS MONTH
UNION SELECT 'Aug' AS MONTH
UNION SELECT 'Sep' AS MONTH
UNION SELECT 'Oct' AS MONTH
UNION SELECT 'Nov' AS MONTH
UNION SELECT 'Dec' AS MONTH
) AS m
LEFT JOIN table_u pu
ON MONTH(STR_TO_DATE(CONCAT(pu.created_date, '2019'),'%M %Y')) = MONTH(pu.created_date)
AND YEAR(pu.created_date) = '2019'
LEFT JOIN table ri
ON ri.id = pu.id
GROUP BY m.month
ORDER by 1+1;
Here is my refrence link.
Can anyone help me to fix this issue?
Thanks in advance.
Keep using LEFT JOIN (but only once) with a integer generator upto 12 (instead of month names, also easy using within the ORDER BY clause ), since you are able to get the month names by monthname() function. From your second query, I considered the current year. So, use :
SET #year=YEAR(NOW());
SELECT
MONTHNAME(STR_TO_DATE(concat(YEAR(NOW()),',',m.month,',1'),"%Y,%m,%d")) as month,
#year as year , SUM( COALESCE( pu.price, 0) ) as total
FROM (
SELECT rn as month
FROM
(
SELECT #rn := if(#i = #rn, #rn + 1, 1) as rn,
#i := #i+1
FROM information_schema.character_sets
JOIN (SELECT #i := 0, #rn := 0) as q_iter
LIMIT 12
) q
) AS m
LEFT JOIN table_u pu ON MONTH(pu.start_time) = m.month
AND YEAR(pu.start_time) = #year
AND ID = 33
GROUP BY m.month
ORDER by m.month;
Demo 1
Edit ( From the beginning of the year 2018 to the end of the current year ) :
SELECT MONTHNAME(
STR_TO_DATE(
CONCAT(m.year,',',if(mod(m.month,12)=0,12,mod(m.month,12)),',1'),"%Y,%m,%d")
)
as month,
year,
SUM( CASE WHEN YEAR(pu.start_time) = m.year AND MONTH(pu.start_time) = m.month
THEN
COALESCE( pu.price, 0)
ELSE
0
END ) as total
FROM
(
SELECT #i := if( #i = 12 , 1 , #i+1) as month,
#j := #j + 1,
#k:= if( #i = 1 and #j > 12, #k - 1, #k ) as year
FROM information_schema.character_sets
JOIN (SELECT #i := 0, #j := 0, #k:=YEAR(now())) as q_iter
) m
LEFT JOIN table_u pu ON MONTH(pu.start_time) = m.month
AND ID = 33
GROUP BY month, year
HAVING year >= ( SELECT MIN( YEAR(start_time) ) FROM table_u )
ORDER BY year, m.month;
Demo 2
If you have data in your table for all months, but just not for that id, then you can switch to conditional aggregation:
SELECT MONTHNAME(start_time) as month, YEAR(start_time) as year,
SUM(CASE WHEN t1.id = 33 THEN price ELSE 0 END) as total
FROM table t1
GROUP BY YEAR(start_time),MONTHNAME(start_time)
ORDER BY MIN(start_time);
Creating nonexistent year-month pairs by using user variables:
SELECT monthname(str_to_date(concat_ws(',',ym.month,'01,01'),'%m,%d,%y')) month
, ym.year year
, sum(price)
FROM table1 t1
RIGHT JOIN( SELECT #year := if(#month=12, #year+1, #year ) year
, #month := if(#month=12, 1 , #month+1) month
FROM table1
, ( SELECT #startYear := min(start_time)
, #endYear := year(now())
, #month := 12
, #year := min(start_time)-1
FROM table1
) t
WHERE (#year,#month) < (#endYear,12)
) ym
ON ym.year = year(t1.start_time)
AND ym.month = month(t1.start_time)
GROUP BY year(t1.start_time)
, month(t1.start_time)
The ym derived table is to provide year-month pairs starting on min year in table1 upto current year.
The innermost SELECT is for variables initialization.
You can try this:
SELECT to_char(start_time,'month')MONTH,to_char(start_time,'yyyy')YEAR,SUM(price)total
FROM TABLE_NAME
GROUP BY to_char(start_time,'month'),to_char(start_time,'yyyy')
Finally, I got the correct output which I want.
select
DATE_FORMAT(m1, '%M - %Y')
from
(
select
('2013-07-23')
+INTERVAL m MONTH as m1
from
(
select #rownum:=#rownum+1 as m from
(select 1 union select 2 union select 3 union select 4) t1,
(select 1 union select 2 union select 3 union select 4) t2,
(select 1 union select 2 union select 3 union select 4) t3,
(select 1 union select 2 union select 3 union select 4) t4,
(select #rownum:=-1) t0
) d1
) d2
where m1<=NOW()
order by m1

Having a statement and reading the dates from another table

I'm having some trouble with my following query:
SELECT DATE_FORMAT(date, "%Y-%m-%d") date, id, c, FromWhere FROM (
SELECT distinct cl.creationtime date, c.id, 1 c, "Created" as FromWhere -- Current amount of contacts in list
FROM `contactsLists` cl, `contacts` c
WHERE cl.contact=c.id AND c.optin="yes" AND c.restriction=0 AND cl.list = 13
AND cl.creationtime > DATE_SUB(now(), interval 13 MONTH)
UNION
SELECT IFNULL(cl.edittime, NOW()) date, c.id, -1 c, "Deleted from list" as FromWhere -- Deleted from list
FROM `contactsLists` cl, `contacts` c
WHERE cl.contact=c.id AND c.optin="yes" AND c.restriction=0 AND cl.deleted=1 AND cl.list = 13
AND IFNULL(cl.edittime, NOW()) > DATE_SUB(now(), interval 13 MONTH)
UNION
SELECT o.optintime date, c.id, 1 c, "Optin added" as FromWhere -- Optins
FROM `contactsLists` cl, `contacts` c, `optin` o
WHERE cl.contact=c.id AND c.email = o.email AND o.reply="yes" AND c.restriction=0 AND cl.list = 13
AND o.optintime > DATE_SUB(now(), interval 13 MONTH)
UNION
SELECT o.optintime date, c.id, -1 c, "Optout added" as FromWhere -- Optouts
FROM `contactsLists` cl, `contacts` c, `optin` o
WHERE cl.contact=c.id AND c.email = o.email AND o.reply="no" AND c.restriction=0 AND cl.list = 13
AND o.optintime > DATE_SUB(now(), interval 13 MONTH)
ORDER BY date) u;
The problem is that the last 2 unions "Optins and Optouts" are reading the optin status o.reply="no"/"yes". But it reads the statement of the situation as now(). It should be read the optin reply while also checking the date at the same time from the o.optintime table. Is it possible todo so?
Everything else works in the query except for that. The idea of the graph is to show the growth or loss of a client database list.

SQL adding Months

Hello I am trying to get records of certain months. For example for this query I would want the user to see the current month which is "May" and one month from the past which would be "April". I don't want my query to run any other month from that point on. I am stuck with this query and cant figure it out. Basically I need a function in my query to automatically know this month and the 1 month before to show the records. Thank you
DECLARE #Year int
set #Year = 2014
SELECT d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone
, COUNT(CASE WHEN MONTH(c.Funded_date) = 1 THEN 1 ELSE NULL END) January
, COUNT(CASE WHEN MONTH(c.Funded_date) = 2 THEN 1 ELSE NULL END) Feburary
, COUNT(CASE WHEN MONTH(c.Funded_date) = 3 THEN 1 ELSE NULL END) March
, COUNT(CASE WHEN MONTH(c.Funded_date) = 4 THEN 1 ELSE NULL END) April
, COUNT(CASE WHEN MONTH(c.Funded_date) = 5 THEN 1 ELSE NULL END) May
, COUNT(CASE WHEN MONTH(c.Funded_date) = 6 THEN 1 ELSE NULL END) June
, COUNT(CASE WHEN MONTH(c.Funded_date) = 7 THEN 1 ELSE NULL END) July
, COUNT(CASE WHEN MONTH(c.Funded_date) = 8 THEN 1 ELSE NULL END) August
, COUNT(CASE WHEN MONTH(c.Funded_date) = 9 THEN 1 ELSE NULL END) September
, COUNT(CASE WHEN MONTH(c.Funded_date) = 10 THEN 1 ELSE NULL END) October
, COUNT(CASE WHEN MONTH(c.Funded_date) = 11 THEN 1 ELSE NULL END) November
, COUNT(CASE WHEN MONTH(c.Funded_date) = 12 THEN 1 ELSE NULL END) December
, COUNT(*) 'Year to Date'
FROM tdealer a
JOIN tContact b ON a.contact_id = b.contact_id
JOIN tContract c ON a.dealer_id = c.dealer_id
JOIN tCompany d ON c.company_id = d.company_id
where YEAR (c.Funded_date) = #Year
GROUP BY d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone
The trick here is to do two things:
Create an expression which converts any arbitrary DATETIME into the first day of the month in which that date occurred.
Use it appropriately in WHERE and GROUP BY clauses.
The expression is
DATE_FORMAT(whatever, '%Y-%m-01')
This takes, for example '2014-05-28 12:22:30', and turns it into '2014-05-01'. It's really handy to convert one valid DATE to another valid DATE, because then you can use all the good date arithmetic built into MySQL.
So, for example, if you want the first of the month before the present month you use this:
DATE_FORMAT(NOW(), '%Y-%m-01') - INTERVAL 1 MONTH
Here's the outline of your query:
SELECT DATE_FORMAT(c.Funded_date, '%Y-%m-01') AS month_beginning,
d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone,
COUNT(1) AS itemcount
FROM tdealer a
JOIN tContact b ON a.contact_id = b.contact_id
JOIN tContract c ON a.dealer_id = c.dealer_id
JOIN tCompany d ON c.company_id = d.company_id
where c.Funded_date >= DATE_FORMAT(NOW(), '%Y-%m-01') - INTERVAL 1 MONTH
AND c.Funded_date < DATE_FORMAT(NOW(), '%Y-%m-01') + INTERVAL 1 MONTH
GROUP BY DATE_FORMAT(c.Funded_date, '%Y-%m-01'),
d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone
ORDER BY d.name, a.dealer_code, b.last_name, b.city, b.state, b.phone,
DATE_FORMAT(c.Funded_date, '%Y-%m-01')
See how you choose the two-month period? It starts with the first day of the month before the present month, and ends with the first day of next month, but without including that day.
How to do this sort of thing is written up at http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/

showing previous and current month data in table using mysql

I am trying to show three different figures of the same column In a mysql query, I would like to keep one month static: April, so it would be a case like this I want to show The current month, the previous month and the static month of the year I'm working with, in this case let us stick with 2012
Example
Tablename:payment
id , pay_date, amount
1 2012-02-12 1000
2 2012-03-11 780
3 2012-04-15 890
4 2012-05-12 1200
5 2012-06-12 1890
6 2012-07-12 1350
7 2012-08-12 1450
So what I want to do is show the column amount for the month of April as I said I want to keep that row static: 890, the current month lets say the current month is August:1450 and the previous month amount which would be July:1350: so the final result would be something like this:
april_amount current_month_amount previous_month_amount
890 1450 1350
However I'm stuck here:
select amount as april_amount
from payment
where monthname(pay_date) LIKE 'April'
and year(pay_date) LIKE 2012
I hope the question is written clear enough, and thanks alot for the help much appreciated.
If the results can be rows instead of columns:
SELECT MONTHNAME(pay_date), amount FROM payment
WHERE pay_date BETWEEN '2012-04-01'
AND '2012-04-30'
OR pay_date BETWEEN CURRENT_DATE
- INTERVAL DAYOFMONTH(CURRENT_DATE) - 1 DAY
AND LAST_DAY(CURRENT_DATE)
OR pay_date BETWEEN CURRENT_DATE
- INTERVAL DAYOFMONTH(CURRENT_DATE) - 1 DAY
- INTERVAL 1 MONTH
AND LAST_DAY(CURRENT_DATE - INTERVAL 1 MONTH)
See it on sqlfiddle.
I might be way off here. But try:
select top 1
p.amount, c.amount, n.amount
from payment c
inner join payment p ON p.pay_date < c.pay_date
inner join payment n ON n.pay_date > c.pay_date
where monthname(c.paydate) LIKE 'April'
and year(c.pay_date) LIKE 2012
order by p.pay_date DESC, n.pay_date ASC
EDIT, I didnt read your question properly. I was going for previous, current, and next month. 1 minute and I'll try again.
select top 1
p.amount AS april_amount, c.amount AS current_month_amount, n.amount AS previous_month_amount
from payment c
inner join payment p ON monthname(p.pay_date) = 'April' AND year(p.pay_date) = 2012
inner join payment n ON n.pay_date > c.pay_date
where monthname(c.paydate) = monthname(curdate())
and year(c.pay_date) = year(curdate())
order by n.pay_date ASC
This assumes there is only 1 entry per month.
Ok, so i haven't written in mysql for a while. here is what worked for your example data:
select
p.amount AS april_amount, c.amount AS current_month_amount, n.amount AS previous_month_amount
from payment AS c
inner join payment AS p ON monthname(p.pay_date) LIKE 'April' AND year(p.pay_date) LIKE 2012
inner join payment AS n ON n.pay_date < c.pay_date
where monthname(c.pay_date) LIKE monthname(curdate())
and year(c.pay_date) LIKE year(curdate())
order by n.pay_date DESC
limit 1
the previous month table joined is counterintuitively named n, but this works. I verified it in a WAMP install.
To handle aggregates per month you can use subselects. Performance may suffer on very large tables (millions of rows or more).
SELECT SUM( a.amount ) AS april_amount,
(
SELECT SUM( c.amount )
FROM payment c
WHERE MONTH( c.pay_date ) = MONTH( CURDATE( ) )
) AS current_month_amount,
(
SELECT SUM( p.amount )
FROM payment p
WHERE MONTH( p.pay_date ) = MONTH( CURDATE( ) - INTERVAL 1
MONTH )
) AS previous_month_amount
FROM payment a
WHERE MONTHNAME( a.pay_date ) = 'April'
AND YEAR( a.pay_date ) =2012