Sum of sales by Quarter - sql

How can i use SQL and SUM monthly data (quarter view)? I will not use Stored Procedures etc.
Current data:
ID | Sales
201601 | 5
201602 | 15
201603 | 5
201604 | 20
201605 | 8
201606 | 2
...
My ID column is like yyyymm
What I want is:
Quarter | Sales
Q1 | 25
Q2 | 30
....

You can try a query like below
SELECT
LEFT(ID,4) Year,
'Q'+ CAST((CAST(RIGHT(ID,2) AS INT)-1 )/3 +1 AS varchar) Quarter,
SUM(Sales) Sales
FROM
Yourtable
GROUP BY
LEFT(ID,4),
'Q'+ CAST((CAST(RIGHT(ID,2) AS INT) -1 )/3 +1 AS varchar)
order by year
SQL demo link

You can use DATEPART and QUARTER to get the quarter of a DATETIME. You just need to build the DATETIME using DATEFROMPARTS and parsing your ID column.
The CONCAT just adds 'Q' to the period number.
SELECT LEFT(id,4) [Year],
CONCAT('Q',DATEPART(QUARTER, DATEFROMPARTS(LEFT(id,4), RIGHT(id, 2), 1))) [Quarter],
SUM(Sales) [Sales]
FROM yourtable
GROUP BY LEFT(id,4), DATEPART(q, DATEFROMPARTS(LEFT(id,4), RIGHT(id, 2), 1))
ORDER BY [Year], [Quarter]

Related

Check if a month is skipped then add values dynamically?

I have a set of data from a table that would only be populated if a user has data for a certain month just like this:
Month | MonthName | Value
3 | March | 136.00
4 | April | 306.00
7 | July | 476.00
12 | December | 510.48
But what I need is to check if a month is skipped then adding the value the month before so the end result would be like this:
Month | MonthName | Value
3 | March | 136.00
4 | April | 306.00
5 | May | 306.00 -- added data
6 | June | 306.00 -- added data
7 | July | 476.00
8 | August | 476.00 -- added data
9 | September | 476.00 -- added data
10 | October | 476.00 -- added data
11 | November | 476.00 -- added data
12 | December | 510.48
How can I do this dynamically on SQL Server?
One method is a recursive CTE:
with cte as (
select month, value, lead(month) over (order by month) as next_month
from t
union all
select month + 1, value, next_month
from cte
where month + 1 < next_month
)
select month, datename(month, datefromparts(2020, month, 1)) as monthname, value
from cte
order by month;
Here is a db<>fiddle.
you can use spt_values to get continuous number 1-12, and then left join your table by max(month)
select t1.month
,datename(month,datefromparts(2020, t1.month, 1)) monthname
,t2.value
from (
select top 12 number + 1 as month from master..spt_values
where type = 'p'
) t1
left join t t2 on t2.month = (select max(month) from t tmp where tmp.month < = t1.month)
where t2.month is not null
CREATE TABLE T
([Month] int, [MonthName] varchar(8), [Value] numeric)
;
INSERT INTO T
([Month], [MonthName], [Value])
VALUES
(3, 'March', 136.00),
(4, 'April', 306.00),
(7, 'July', 476.00),
(12, 'December', 510.48)
;
Demo Link SQL Server 2012 | db<>fiddle
note
if you have year column then you need to fix the script.

Combining Dates and Ordering by Date

I have the below Query
SELECT distinct
COUNT(Status) AS [Transactions],
left(DATENAME(mm, Date_Reported), 3) AS Month,
DATENAME(yyyy, Date_Reported) AS Year
FROM [Transactions]
GROUP BY DATENAME(mm, Date_Reported), DATENAME(yyyy,Date_Reported)
ORDER BY Year, Month DESC;
My output is as below:
Transaction | Month | Year
123 | Jan | 2000
1234 | Mar | 2000
12 | Feb | 2000
How can I alter the query so I can get the month and year together like "Jan 2000" and then order it by the date so Jan 2000, Feb 2000 and Mar 2000
Thank you in advance
I think you want :
SELECT COUNT(Status) AS [Transactions], t1.MonthYear
FROM [Transactions] t
CROSS APPLY ( VALUES (CONCAT(DATENAME(mm, Date_Reported),' ',
DATENAME(yyyy, Date_Reported)),
DATEPART(mm, Date_Reported)
)
) t1 (MonthYear, Morder)
GROUP BY t1.MonthYear, t1.Morder
ORDER BY t1.Morder;

SQL query to get current and last year sales

I have the following table Sales:
Date Store Sales
1/1/2015 St01 12123
1/1/2015 St02 3123
1/1/2016 St01 4213
1/1/2016 St03 2134
When I try to self join to get this year and last year sales the closed store is not showing up.
The result should be like this:
Date Store This year Sales Last Year Sales
1/1/2016 St01 4213 1212
1/1/2016 St02 0 3123
1/1/2016 St03 2134 0
My query as follows:
SELECT CY.DATE,
CY.store cy.Sales,
LY.sales
FROM sales CY,
sales LY
WHERE CY.store(+) = LY.store(+)
AND LY.DATE = CY.DATE - 365
Oracle Setup:
CREATE TABLE sales ( "DATE", Store, Sales ) AS
SELECT DATE '2015-01-01', 'St01', 12123 FROM DUAL UNION ALL
SELECT DATE '2015-01-01', 'St02', 3123 FROM DUAL UNION ALL
SELECT DATE '2016-01-01', 'St01', 4213 FROM DUAL UNION ALL
SELECT DATE '2016-01-01', 'St03', 2134 FROM DUAL;
Query:
SELECT TRUNC( SYSDATE, 'YY' ) AS "DATE",
Store,
SUM( CASE WHEN "DATE" = TRUNC( SYSDATE, 'YY' )
THEN sales END )
AS "This year sales",
SUM( CASE WHEN "DATE" = ADD_MONTHS( TRUNC( SYSDATE, 'YY' ), -12 )
THEN sales END )
AS "Last year sales"
FROM sales
GROUP BY store
ORDER BY store;
Output:
DATE STORE This year sales Last year sales
------------------- ----- --------------- ---------------
2016-01-01 00:00:00 St01 4213 12123
2016-01-01 00:00:00 St02 3123
2016-01-01 00:00:00 St03 2134
What you need is called Pivoting Table. Although Oracle has specific clauses to do it, you can use just plain and pure SQL to do so, like this:
SELECT store,
SUM(CASE WHEN Extract(year FROM DATE) = Extract(year FROM SYSDATE) THEN
sales
ELSE 0
END) AS "This year Sales",
SUM(CASE WHEN Extract(year FROM DATE) = Extract(year FROM SYSDATE) - 1 THEN
sales
ELSE 0
END) AS "Last year Sales"
FROM sales
WHERE Extract(year FROM DATE) >= Extract(year FROM SYSDATE) - 1
GROUP BY store
ORDER BY store
It would show:
Store This year Sales Last year Sales
St01 4213 12123
St02 0 3123
St03 2134 0
Note that makes no sense to have to column date as the first column. You couldn't group by it to show the output you want.
See the equivalent of this query here on fiddle: http://sqlfiddle.com/#!15/7662d8/6
Since I want the query to return day by day sales I used MT0 answer and added the dates, this way I can get the data for all year days.
WITH AllYear AS
(select to_date('2016-01-01', 'yyyy-mm-dd') + level - 1 AS dobs
from dual
connect by level <= 366)
SELECT dobs AS "DATE",
Store,
nvl(SUM(CASE
WHEN t.Date = dobs THEN
t.sales
END),
0) AS "This Year Sales",
nvl(SUM(CASE
WHEN t.Date = dobs-365 THEN
t.sales
END),
0) AS "Last Year Sales"
FROM Sales t,AllYear
where dobs='01-Jan-2016'
GROUP BY Store
ORDER BY Store;
The general solution is a full outer join, which includes all records from both joined tables. I don't know the Oracle syntax, but in MS SQL Server it would be something like this:
SELECT ISNULL(CY.DATE, LY.DATE) as DATE,
ISNULL(CY.store, LY.store) as STORE,
isnull(cy.Sales, 0),
isnull(LY.sales, 0)
FROM sales CY FULL OUTER JOIN sales LY
ON CY.store = LY.store
AND (CY.DATE IS NULL OR
DATEPART(year, LY.DATE) = DATEPART(year, CY.DATE) - 1
ISNULL(a, b) gives a if A IS NOT NULL, else b. DATEPART extracts specified part of a date; I'm comparing a difference of exactly one year rather than 365 days, in case "last year" is a leap year/
I only work with SQL Server. If anything is different, try to apply the same logic.
Declaring a temporary table to test the query:
DECLARE #Sales TABLE (
[Date] DATE,
Store NVARCHAR(10),
Sales INT
)
INSERT INTO #Sales VALUES
('1/1/2015','St01',12123),
('1/1/2015','St02',3123),
('1/1/2016','St01',4213),
('1/1/2016','St03',2134);
SELECT * FROM #Sales;
The actual query:
SELECT
CY_Date = CASE
WHEN CY.Date IS NULL THEN DATEADD(YEAR, 1, LY.Date)
ELSE CY.Date
END,
LY_Date = CASE
WHEN LY.Date IS NULL THEN DATEADD(YEAR, -1, CY.Date)
ELSE LY.Date
END,
Store = CASE
WHEN CY.Store IS NULL THEN LY.Store
ELSE CY.Store
END,
ISNULL(CY.Sales, 0) AS CY_Sales,
ISNULL(LY.Sales, 0) AS LY_Sales
FROM #Sales CY
FULL JOIN #Sales LY ON (CY.Store = LY.Store AND LY.Date = DATEADD(YEAR, -1, CY.Date))
WHERE (CY.Date = '1/1/2016' OR CY.Date IS NULL)
AND (LY.Date = DATEADD(YEAR, -1, '1/1/2016') OR LY.Date IS NULL);
Result:
CY_Date LY_Date Store CY_Sales LY_Sales
2016-01-01 2015-01-01 St01 4213 12123
2016-01-01 2015-01-01 St03 2134 0
2016-01-01 2015-01-01 St02 0 3123
How it works:
The FULL JOIN will will combine by the Store and the lines from the current and the year before.
The WHERE clause will filter by the current date '1/1/2016'. The NULLs are allowed because sometimes you don't have lines for the current or for the last year.
On the columns, CASES are used to create the dates if they are null (If the current date is null, get the last year + 1 year, and vice versa), to create the store if they are null and to place a zero instead of a null on the sales columns.

SQL Ranking Dates to Get Year Over Year Order

I have a list of dates in a YYYYMM format and I am trying to rank them in a Year over Year format that would look like the following:
MonthDisplay YearMonth Rank MonthNumber YearNumber
Aug-2013 201308 1 8 2013
Aug-2012 201208 2 8 2012
Jul-2013 201307 3 7 2013
Jul-2012 201207 4 7 2012
I have been able to get it close by using the following Rank and get the results below:
RANK() OVER(PARTITION BY 1 ORDER BY MonthNumber DESC, YearNumber DESC)
Month YearMonth Rank
Dec-2012 201212 1
Dec-2011 201112 2
Nov-2012 201211 114
Nov-2011 201111 115
Oct-2012 201210 227
Oct-2011 201110 228
However, this starts with Dec-2012 instead of the Aug-2013 (current month). I can't figure out how to get it to start with the current month. I am sure it something super easy and I am just missing it. Thanks!
select
T.YearMonth,
rank() over (order by R.rnk asc, D.YearNumber desc) as [Rank],
D.MonthNumber, D.YearNumber
from Table1 as T
outer apply (
select
month(getdate()) as CurMonthNumber,
cast(right(T.YearMonth, 2) as int) as MonthNumber,
cast(left(T.YearMonth, 4) as int) as YearNumber
) as D
outer apply (
select
case
when D.MonthNumber <= D.CurMonthNumber then
D.CurMonthNumber - D.MonthNumber
else
12 + D.CurMonthNumber - D.MonthNumber
end as rnk
) as R
sql fiddle example

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