Group By Count and Total Count - sql

I am trying to figure out how can I get following result by query but unable to do the same.
Table Name: ProductMaster
ProductCode Quantity EntryDate
A1 10 10/03/2015
A1 10 15/03/2015
A2 10 18/03/2015
A2 10 25/03/2015
A1 10 10/04/2015
A2 10 15/04/2015
I want to get result as
If I select March month, result should be as:
ProductCode MonthCount TotalCount
A1 20 30
A2 20 30
If I select April month, result should be as:
ProductCode MonthCount TotalCount
A1 10 30
A2 10 30
My Query:
SELECT ProductCode, SUM(Quantity)
FROM ProductMaster
WHERE DATEPART(MONTH, EntryDate) = #Month
GROUP BY ProductCode
Where #month = 3 or 4, based on input.
Additionally, How can i get the count of productcode.
For Month = 3
ProductCode MonthCount TotalCount
A1 2 3
A2 2 3

You could use a case expression based on #Month:
SELECT ProductCode,
SUM (CASE WHEN MONTH(EntryDate) = #Month THEN Quantity ELSE 0 END)
AS MonthCount,
SUM (Quantity) AS TotalConount
FROM ProductMaster
GROUP BY ProductCode
EDIT:
To answer the edited question, you can use the same technique with count instead of sum:
SELECT ProductCode,
COUNT (CASE WHEN MONTH(EntryDate) = #Month THEN Quantity ELSE NULL END)
AS MonthCount,
COUNT (*) AS TotalConount
FROM ProductMaster
GROUP BY ProductCode

Here is a solution using a Common Table Expression (CTE). This query will get the results for all months in the same resultset. If you only need one month you can add a WHERE clause and filter on MonthNumber for just the month you desire.
;WITH cteProductMasterByMonth AS
(
SELECT ProductCode, DATEPART(MONTH, EntryDate) as MonthNumber, SUM(Quantity) as MonthCount
FROM #ProductMaster
GROUP BY ProductCode, DATEPART(MONTH, EntryDate)
)
SELECT ProductCode, MonthCount, SUM(MonthCount) OVER(PARTITION BY ProductCode) AS TotalCount
FROM cteProductMasterByMonth
ORDER BY MonthNumber, ProductCode
I tested this on SqlFiddle
Note: If the EntryDate values spans multiple years and you want to the results for each month-year pair, then you will need to add an additional condition to in the CTE query to group by DATEPART(YEAR, EntryDate) as well and maybe a YearNumber field similar to the MonthNumber field in the select clause.

Related

What to use in place of union in above query i wrote or more optimize query then my given query without union and union all

I am counting the birthdays , sales , order in all 12 months from customers table in SQL server like these
In Customers table birth_date ,sale_date, order_date are columns of the table
select 1 as ranking,'Birthdays' as Type,[MONTH],TOTAL
from ( select DATENAME(month, birth_date) AS [MONTH],count(*) TOTAL
from customers
group by DATENAME(month, birth_date)
)x
union
select 2 as ranking,'sales' as Type,[MONTH],TOTAL
from ( select DATENAME(month, sale_date) AS [MONTH],count(*) TOTAL
from customers
group by DATENAME(month, sale_date)
)x
union
select 3 as ranking,'Orders' as Type,[MONTH],TOTAL
from ( select DATENAME(month, order_date) AS [MONTH],count(*) TOTAL
from customers
group by DATENAME(month, order_date)
)x
And the output is like these(just dummy data)
ranking
Type
MONTH
TOTAL
1
Birthdays
January
12
1
Birthdays
April
6
1
Birthdays
May
10
2
Sales
Febrary
8
2
Sales
April
14
2
Sales
May
10
3
Orders
June
4
3
Orders
July
3
3
Orders
October
6
3
Orders
December
17
I want to find count of these all these three types without using UNION and UNION ALL, means I want these data by single query statement (or more optimize version of these query)
Another approach is to create a CTE with all available ranking values ​​and use CROSS APPLY for it, as shown below.
WITH ranks(ranking) AS (
SELECT * FROM (VALUES (1), (2), (3)) v(r)
)
SELECT
r.ranking,
CASE WHEN r.ranking = 1 THEN 'Birthdays'
WHEN r.ranking = 2 THEN 'Sales'
WHEN r.ranking = 3 THEN 'Orders'
END AS Type,
DATENAME(month, CASE WHEN r.ranking = 1 THEN c.birth_date
WHEN r.ranking = 2 THEN c.sale_date
WHEN r.ranking = 3 THEN c.order_date
END) AS MONTH,
COUNT(*) AS TOTAL
FROM customers c
CROSS APPLY ranks r
GROUP BY r.ranking,
DATENAME(month, CASE WHEN r.ranking = 1 THEN c.birth_date
WHEN r.ranking = 2 THEN c.sale_date
WHEN r.ranking = 3 THEN c.order_date
END)
ORDER BY r.ranking, MONTH

Number of Customer Purchases in Their First Month

I have a list of customer orders. I can easily calculate the month and year of first purchase for each customer (e.g. customer 1 had their first purchase in Sept 2021, customer 2 had their first purchase in Oct 2021, etc.). What I want to add is an additional column that counts the number of purchases a customer made in their first month.
Existing data table (Orders):
OrderId
CustomerId
OrderDate
1
1
9/15/2021
2
1
10/15/2021
3
1
11/1/2021
4
2
10/1/2021
5
2
10/6/2021
6
2
10/7/2021
7
2
11/9/2021
8
3
11/15/2021
Desired output:
CustomerId
FirstOrderMonth
FirstOrderYear
FirstMonthPurchaseCount
1
9
2021
1
2
10
2021
3
3
11
2021
1
I was thinking something like this for the first three columns:
SELECT o.CustomerId,
MONTH(MIN(o.OrderDate)) as FirstOrderMonth,
YEAR(MIN(o.OrderDate)) as FirstOrderYear
FROM Orders o
GROUP BY o.CustomerId
I am not sure how to approach the final column and was hoping for some help.
Aggregate by the customer's id, the year and the month of the order and use window functions to get the year and month of the 1st order and the count of that 1st month:
SELECT DISTINCT CustomerId,
FIRST_VALUE(MONTH(OrderDate)) OVER (PARTITION BY CustomerId ORDER BY YEAR(OrderDate), MONTH(OrderDate)) FirstOrderMonth,
MIN(YEAR(OrderDate)) OVER (PARTITION BY CustomerId) FirstOrderYear,
FIRST_VALUE(COUNT(*)) OVER (PARTITION BY CustomerId ORDER BY YEAR(OrderDate), MONTH(OrderDate)) FirstMonthPurchaseCount
FROM Orders
GROUP BY CustomerId, YEAR(OrderDate), MONTH(OrderDate);
See the demo.
You may use the RANK() function to identify the first month purchases for each user as the following:
Select D.CustomerId, MONTH(OrderDate) FirstOrderMonth,
YEAR(OrderDate) FirstOrderYear, COUNT(*) FirstMonthPurchaseCount
From
(
Select *, RANK() Over (Partition By CustomerId Order By YEAR(OrderDate), MONTH(OrderDate)) rnk
From table_name
) D
Where D.rnk = 1
Group By D.CustomerId, MONTH(OrderDate), YEAR(OrderDate)
See a demo.
If you want to find second, third ... month purchases, you may use the DENSE_RANK() function instead of RANK() and change the value in the where clause to the required month order.
select CustomerId
,min(month(OrderDate)) as FirstOrderMonth
,min(year(OrderDate)) as FirstOrderYear
,count(first_month_flag) as FirstMonthPurchaseCount
from (select *
,case when month(OrderDate) = month(min(OrderDate) over(partition by CustomerId)) then 1 end as first_month_flag
from Orders) Orders
group by CustomerId
CustomerId
FirstOrderMonth
FirstOrderYear
FirstMonthPurchaseCount
1
9
2021
1
2
10
2021
3
3
11
2021
1
Fiddle

MS SQL | How to query a filtered column (WHERE) with non filtered data

I have a problem solving an MS SQL query.
in summary, the query should get the date column as two columns, year and month, the count of other columns, the sum of total of a column, and a filtered sum column.
what I struggled with was adding the filtered sum column.
a sample data, Test:
customerID, 1,2,3,4...
InvoiceID, 1234551, 1234552...
ProductID, A, B, C...
Date, Datetime
Income, int
customerID
InvoiceID
ProductID
Date
Income
1
1234551
A
01/01/2015
300
2
1234552
B
02/01/2016
300
I have a solution, but I am sure there is a more simple solution.
WITH CTE_1 AS
(
SELECT Date,
COUNT(DISTINCT Test.customerID) AS customers,
COUNT(Test.InvoiceID) AS Invoices,
COUNT(Test.ProductID) AS Products,
Sum(Income) AS Total_Income,
ISNULL((SELECT Sum(Income) AS Income_A FROM Test ts WHERE ProductID = 'A' AND ts.Date = Test.Date),0) AS Total_Income_A
FROM Test
GROUP BY Test.Date
)
SELECT YEAR(Date) AS Year,
MONTH(Date) AS Month,
Sum(customers) AS customers,
Sum(Invoices) AS Invoices,
Sum(Products) AS Products,
Sum(Total_Income) AS Total_Income,
Sum(Total_Income_A) AS Total_Income_A
FROM CTE_1
GROUP BY YEAR(Date), MONTH(Date)
ORDER BY YEAR(Date), MONTH(Date)
to produce:
Year, 2015, 2016...
Month, 1, 2, ...
customers, int
Invoices, int
Products, int
Total_Income, int
Total_Income_A, int
Year
Month
customers
Invoices
Products
Total_Income
Total_Income_A
2015
1
3
4
4
1600
600
2015
2
1
1
1
1200
0
Thanks!
Nir
You can directly apply a Conditional Aggregation such as
SELECT YEAR(Date) AS Year,
MONTH(Date) AS Month,
COUNT(DISTINCT customerID) AS customers,
COUNT(DISTINCT InvoiceID) AS Invoices,
COUNT(ProductID) AS Products,
SUM(Income) AS Total_Income,
ISNULL(SUM(CASE WHEN ProductID = 'A' THEN Income END),0) AS Total_Income_A
FROM Test
GROUP BY YEAR(Date), MONTH(Date)
ORDER BY YEAR(Date), MONTH(Date)
Demo

How can I sum cost items, grouped by invoice?

I have two SQL Server tables below:
Invoice
InvoiceId Amount [Date]
1 10 2015-05-28 21:47:50.000
2 20 2015-05-28 21:47:50.000
3 25 2015-05-28 23:25:50.000
InvoiceItem
Id InvoiceId Cost
1 1 8
2 1 3
3 1 7
4 2 15
5 2 17
6 3 20
7 3 22
Now I want to JOIN these two tables ON InvoiceId and retrieve the following:
COUNT of DISTINCT InvoiceId from Invoice table AS [Count]
SUM of Amount from Invoice table AS Amount
SUM of Cost from InvoiceItem table AS Cost
HOUR part of [Date]
and GROUP them BY HOUR part of [Date].
Desired Output wil be:
[Count] Amount Cost HourOfDay
2 30 50 22
1 25 42 23
How can I do this?
one approach is to use a derived table:
SELECT CAST([Date] AS DATE) AS [Date],
DATEPART(HOUR,i.[Date]) AS HourOfDay,
COUNT(i.InvoiceId) AS NumberOfInvoices,
SUM(i.Amount) AS Amount,
SUM(it.Cost) AS Cost
FROM invoice i
INNER JOIN
(SELECT InvoiceId, SUM(Cost) AS Cost
FROM invoiceitem
GROUP BY InvoiceId) it ON i.InvoiceId = it.InvoiceId
GROUP BY [Date],DATEPART(HOUR,i.[Date])
or a CTE (Common Table Expression)
WITH InvoiceCosts (InvoiceId, Cost)
AS
(
SELECT InvoiceId, SUM(Cost) AS Cost
FROM invoiceitem
GROUP BY InvoiceId
)
SELECT CAST([Date] AS DATE) AS [Date],
DATEPART(HOUR,i.[Date]) AS HourOfDay,
COUNT(i.InvoiceId) AS NumberOfInvoices,
SUM(i.Amount) AS Amount,
SUM(ic.Cost) AS Cost
FROM invoice i
INNER JOIN
InvoiceCosts ic ON i.InvoiceId = ic.InvoiceId
GROUP BY [Date],DATEPART(HOUR,i.[Date])
SELECT COUNT (DISTINCT inv.InvoiceId) [Count],
SUM (Amount) Amount,
SUM (Cost) Cost,
datepart(HOUR, inv.[Date]) HourOfDay
FROM Invoice inv
INNER JOIN InvoiceItem itm
ON inv.InvoiceId = itm.InvoiceId
GROUP BY datepart(HOUR, inv.[Date]);

Select sum of items based on their monthly entry date

I am trying to select sum of items based on their monthly entry date:
The Inventory table is as below:
EntryDate Items
1/1/2013 2
1/20/2013 5
1/23/2013 3
1/30/2013 2
2/4/2013 4
2/17/2013 34
The desired output with Total row added:
EntryDate Items
1/1/2013 2
1/20/2013 5
1/23/2013 3
1/30/2013 2
**Total 12**
2/4/2013 4
2/17/2013 34
**Total 38**
Below is my attempt. I am trying to do this using rollup but its counting all items at once and not by monthly basis, how to achieve this:
Select Convert(date, EntryDate) AS [DATE],SUM(Items) AS Total,
Case WHEN GROUPING(Items) = 1 THEN 'Rollup'
Else Status end AS Total From [Inventory] Group by Convert(date, EntryDate) WITH
Rollup
If you actually want results like your example, you can use the following:
SELECT EntryDate, Items
FROM (SELECT YEAR(EntryDate)'Year_',MONTH(EntryDate)'Month_',CAST(EntryDate AS VARCHAR(12))'EntryDate',Items,1 'sort'
FROM Inventory
UNION ALL
SELECT YEAR(EntryDate)'Year_',MONTH(EntryDate)'Month_','Total',SUM(Items)'Items',2 'sort'
FROM Inventory
GROUP BY YEAR(EntryDate),MONTH(EntryDate)
)sub
ORDER BY Year_,Month_,sort
Demo: SQL Fiddle
Try this, it will give you the month total in a new column, it will also work when your dates stretch over more than 1 year
SELECT EntryDate, Items,
SUM(Items) OVER (partition by dateadd(month, datediff(month, 0, entrydate), 0)) Total
FROM [Inventory]
Result:
EntryDate Items Total
2013-01-01 2 12
2013-01-20 5 12
2013-01-23 3 12
2013-01-30 2 12
2013-02-04 4 38
2013-02-17 34 38
DATEPART will do try this,
SELECT DATEPART(month, EntryDate) AS 'Month Number',sum(items1) FROM ITEMS
GROUP BY DATEPART(month, EntryDate)
# sqlfiddle
Try:
select entryDate, Items, sum(Items)
from (select entryDate,Items, to_date(entry_date, 'mm/yyyy') month_date from Inventory)
group by month_date
Here's a perfect solution for you
select isnull(cast(DATEFROMPARTS(year,month,day) as varchar),'Total'),
items
from ( SELECT datepart(yyyy,EntryDate) year,
DATEPART(mm, EntryDate) month,
DATEPART(dd, EntryDate) day,
sum(items) items
From Inventory
GROUP BY
ROLLUP (datepart(yyyy,EntryDate),
DATEPART(mm, EntryDate),
DATEPART(dd, EntryDate)
)
) a
where a.year is not null
and a.month is not null;
Demo HERE