I am trying to write a SQL query that show the distribution of customers seniority since 2015/01/31:
Up to one month
Between one and six months
Between six months and one year
Over a year
I succeeded to split and group the number of months of the customers.
SELECT Seniority, COUNT(Customer_ID) [Number of Customers]
FROM
(SELECT Customer_ID,
DATEDIFF(MONTH, MIN(CONVERT(datetime, Order_Date)), '2015/01/31') Seniority
FROM Orders
GROUP BY Customer_ID) t
GROUP BY Seniority
How can I split by given ranges?
Expected:
Seniorty | Number of Customers
Up to one month | 0
Between one and six months | 14
Between six months and one year | 1
Over a year | 0
Use conditional aggregation:
WITH cte AS (
SELECT
'Up to one month' AS Seniority,
COUNT(CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') < 1 THEN 1 END) AS [Number of Customers],
1 AS position
FROM Orders
UNION ALL
SELECT
'Between one and six months',
COUNT(CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') >= 1 AND
DATEDIFF(MONTH, Order_Date, '2015-01-31') < 6 THEN 1 END),
2
FROM Orders
UNION ALL
SELECT
'Between six months and one year',
COUNT( CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') >= 6 AND
DATEDIFF(MONTH, Order_Date, '2015-01-31') < 12 THEN 1 END),
3
FROM Orders
UNION ALL
SELECT
'Over a year',
COUNT(CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') > 12 THEN 1 END),
4
FROM Orders
)
SELECT
Seniority,
[Number of Customers]
FROM cte
ORDER BY
position;
This answer assumes that the Order_Date column is already date or datetime. If not, then the first thing you should do is to convert this column to an actual date type.
WITH CTE AS (
SELECT CUSTOMER_ID,
CASE WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE)=1 THEN 'Up to one month'
WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE) BETWEEN 1 AND 6 THEN 'Between one and six months'
WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE) BETWEEN 6 AND 12 THEN 'Between six months and one year'
WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE)>12 THEN 'Over a year'
END AS SENIORITY
FROM ORDERS
)
SELECT SENIORITY AS 'Seniority', COUNT(CUSTOMER_ID) AS 'Number of Customers'
FROM CTE
WHERE SENIORITY IS NOT NULL
GROUP BY SENIORITY
Related
Edit: #shawnt00 has the correct answer. Thank you very much!
I am having trouble accurately doing a year over year comparison by month but at any point during the month. For example for August 2022 vs 2021, I want to compare August 1 - August 25, rather than full month of August 2021.
I am also using a daily date field.
I want the final result to basically be:
Product_ID, Year, Month, PY_Sales, CY_Sales
Edit: I have daily totals. Some products do have not sales on certain days though:
product_id
sale_date
units
1
2021-01-01
5
2
2021-01-02
4
...
...
...
1
2021-06-05
2
2
2022-01-06
1
2
2022-08-15
9
This is the code I have, but it doesn't do MTD. So 2021 August is the entire month of August and I want it the same dates for 2022. I used this code because some products do not have sales on certain months.
WITH cte AS
(
SELECT
PRODUCT_ID,
EXTRACT(YEAR FROM SALE_DATE) AS Year,
EXTRACT(MONTH FROM SALE_DATE) AS Month,
CONCAT(EXTRACT(YEAR FROM SALE_DATE), '-',EXTRACT(MONTH FROM SALE_DATE)) AS Year_Month,
SUM(Units) AS Units
FROM data
WHERE Product_ID = 1
AND DATE(SALE_DATE) >= '2019-01-01'
GROUP BY 1, 2, 3
),
diff AS
(
SELECT
COALESCE(c.PRODUCT_ID, p.PRODUCT_ID) AS Product_ID,
COALESCE(c.Year, p.Year + 1) AS Year,
COALESCE(c.Month, p.Month) AS Month,
IFNULL(c.Units, 0) AS Current_Units,
IFNULL(p.Units, 0) AS Previous_Units,
NULLIF(((IFNULL(c.Units, 0) - IFNULL(p.Units,0)) / p.Units),0) * 100 AS Percent_Change
FROM CTE c
FULL OUTER JOIN CTE p ON c.PRODUCT_ID = p.PRODUCT_ID AND c.Year = p.Year + 1 AND c.Month = p.Month
WHERE c.Year <= EXTRACT(YEAR FROM CURRENT_DATE())
ORDER BY 2, c.Year, c.Month
)
SELECT *
FROM diff
--This is to avoid dividing by 0
WHERE diff.Previous_Units > 0
--AND Percent_Change <= -.5
You could just roll up two different monthly totals and then switch for the current month comparison:
with agg as (
select
PRODUCT_ID,
extract(year from SALE_DATE) as yr,
extract(month from SALE_DATE) as mth,
sum(Units) as Units,
sum(case when extract(day from SALE_DATE) <= extract(day from current_date())
then Units end) as UnitsMTD
from data
where date(SALE_DATE) >= '2019-01-01' -- one year before report output
group by 1, 2, 3
)
select c.Yr, c.Mth, c.PRODUCT_ID,
case when Yr = extract(year from current_date())
and Mth = extract(month from current_date())
then (c.UnitsMTD - p.UnitsMTD) / p.UnitsMTD
else (c.Units - p.Units ) / p.Units
end as Percent_Change
from agg c left outer join agg p
on p.Product_ID = c.Product_ID and p.Yr = c.Yr - 1 and p.Mth = c.Mth
order by c.Yr, c.Mth, c.PRODUCT_ID;
Note my earlier comment about leap years. This will treat February 28 of the year following a leap year as an "MTD" month. You might need to handle that differently inside the case expression.
I am having trouble accurately doing a year over year comparison by month but at any point during the month. For example for August 2022 vs 2021, I want to compare August 1 to today, rather than full month of August 2021.
My data has a date field.
I want the final result to basically be:
Product_ID, Year, Month, PY_Sales, CY_Sales
I have daily totals. Some products do have not sales on certain days though. Here's an example:
product_id
sale_date
units
1
2021-01-01
5
2
2021-01-02
4
...
...
...
1
2021-06-05
2
2
2021-08-01
1
2
2021-08-31
6
2
2022-01-06
1
2
2022-08-15
9
The final result for August should be:
product_id
Year
Month
PY_Sales
CY_Sales
2
2022
8
1
9
Right now my code will show 7 for August for product_id = 2 because 6 sales happened on August 31st but that day hasn't happened yet in 2022.
This is the code I have, but it doesn't do MTD. Right now, PY_Sales for August 2022 is showing the entire August of 2021, but I want it to show the MTD of August 2021. I used this code because some products do not have sales on certain months.
WITH cte AS
(
SELECT
PRODUCT_ID,
EXTRACT(YEAR FROM SALE_DATE) AS Year,
EXTRACT(MONTH FROM SALE_DATE) AS Month,
CONCAT(EXTRACT(YEAR FROM SALE_DATE), '-',EXTRACT(MONTH FROM SALE_DATE)) AS Year_Month,
SUM(Units) AS Units
FROM data
WHERE Product_ID = 1
AND DATE(SALE_DATE) >= '2019-01-01'
GROUP BY 1, 2, 3
),
diff AS
(
SELECT
COALESCE(c.PRODUCT_ID, p.PRODUCT_ID) AS Product_ID,
COALESCE(c.Year, p.Year + 1) AS Year,
COALESCE(c.Month, p.Month) AS Month,
IFNULL(c.Units, 0) AS Current_Units,
IFNULL(p.Units, 0) AS Previous_Units,
NULLIF(((IFNULL(c.Units, 0) - IFNULL(p.Units,0)) / p.Units),0) * 100 AS Percent_Change
FROM CTE c
FULL OUTER JOIN CTE p ON c.PRODUCT_ID = p.PRODUCT_ID AND c.Year = p.Year + 1 AND c.Month = p.Month
WHERE c.Year <= EXTRACT(YEAR FROM CURRENT_DATE())
ORDER BY 2, c.Year, c.Month
)
SELECT *
FROM diff
--This is to avoid dividing by 0
WHERE diff.Previous_Units > 0
--AND Percent_Change <= -.5
I'm being a little repetitive but I hope this is clear! Thank you so much!
In the cte table you summarize the sold units by month and year.
Your question can be solved by adding here a column units_last_year. This contains the units, which are sold up to the day one year ago. Today is the 27th of August 2022, therefore the units on the 31th of August 2021 will be set to zero.
SUM(Units) AS Units,
SUM(IF(SALE_DATE< date_sub(current_Date(),interval 1 year), Units, 0 )) as units_last_year
Please use the safe_divide command, if there is any chance of diving by zero
Here is the full query with example data.
You given an example of fixed dates, which are compared to the current date. Therefore, the query would not show the desired effect after 30th of August 2022.
The product_id three is made up values related to the current date, thus the following query yields results after August 2022.
with data as (
select *,date(sale_date_) as sale_date
from (
Select 1 product_id, "2021-01-01" sale_date_, 5 units
union all select 2,"2021-01-02", 4
union all select 1,"2021-06-05", 2
union all select 2,"2021-08-01", 1
union all select 2,"2021-08-31", 6
union all select 2,"2022-01-06", 1
union all select 2,"2022-08-15", 9
union all select 3, current_date(), 10
union all select 3, date_sub(current_date(),interval 1 year), 9
union all select 3, date_sub( date_trunc(current_date(),month),interval 1 year), 1
)
),
cte AS
(
SELECT
PRODUCT_ID,
EXTRACT(YEAR FROM SALE_DATE) AS Year,
EXTRACT(MONTH FROM SALE_DATE) AS Month,
CONCAT(EXTRACT(YEAR FROM SALE_DATE), '-',EXTRACT(MONTH FROM SALE_DATE)) AS Year_Month,
SUM(Units) AS Units,
sum(if(SALE_DATE< date_sub(current_Date(),interval 1 year), units, 0 )) as units_last_year
FROM data
WHERE # Product_ID = 1 AND
DATE(SALE_DATE) >= '2019-01-01'
GROUP BY 1, 2, 3, 4
),
diff AS
(
SELECT
COALESCE(c.PRODUCT_ID, p.PRODUCT_ID) AS Product_ID,
COALESCE(c.Year, p.Year + 1) AS Year,
COALESCE(c.Month, p.Month) AS Month,
IFNULL(c.Units, 0) AS Current_Units,
IFNULL(p.Units, 0) AS Previous_Units,
IFNULL(p.Units_last_Year, 0) AS Previous_Units_ok,
NULLIF(((IFNULL(c.Units, 0) - IFNULL(p.Units,0)) / p.Units),0) * 100 AS Percent_Change,
NULLIF(safe_divide((IFNULL(c.Units, 0) - IFNULL(p.Units_last_Year,0)) , p.Units_last_Year),0) * 100 AS Percent_Change_ok,
FROM CTE c
FULL OUTER JOIN CTE p ON c.PRODUCT_ID = p.PRODUCT_ID AND c.Year = p.Year + 1 AND c.Month = p.Month
WHERE c.Year <= EXTRACT(YEAR FROM CURRENT_DATE())
ORDER BY 2, c.Year, c.Month
)
SELECT *
FROM diff
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
I have a table with columns sales_date, sales_id, sales_region and I am looking to display the count of sales for the past 7 days, 20 days and YTD.
I have this query below that returns the correct count for 7 and 20 days but the YTD shows the count minus the 7 and 20 days. How can I tweak this query to show the YTD correctly? Thank you
select region,
case when current_date- sales_date <=7 then 'Past7'
when current_date- sales_date <=28 then 'Past20'
else 'YTD'
end as "trendsales",
count(*) as salescount
from sales_table
where sales_date >= '2022-01-01'
group by 1
you could pivot it a bit. 4 columns Region, YTD, Past7, and Past20 would be columns.
select region,
sum(case when current_date- sales_date <=7 then 1 else 0 end) as Past7,
sum(case when current_date- sales_date <=28 then 1 else 0 end) as Past20,
count(*) as YTD
from sales_table
where sales_date >= '2022-01-01'
group by 1
I got a problem while trying to count the entries that were created in a month for the last 6 months.
The table looks like this:
A B C D
Year Month Startingdate Identifier
-----------------------------------------
2019 3 2019-03-12 OAM_1903121
2019 2 2019-03-21 OAM_1902211
And the result should look like:
A B C
Year Month Amount of orders
---------------------------------
2019 3 26
2019 2 34
This is what I have so far, but it doesn't get me the proper results:
SELECT year, month, COUNT(Startingdate) as Amount
FROM table
WHERE Startingdate > ((TRUNC(add_months(sysdate,-3) , 'MM'))-1)
GROUP BY year, month
I have not tested it, but it should work:
select year, month, count(Stringdate) as Amount_of_order
from table
where Stringdate between add_months(sysdate, -6) and sysdate
group by year, month;
Let me know.
Try that :
SELECT YEAR(Startingdate) AS [Year], MONTH(Startingdate) AS [Month], COUNT(*) AS Amount
FROM table
WHERE Startingdate > DATEADD(MONTH, -6, GETDATE())
GROUP BY YEAR(Startingdate), MONTH(Startingdate)
ORDER BY YEAR(Startingdate), MONTH(Startingdate) DESC
I think your issue is the filtering. If so, this should handle the most recent six full months:
SELECT year, month, COUNT(*) as num_orders
FROM table
WHERE Startingdate >= TRUNC(add_months(sysdate, -6) , 'MM')
GROUP BY year, month;