Joining 3 grouped values - sql

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

Related

T-SQL query to summarize total per month per year, and cumulative amounts to date

I have a database table that captures every Sales Transaction:
Transactions
(
ID INT,
TransactionDate DATETIME,
SalesAmount MONEY
)
I want to write a T-SQL query which returns a report (snapshot sample below). First column it lists the month, next column Total-Sales per month within year, and last column cumulative amount of that year up to this month. Only for year of 2018.
Any thoughts or solutions? Thank you.
Try this:
;with cte as
(
Select
YEAR(TransactionDate) as [Year],
MONTH(TransactionDate) as [Month],
SUM (SalesAmount) as [MonthlySales],
DATEPART(m, TransactionDate) as [MonthNumber]
from Transactions
group by YEAR(TransactionDate), MONTH(TransactionDate)
)
select
a.[Month], a.MonthlySales as [MonthlySales 2018], SUM(b.MonthlySales) as [Cumulative 2018]
from cte a inner join cte b on a.MonthNumber >= b.MonthNumber
WHERE (a.[Year]) = 2018 AND (b.[Year]) = 2018
group by a.[Month], a.MonthlySales
ORDER by a.[Month]
Try this one:
With Q
as
(
Select DatePart(yyyy,TransactionDate) 'Year',DatePart(m,TransactionDate) 'Month', sum(SalesAmount) 'Sales'
From Transactions
Group by DatePart(yyyy,TransactionDate),DatePart(m,TransactionDate)
)
Select q.Year,q.Month,( Select sum(q1.Sales)
From Q q1
Where q1.Year=q.Year
And q1.Month <= q.Month
) 'Cumulative Sale'
From Q q
Order by q.Year,q.Month
You would use aggregation and window functions:
select datename(month, transaction_date) as mon,
sum(salesAmount) as monthly_sales,
sum(salesAumount) over (order by min(transaction_date)) as running_amount
from transactions t
where t.transaction_date >= '2018-01-01' and
t.transaction_date < '2019-01-01'
group by datename(month, transaction_date)
order by min(transaction_date);

Count the number of rows each month in SQL Server

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.

SQL Server: Attempting to output a count with a date

I am trying to write a statement and just a bit puzzled what is the best way to put it together. So I am doing a UNION on a number of tables and then from there I want to produce as the output a count for the UserID within that day.
So I will have numerous tables union such as:
Order ID, USERID, DATE, Task Completed.
UNION
Order ID, USERID, DATE, Task Completed
etc
Above is layout of the table which will have 4 tables union together with same names.
Then statement output I want is for a count of USERID that occurred within the last 24 hours.
So output should be:
USERID--- COUNT OUTPUT-- DATE
I was attempting a WHERE statement but think the output is not what I am after exactly, just thinking if anyone can point me in the right direction and if there is alternative way compared to the union? Maybe a joint could be a better alternative, any help be appreciated.
I will eventually then put this into a SSRS report, so it gets updated daily.
You can try this:
select USERID, count(*) as [COUNT], cast(DATE as date) as [DATE]
from
(select USERID, DATE From SomeTable1
union all
select USERID, DATE From SomeTable2
....
) t
where DATE <= GETDATE() AND DATE >= DATEADD(hh, -24, GETDATE())
group by USERID, cast(DATE as date)
First, you should use union all rather than union. Second, you need to aggregate and use count distinct to get what you want:
So, the query you want is something like:
select count(distinct userid)
from ((select date, userid
from table1
where date >= '2015-05-26'
) union all
(select date, userid
from table2
where date >= '2015-05-26'
) union all
(select date, userid
from table3
where date >= '2015-05-26'
)
) du
Note that this hardcodes the date. In SQL Server, you would do something like:
date >= cast(getdate() - 1 as date)
And in MySQL
date >= date_sub(curdate(), interval 1 day)
EDIT:
I read the question as wanting a single day. It is easy enough to extend to all days:
select cast(date as date) as dte, count(distinct userid)
from ((select date, userid
from table1
) union all
(select date, userid
from table2
) union all
(select date, userid
from table3
)
) du
group by cast(date as date)
order by dte;
For even more readability, you could use a CTE:
;WITH cte_CTEName AS(
SELECT UserID, Date, [Task Completed] FROM Table1
UNION
SELECT UserID, Date, [Task Completed] FROM Table2
etc
)
SELECT COUNT(UserID) AS [Count] FROM cte_CTEName
WHERE Date <= GETDATE() AND Date >= DATEADD(hh, -24, GETDATE())
I think this is what you are trying to achieve...
Select
UserID,
Date,
Count(1)
from
(Select *
from table1
Union All
Select *
from table2
Union All
Select *
from table3
Union All
Select *
from table4
) a
Group by
Userid,
Date

how to sum columns from 2 tables by date

I have 2 tables:
table1 contains (amount, date)
table2 contains (amount, revenue, date)
I need to:
sum table1.amount + table2.amount, sum table2.revenue
according to table1.date and table2.date
like:
select sum(table1.amount + table2.amount), sum(table2.revenue)
from table1,table2
where table1.date and table2.date = '2008-02%'
This will show the result for all year-month:
with table3 (amount, revenue, date) as
(
select amount, 0, date from table1
union all
select cost, revenue, date from table2
)
select year(date), month(date), sum(amount), sum(revenue)
from table3
group by year(date), month(date)
order by year(date), month(date);
SELECT SUM(amount), SUM(revenue)
FROM (
SELECT amount, 0 as revenue, theDate
FROM table1
UNION ALL
SELECT amount, revenue, theDate
FROM table2 ) AS DERIVED
WHERE (MONTH(DERIVED.theDate) = 2
AND YEAR(DERIVED.theDate) = 2008)
You could put the where clause in the UNION statement, which I probably would but this simplifies things for the example. If you have keys in the equation I'd do this differently with a full outer join or something like that but for your example this works.
If you want the date to show and view the sums for all the dates then the following would work:
SELECT SUM(amount), SUM(revenue), theDate
FROM (
SELECT amount, 0 as revenue, theDate
FROM table1
UNION ALL
SELECT amount, revenue, theDate
FROM table2 ) AS DERIVED
GROUP BY theDate
Hope that helps!

Grouping/aggregating SQL results into 1-hour buckets

Similar to this question, I need to group a large number of records into 1-hour "buckets". For example, let's say I've got a typical ORDER table with a datetime attached to each order. And I want to see the total number of orders per hour. So I'm using SQL roughly like this:
SELECT datepart(hh, order_date), SUM(order_id)
FROM ORDERS
GROUP BY datepart(hh, order_date)
The problem is that if there are no orders in a given 1-hour "bucket", no row is emitted into the result set. I'd like the resultset to have a row for each of the 24 hour, but if no orders were made during a particular hour, just record the number of orders as O.
Is there any way to do this in a single query?
See also Getting Hourly Statistics Using SQL.
Some of the previous answers recommend using a table of hours and populating it using a UNION query; this can be better done with a Common Table Expression:
; WITH [Hours] ([Hour]) AS
(
SELECT TOP 24 ROW_NUMBER() OVER (ORDER BY [object_id]) AS [Hour]
FROM sys.objects
ORDER BY [object_id]
)
SELECT h.[Hour], o.[Sum]
FROM [Hours] h
LEFT OUTER JOIN (
SELECT datepart(hh, order_date) as [Hour], SUM(order_id) as [Sum]
FROM Orders
GROUP BY datepart(hh, order_date)
) o
ON h.[Hour] = o.[Hour]
You need to have a pre-populated table (or a function returning a table result set) to join with, that contains all the 1-hour slots you want in your result.
Then you do a OUTER JOIN with that, and you should get them all.
Something like this:
SELECT SLOT_HOUR, SUM(order_id)
FROM
ONEHOURSLOTS
LEFT JOIN ORDERS ON DATEPART(hh, order_date) = SLOT_HOUR
GROUP BY SLOT_HOUR
Create a table of hours, either persisted or even synthesized 'on the fly':
SELECT h.hour, s.sum
FROM (
SELECT 1 as hour
UNION ALL SELECT 2
UNION ALL SELECT 3
...
UNION ALL SELECT 24) as h
LEFT OUTER JOIN (
SELECT datepart(hh, order_date) as hour, SUM(order_id) as sum
FROM ORDERS
GROUP BY datepart(hh, order_date) ) as s
ON h.hour = s.hour;