I have two queries:
select
CM.Month,
CM.Year
FROM CalendarMonths AS CM
The above query returns all montts and years from available in a Calendar Table.
The second query is as follows:
select
DATEPART(month, T.Date) AS Month,
DATEPART(year, T.Date) AS Year,
ISNULL(SUM(Amount) ,0) As Total
from Transactions T
inner join TransactionClasses TC on TC.TransactionClassId = T.TransactionClassId AND T.TransactionClassId = 3
GROUP BY
DATEPART(month, T.Date),
DATEPART(year, T.Date)
This simply returns the the total for Transactions that belong to a specific Transaction Class grouped by month and year. The above query only returns rows for months and years which actually contain transactions.
What I am attempting to accomplish is combining both queries, so that as well returning totals for month and year which contains Transactions, it also returns zero total for the rest of the months.
My attempts so far have been unsuccesfull so any help would be appreciated.
You need to LEFT JOIN to your data set. I would put your second query into a CTE/derived table, and that makes the JOINing easier:
WITH Totals AS(
SELECT DATEPART(MONTH, T.Date) AS Month,
DATEPART(YEAR, T.Date) AS Year,
SUM(TC.Amount) AS Total
FROM dbo.Transactions T
INNER JOIN dbo.TransactionClasses TC ON TC.TransactionClassId = T.TransactionClassId
WHERE T.TransactionClassId = 3 --Moved from ON
GROUP BY DATEPART(MONTH, T.Date),
DATEPART(YEAR, T.Date))
SELECT CM.Month,
CM.Year,
ISNULL(T.Total,0) AS Total
FROM dbo.CalendarMonths CM
LEFT JOIN Totals T ON CM.Month = T.Month
AND CM.Year = T.Year;
Related
I am attempting to get the total of a column (Amount) by Month:
SELECT
Date,
DATEPART(month, Date) AS Month,
DATEPART(year, Date) AS year,
(
SELECT
SUM(Amount)
FROM Transactions AS T
WHERE DATEPART(month, Date) = DATEPART(month, T.Date)
) AS Amount
FROM Transactions AS T
I know that this could be done in a straightforward manner using a group by clause as follows:
SELECT
DATEPART(month, Date) AS Month,
SUM(Amount) AS Total
FROM Transactions AS T
GROUP BY DATEPART(month, Date)
However I need to use subquery instead as the subquery will contain additional conditions and joins to assist in the calculation of a total based on rules from other tables.
I expected the first query to return all months and their respective totals, in the subquery I am matching Month of the transactions with the Month of the outer query row. However it simply returns the full SUM of all months for each month.
My guess is that my attempt to match the month is not quite correct, but I can't see how to fix.
I'm using SQL Server. I've a following table Orders:
Orders (Id, ItemId, CustomerId, Quantity, OrderDateTime)
I want to count the number of orders each month. I've written 2 of the following query.
Query #1:
SELECT
MONTH(OrderDateTime) AS MonthCol,
YEAR(OrderDateTime) AS YearCol,
COUNT(id) AS OrderCount
FROM
Orders
WHERE
OrderDateTime >= '2000' AND OrderDateTime <= '2018'
GROUP BY
YEAR(OrderDateTime), MONTH(OrderDateTime)
ORDER BY
YearCol, MonthCol
Query #2:
SELECT
DATEPART(mm, OrderDateTime) AS Month,
COUNT(*) AS OrderCount
FROM
Orders
WHERE
OrderDateTime >= '2000' AND OrderDateTime <= '2018'
GROUP BY
DATEPART(mm, OrderDateTime)
Issue with both queries is that I'm not getting the columns with 0 orders. How will I get it?
SQL will not give you data about months and year which do not exist as rows. To get 0 order rows you'd need to right join the results with a calendar table containing all needed months and years or you can also use a tally table.
Select T.MonthCol, T.YearCol,OrderCount= COALESCE(OrderCount,0)
from
(
SELECT MONTH(OrderDateTime) AS MonthCol, YEAR(OrderDateTime) AS YearCol, count(id) AS OrderCount
FROM Orders
WHERE OrderDateTime >= '2000' AND OrderDateTime <= '2018'
GROUP BY YEAR(OrderDateTime), MONTH(OrderDateTime)
ORDER BY YearCol, MonthCol)
P
RIGHT JOIN
(
select * from
( values (2000),(2001),(2002),(2003),(2004),(2005),(2006),(2007),(2008))v(YearCol)
cross join
( values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12))u(MonthCol)
)T
on P.MonthCol=T.MonthCol
and P.YearCol=T.YearCol
I would be inclined to use a recursive CTE for this -- this gives pretty easy flexibility on the range you want:
with dates as (
select cast('2000-01-01' as date) dte
union all
select dateadd(month, 1, dte)
from dates
where dte < '2018-12-01'
)
select year(OrderDateTime) AS year,
month(OrderDateTime) AS month,
count(o.id) as OrderCount
from dates left join
orders o
on d.OrderDateTime >= dates.dte and
d.OrderDateTime < dateadd(month, 1, dates.dte)
group by year(OrderDateTime), month(OrderDateTime)
order by year(OrderDateTime), month(OrderDateTime)
option (maxrecursion 0);
Notes:
This uses the JOIN to do the filtering. This makes it safer to change the range that you are looking for.
I find the year() and month() functions to be more convenient datepart().
When using date parts, spell them out. Why waste brain power trying to remember if mm really means months or minutes?
I added an order by. Presumably you want the results in chronological order.
I have 3 queries that counts for each company, number of rows during certain month, from 3 different tables, and returning the same columns :qty, month and company_name.
Instead of this I need to return 1 table with same 3 columns but qty must sum the value of all the 3 separated queries.
Can you suggest the best way to join or execute it in which I will not loose speed of execution.
Here is example of one of queries, the other 2 queries has exactly the same syntax, just instead T_CUSTSK, they use T_CUSTSK2 and T_CUSTSK3:
SELECT
COUNT(*) as qty,
DATEPART (MONTH, [start_date]) AS [month],
T_SYSCOM.company_name
FROM
T_CUSTSK
INNER JOIN
T_SYSCOM ON T_CUSTSK.company_id = T_SYSCOM.company_id
WHERE
DATEPART (MONTH, [start_date]) = 12
GROUP BY
DATEPART (MONTH, [start_date]), T_SYSCOM.company_name
ORDER BY
month, qty DESC
Union all will combine your queries.
You can then wrap your statement with another query that sums:
select sum(qty), month, company_name from (
Select count(*) as qty, datepart(month, [start_date]) as [month], T_SYSCOM.company_name
from T_CUSTSK
INNER JOIN T_SYSCOM
ON T_CUSTSK.company_id=T_SYSCOM.company_id
where datepart(month, [start_date])=12
group by datepart(month, [start_date]), T_SYSCOM.company_name
order by month, qty DESC
union all
<second query>
union all
<third query> )
group by month, company_name
Try this you could do UNION ALL of all the T_CUSTSKx tables in the subquery and join that result once with T_SYSCOM table.
I'm assuming the start_date is in T_CUSTSK table
Select count(*) as qty, datepart(month, [start_date]) as [month], T2.company_name
from (
SELECT company_id,start_date FROM T_CUSTSK
UNION ALL
SELECT company_id,start_date FROM T_CUSTSK2
UNION ALL
SELECT company_id,start_date FROM T_CUSTSK3
) T1
INNER JOIN #T_SYSCOM T2
ON T1.company_id=T2.company_id
where datepart(month, [start_date])=12
group by datepart(month, [start_date]), T2.company_name
order by month, qty DESC
kindly let me know if this works
I have a table called SOITEM. In that table the column TOTALPRICE has to be summed and result in the total sales by month, where the column with the dates is called DATELASTFULFILLMENT.
I want to compare sales form Jan 2014 with Jan 2015, then Feb 2014 with Feb 2015 and so forth.
I got this so far, but I'm not sure how continue.
Select SUM(SOITEM.TOTALPRICE)
FROM SOITEM
WHERE DATELASTFULFILLMENT>='2014-01-31' AND DATELASTFULFILLMENT<='2014-01-31'
but it only results in totals from Jan 2014....
Thank you.
You could consider grouping your results using the Month/Year from your date field and then using calculating the SUM() for each of those groups :
SELECT DATEPART(Year, DATELASTFULFILLMENT) AS [Year],
DATEPART(Month, DATELASTFULFILLMENT) AS [Month],
SUM(TOTALPRICE) AS Total
FROM SOITEM
GROUP BY DATEPART(Year, DATELASTFULFILLMENT), DATEPART(Month, DATELASTFULFILLMENT)
ORDER BY [Year], [Month]
You can see an interactive example of this here and results demonstrated below :
This works for MySQL. I assume should be the same for MS SQL.
Select SUM(SOITEM.TOTALPRICE)
FROM SOITEM
WHERE DATELASTFULFILLMENT>='2014-01-31' AND DATELASTFULFILLMENT<='2014-01-31'
UNION
Select SUM(SOITEM.TOTALPRICE)
FROM SOITEM
WHERE DATELASTFULFILLMENT>='2015-01-31' AND DATELASTFULFILLMENT<='2015-01-31'
UNION
Select SUM(SOITEM.TOTALPRICE)
FROM SOITEM
WHERE DATELASTFULFILLMENT>='2014-02-31' AND DATELASTFULFILLMENT<='2014-02-31'
UNION
Select SUM(SOITEM.TOTALPRICE)
FROM SOITEM
WHERE DATELASTFULFILLMENT>='2014-02-31' AND DATELASTFULFILLMENT<='2015-02-31'
Use a nested query within your select statement -- notice the subtraction from the YEAR within the nested query to pull back the previous year's summary:
SELECT MONTH(so2.DATELASTFULFILLMENT) AS MonthFulfilled,
YEAR(so2.DATELASTFULFILLMENT) AS YearFulfilled,
SUM(so2.TOTALPRICE),
(SELECT SUM(SOITEM.TOTALPRICE) FROM SOITEM WHERE MONTH(DATELASTFULFILLMENT) = MONTH(so2.DATELASTFULFILLMENT) AND YEAR(DATELASTFULFILLMENT) = (YEAR(so2.DATELASTFULFILLMENT)-1)) AS LastYearTotal
FROM SOITEM AS so2
GROUP BY MONTH(so2.DATELASTFULFILLMENT), YEAR(so2.DATELASTFULFILLMENT)
Or you could do it like this.
WITH MonthTotal AS
(
SELECT
DATEADD(MONTH, DATEDIFF(MONTH, 0, DATELASTFULFILLMENT), 0) AS MonthDate
, SUM(TOTALPRICE) AS Total
FROM
SOITEM
GROUP BY
DATEADD(MONTH, DATEDIFF(MONTH, 0, DATELASTFULFILLMENT), 0)
)
SELECT
MonthTotal.MonthDate
, MonthTotal.Total
, PreviousYear.Total AS PreviousYearTotal
FROM
MonthTotal
LEFT JOIN MonthTotal AS PreviousYear
ON DATEADD(YEAR, -1, MonthTotal.MonthDate) = PreviousYear.MonthDatee) = PreviousYear.MonthDate
You first group the results based on themonth date, the calculation converts a date to the 1st of the month the date drops in. We then use these results and join back to it getting last years result as well.
select datepart(year, data) Year, DATEPART(month, data) Month,
Coalesce(sum(number),0) as sum
from drugsEdition inner join
visit
on drugsEdition.idVisit = visit.id inner join
drugsSeries
on drugsEdition.idDrugSeries = drugsSeries.id inner join
drugs
on drugsSeries.idDrugs = drugs.id
where idDrugs = 153 or idDrugs = 1241
group by DATEPART(year, data), DATEPART(month, data)
order by Year, Month
Hello,
I have a problem, how i can return 0 in this case. I want to return the sum number of drugs used in each month, but this code does not return the months with result 0. example:
Year Month sum
2014 3 3,9
2014 4 2,8
2014 5 0,7
2014 8 2,6
2014 9 0,5
2014 10 2,4
month 7 isn't here
There are two basic approach to this. One is to use a list of valid months and then left join to include all of them. The is the more general purpose method -- although somehow you need to generate the list of months.
In your case, though, it is quite possible that there are records in the table for each month, but they are filtered out in the where clause. If so, you can use conditional aggregation:
select datepart(year, data) as yyyy, DATEPART(month, data) as mon,
sum(case when idDrugs in (153, 1241) then number else 0 end) as Total
from drugsEdition inner join
visit
on drugsEdition.idVisit = visit.id inner join
drugsSeries
on drugsEdition.idDrugSeries = drugsSeries.id inner join
drugs
on drugsSeries.idDrugs = drugs.id
group by DATEPART(year, data), DATEPART(month, data)
order by yyyy, mon;
Note that column names such as year, month, and sum are bad choices. These are SQL keywords or function names. It is better to steer clear of the handful of names reserved by the language.