I'm trying to insert the followings columns below into a #table based off a time range.
For example if the query is run in August 2020, I'd need it to insert Months names from Jan - July 2020
Report_Date_Name| Report_Date_Month| DateYrmo| DateYear| DateMonth|
January 2020| January| 202001| 2020| 1|
February 2020| February| 202002| 2020| 2|
March 2020| March| 202003| 2020| 3|
April 2020| April| 202004| 2020| 4|
May 2020| May| 202005| 2020| 5|
June 2020| June| 202006| 2020| 6|
July 2020| July| 202007| 2020| 7|
________________________________________________________________________________
A recursive CTE seems like a simple approach:
with dates as (
select datefromparts(year(getdate()), 1, 1) as dte
union all
select dateadd(month, 1, dte)
from dates
where dte < getdate()
)
select concat(convert(char(12), datename(month, dte)), year(dte)) as report_date_name,
datename(month, dte) as report_date_month,
year(dte) * 100 + month(dte) as year_month,
year(dte) as year
from dates;
It is not clear if the last columns are strings or numbers. If strings, just cast them to the type you want.
Here is a db<>fiddle.
Try this:
DECLARE #SomeDate DATE = '2020-08-08';
WITH Dates ([Date]) AS
(
SELECT DATEADD(MONTH, -[number], '2020-08-08')
FROM
(
SELECT [number] + 1
FROM [master].[dbo].[spt_values]
WHERE [type] = 'P'
) n ([number])
WHERE DATEADD(MONTH, -[number], #SomeDate) <= DATEADD(MONTH, -1, #SomeDate)
AND YEAR(DATEADD(MONTH, -[number], #SomeDate)) = YEAR(#SomeDate)
)
SELECT CONCAT(DATENAME(MONTH, [Date]), ' ', YEAR([Date])) AS [Report_Date_Name]
,DATENAME(MONTH, [Date]) AS [Report_Date_Month]
,RIGHT(YEAR([Date]) * 100 + 10000000 + MONTH([Date]), 6) AS [DateYrmo]
,YEAR([Date]) AS [DateYear]
,MONTH([Date]) AS [DateMonth]
FROM Dates
ORDER BY [Date] ASC;
The CTE is used to generated one date from each previous month for the current year. Then we are using built-in functions to format the output.
Related
I have a table with some columns. Unfortunately, I don't have column with classic date format like "YYYY-MM-DD".
I have columns year and month, like:
2021 | 7
2021 | 10
2021 | 1
I want to build date from these columns and extract quarter in format (Q3'21) from this date. Can I do it with PostgreSQL?
I expect two new columns date and quarter:
2021 | 7 | 2021-07-01 | Q3'21
2021 | 10 | 2021-10-01 | Q4'21
2021 | 1 | 2021-01-01 | Q1'21
I tried to build date with simple concatenation gr."year" || '-0' || gr."month" || '-01' as custom_date, but i got wrong values like:
2021-010-01
You an use make_date() to create a date, then use to_char() to format that date:
select t.year,
t.month,
make_date(t.year, t.month, 1),
to_char(make_date(t.year, t.month, 1), '"Q"Q''YY')
from the_table t;
Use MAKE_DATE:
select
year, month,
make_date(year, month, 1) as first_day_of_month,
'Q' || to_char(make_date(year, month, 1), 'Q') || '''' ||
to_char(make_date(year, month, 1), 'YY') as quarter
from mytable;
I can calculate the number of ids in a month and then sum it up over 12 months. I also get the average using this code.
select id, to_char(event_month, 'yyyy') event_year, sum(cnt) overall_count, avg(cnt) average_count
from (
select id, trunc(event_date, 'month') event_month, count(*) cnt
from daily
where event_date >= date '2019-01-01' and event_date < '2019-01-31'
group by id, trunc(event_date, 'month')
) t
group by id, to_char(event_month, 'yyyy')
The results looks something like this:
ID| YEAR | OVER_ALL_COUNT| AVG
1| 2019 | 712 | 59.33
2| 2019 | 20936849 | 161185684.6
3| 2019 | 14255773 | 2177532.2
However, I want to modify this to get the over all id counts for a month instead and the average of the id counts per month. Desired result is:
ID| MONTH | OVER_ALL_COUNT| AVG
1| Jan | 152 | 10.3
2| Jan | 15000 | 1611
3| Jan | 14255 | 2177
1| Feb | 4300 | 113
2| Feb | 9700 | 782
3| Feb | 1900 | 97
where January has 152 id counts over all for id=1, and the average id count per day is 10.3. For id=2, the january count is 15000 and the average id=2 count for jan is 1611.
You just need to change the truncating on your subquery to truncate by day instead of by month, then truncate the outer query by month instead of year.
select id, to_char(event_day, 'Mon') event_month, sum(cnt) overall_count, avg(cnt) average_count
from (
select id, trunc(event_date) event_day, count(*) cnt
from daily
where event_date >= date '2019-01-01' and event_date < date '2019-01-31'
group by id, trunc(event_date)
) t
group by id, to_char(event_month, 'Mon')
This answers the original version of the question.
You can use last_day():
select id, to_char(event_month, 'yyyy') event_year, sum(cnt) as overall_count,
avg(cnt) as average_count,
extract(day from last_day(min(event_month)) as days_in_month,
sum(cnt) / extract(day from last_day(min(event_month)) as avg_days_in_month
from (select id, trunc(event_date, 'month') as event_month, count(*) as cnt
from daily
where event_date >= date '2019-01-01' and
event_date < date '2020-01-01'
group by id, trunc(event_date, 'month')
) t
group by id, to_char(event_month, 'yyyy')
I have this table:
tbl_Sales
----------------------------------------
|Item_Code|Sold|Date |
|---------+----+-----------------------|
|BBPen100 |30 |2017-04-17 00:00:00.000|
|BBPen100 |21 |2017-04-13 00:00:00.000|
|BBPen100 |13 |2017-04-12 00:00:00.000|
|XSHIRT80 |0 |2017-04-17 00:00:00.000|
|XSHIRT80 |24 |2017-04-14 00:00:00.000|
|XSHIRT80 |9 |2017-04-13 00:00:00.000|
|XSHIRT80 |5 |2017-04-12 00:00:00.000|
|YBSHADE7 |0 |2017-04-17 00:00:00.000|
|YBSHADE7 |6 |2017-04-15 00:00:00.000|
|YBSHADE7 |0 |2017-04-13 00:00:00.000|
|YBSHADE7 |11 |2017-04-12 00:00:00.000|
----------------------------------------
How can I get the last non-zero Sold value from the last 2 working days? This means that I need to exclude the Weekends and Holidays.
I have this table which consists holidays.
tbl_Holiday
-------------------------
|Holiday_Date |
|-----------------------|
|2017-04-14 00:00:00.000|
|2017-05-01 00:00:00.000|
|2017-10-18 00:00:00.000|
|2017-12-25 00:00:00.000|
-------------------------
So for example today is 2017-04-18, the output should be like this:
---------------------
|Item_Code|Last_Sold|
|---------+---------|
|BBPen100 |30 |
|XSHIRT80 |9 |
|YBSHADE7 |0 |
---------------------
The goal is to get the last Sold value from LAST 2 working days, so the counting start on 2017-04-17.
Output analysis:
BBPen100-since it has value from last 1 working day (2017-04-17), that value will be retrieved.
XSHIRT80-Zero value from last 1 working day (2017-04-17)
-2017-04-16 & 2017-04-15 are weekends
-2017-04-14 is holiday
-So value from 2017-04-13 will be retrieved.
YBSHADE7-Zero value from last 1 working day (2017-04-17)
-2017-04-16 & 2017-04-15 are weekends
-2017-04-14 is holiday
-2017-04-13 has Zero value
-2017-04-12 is beyond Last 2 working days
-So value retrived should be Zero
Currently, I have this query:
SELECT Item_Code, Sold AS 'Last_Sold'
FROM tbl_Sales
WHERE CONVERT(date, [DATE]) = CASE
WHEN CONVERT(date, [DATE]) = CONVERT(date, DATEADD(day, -1, GETDATE())) THEN CONVERT(date, DATEADD(day, -1, GETDATE()))
WHEN CONVERT(date, [DATE]) <> CONVERT(date, DATEADD(day, -1, GETDATE())) THEN CONVERT(date, DATEADD(day, -2, GETDATE()))
But of course, this would not meet the requirements.
Please help me resolve this.
IMPORTANT NOTE: Consider the holidays on weekends and what if I run the program on weekends or holidays.
Thank you in advance.
You could try it
Sample data
DECLARE #SampleData as TABLE (Item_Code varchar(10), Sold int, Date datetime)
Insert into #SampleData VALUES
('BBPen100', 30,'2017-04-17 00:00:00.000'),
('BBPen100', 21,'2017-04-13 00:00:00.000'),
('BBPen100', 13,'2017-04-12 00:00:00.000'),
('XSHIRT80', 0 ,'2017-04-17 00:00:00.000'),
('XSHIRT80', 24,'2017-04-14 00:00:00.000'),
('XSHIRT80', 9 ,'2017-04-13 00:00:00.000'),
('XSHIRT80', 5 ,'2017-04-12 00:00:00.000'),
('YBSHADE7', 0 ,'2017-04-17 00:00:00.000'),
('YBSHADE7', 6 ,'2017-04-15 00:00:00.000'),
('YBSHADE7', 0 ,'2017-04-13 00:00:00.000'),
('YBSHADE7', 11,'2017-04-12 00:00:00.000')
DECLARE #TblHoliday AS TABLE
(
Holiday_Date date
)
INSERT INTO #TblHoliday
VALUES
('2017-04-14 00:00:00.000'),
('2017-05-01 00:00:00.000'),
('2017-10-18 00:00:00.000'),
('2017-12-25 00:00:00.000')
DECLARE #CurrentDate datetime = '2017-04-18 00:00:00'
You could calculate #2PreviousWorkingDays before #CurrentDate
-- 2 Previous Working Day with out Holiday
DECLARE #2PreviousWorkingDay date = CASE
WHEN datepart(dw,#CurrentDate) IN (2,3) THEN dateadd(day,-4, #CurrentDate) -- 2 previous working day before monday
WHEN datepart(dw,#CurrentDate) IN (1) THEN dateadd(day,-3, #CurrentDate) -- 2 previous working day before sunday
ELSE dateadd(day,-2, #CurrentDate) -- other day week
END
-- with holiday
SELECT #2PreviousWorkingDay = dateadd(day,0 - (SELECT count(1) FROM #TblHoliday th
WHERE th.Holiday_Date BETWEEN #2PreviousWorkingDay AND #CurrentDate
ANd datepart(dw,th.Holiday_Date) NOT IN (7,1) -- calculate only holiday that isn't weekend
)
, #2PreviousWorkingDay
And your desired result:
;with temps AS
(
SELECT *, row_number() over(PARTITION BY sd.Item_Code ORDER BY sd.[Date] DESC) AS Rn
FROM #SampleData sd
WHERE sd.[Date] >= #2PreviousWorkingDay -- 2 working days
AND NOT EXISTS (SELECT 1 FROM #TblHoliday th WHERE th.Holiday_Date = Cast(sd.[Date] AS date)) -- not holiday
AND datepart(dw,sd.[Date]) NOT IN (7,1) -- not weekend
AND sd.Sold <> 0 -- not zero sold
)
SELECT sd.Item_Code, ISNULL(t.Sold,0) AS Sold FROM
(
SELECT DISTINCT sd.Item_Code FROM #SampleData sd
) sd
LEFT JOIN temps t ON t.Item_Code = sd.Item_Code AND t.Rn = 1
Demo link: Rextester
WITH cte1 AS(
SELECT t.*
FROM tbl_Sales t
LEFT JOIN tbl_Holiday hd
ON t.Date = hd.Holiday_Date
WHERE hd.Holiday_Date IS NULL AND
DATENAME(WEEKDAY, t.Date) <> 'Saturday' AND
DATENAME(WEEKDAY, t.Date) <> 'Sunday'
),
WITH cte2 AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY Item_Code ORDER BY Date DESC) rn
FROM cte1
)
SELECT *
FROM cte2
WHERE rn=1
I am using SQL Server 2008 R2 and T-SQL. These are abbreviated examples.
My table/view has the following fields
Table/View
ReportID,
UnitName,
UnitID,
CaseDefinitionID,
CaseDefinition,
DateOfDelivery,
YEAR(DateOfDelivery) AS [Year],
MONTH(DateOfDelivery) AS [Month],
DATENAME(m,DateOfDelivery) AS [Month name]
and my target report is:
Unit | Case type 1| Case type 2| Case total|
Unit A | 36| 40| 76|
2013| 20| 18| 38|
Jan| 10|
Feb| 10|
2014| 16|
Mar| 8|
Dec| 8|
Unit B | 12|
2013| 12|
Jan| 6|
May| 6|
Grand total| 48|
The month rows can be empty, NULL content is fine. The example report is incomplete to aid clarity.
Progress so far is the following query:
SELECT *
FROM
(
SELECT
PCT.[Year],
PCT.UnitName AS [Unit],
PCT.UnitID,
PCT.CaseDefinition AS [Case definition]
FROM
PivotBase AS PCT
) AS P1
PIVOT
(
COUNT(UnitID)
FOR [Case definition] IN ([Case type 1],[Case type 2])
) AS P2
This produces the following table
Year|Unit |Case type 1|Case type 2|
2013|Unit A | 20 | 18|
2014|Unit A | 16 | 22|
2013|Unit B | 6 | 8|
2014|Unit B | 6 | 8|
There is no need for dynamic SQL
My reading so far has covered many options but I still have no idea where to go next. How do I produce the required report.
You can get there by pivoting on Month and Year and then using WITH ROLLUP
WITH data
AS (SELECT *
FROM (SELECT Dateadd(month, Datediff(month, 0, PCT.dateofdelivery), 0
)
MonthYear,
PCT.unitid,
PCT.unitname
AS
[Unit],
PCT.casedefinition
AS
[Case definition]
FROM pivotbase AS PCT) AS P1
PIVOT ( Count(unitid)
FOR [case definition] IN ([Case type 1],
[Case type 2]) ) AS p2),
rollup
AS (SELECT Month(monthyear) Month,
unit unitX,
Year(monthyear) Year,
Sum([case type 1]) [case type 1],
Sum([case type 2]) [case type 2],
Grouping(unit) GUnit,
Grouping(Month(monthyear)) gm,
Grouping(Year(monthyear)) gy
FROM data
GROUP BY unit,
Year(monthyear),
Month(monthyear) WITH rollup)
SELECT COALESCE(Cast(month AS VARCHAR), Cast(year AS VARCHAR), unitx,
'Grand Total')
Unit,
[case type 1],
[case type 2]
FROM rollup
ORDER BY gunit,
unitx,
year,
gm DESC,
month
SQLFiddle
It's doable with some fancy rollup (SQL Fiddle)
;WITH
CTE1 AS
(
SELECT UnitName,
[Year],
[Month],
CaseDefinition,
GROUPING_ID(UnitName,[Year],[Month],CaseDefinition)
AS GroupingID,
CASE GROUPING_ID(CaseDefinition,UnitName,[Year],[Month])
WHEN 0 THEN UnitName + '-' + CAST(Year AS varchar(4)) + '-' + RIGHT('0' + CAST([Month] AS varchar(10)),2)
WHEN 1 THEN UnitName + '-' + CAST(Year AS varchar(4))
WHEN 3 THEN UnitName
WHEN 7 THEN 'zzz' -- So that grand total appears at the bottom
ELSE NULL
END AS GroupingLevel,
CASE GROUPING_ID(CaseDefinition,UnitName,[Year],[Month])
WHEN 0 THEN ' ' + CASE [Month]
WHEN 1 THEN 'Jan'
WHEN 2 THEN 'Feb'
WHEN 3 THEN 'Mar'
WHEN 4 THEN 'Apr'
WHEN 5 THEN 'May'
WHEN 6 THEN 'Jun'
WHEN 7 THEN 'Jul'
WHEN 8 THEN 'Aug'
WHEN 9 THEN 'Sep'
WHEN 10 THEN 'Oct'
WHEN 11 THEN 'Nov'
WHEN 12 THEN 'Dec'
END
WHEN 1 THEN ' ' + CAST([Year] AS varchar(4))
WHEN 3 THEN UnitName
WHEN 7 THEN 'Grand Total'
END AS DisplayName,
COUNT(UnitID) AS UnitCount
FROM PivotBase
GROUP BY GROUPING SETS(
(CaseDefinition),
(UnitName,CaseDefinition),
(UnitName,[Year],[CaseDefinition]),
(UnitName,[Year],[Month],CaseDefinition)
)
)
SELECT pvt.GroupingLevel,
pvt.DisplayName,
pvt.[Case Type 1],
pvt.[Case Type 2],
ISNULL(pvt.[Case Type 1],0) + ISNULL(pvt.[Case Type 2],0) AS [Case Total]
FROM CTE1
PIVOT (
SUM(UnitCount) FOR CaseDefinition IN ([Case Type 1],[Case Type 2])
) pvt
ORDER BY GroupingLevel
Explanation:
GROUPING SET( (set1), (set2), (set3) ) defines the roll up level. You will get a count of UnitID for each unique combination inside set1, then set2, then set3
GROUPING_ID is the most obscure function in here. Think of it as a bit mask. If a column is aggregated, its bit value is set to 1. For example: GROUPING_ID(field3, field2, field1, field0). If all 4 are not aggregated, the bit mask is 0000 = 0. If field0 is aggregated away, the returned value is 0001 = 1, and so on.
You can replace the last SELECT with SELECT * FROM CTE1 to see the inner working of the query.
I need to get monthly count of distinct customers based on last three month Sales.
To show the result by adding current month customer count and adding last three month customer to the count as below:
In month of APRIL ,distinct customers count of (APRIL+MARCH+FEBRUARY)
In month of MAY,distinct customers count of (MAY+APRIL+MARCH)
In month of JUNE,distinct customers count of (JUNE+MAY+APRIL)
In month of JULY,distinct customers count of (JULY+JUNE+MAY)
Here what I tried:
SELECT MonNumber = MONTH(h.Invoicedate) ,
YearNumber = YEAR(h.Invoicedate) ,
PartyCount = ( SELECT COUNT(DISTINCT s.CustomerID)
FROM salesdata s
WHERE s.Invoicedate BETWEEN DATEADD(month, -6,
h.Invoicedate)
AND h.Invoicedate
)
FROM salesdata h
GROUP BY MONTH(h.Invoicedate) ,
YEAR(h.Invoicedate)
ORDER BY YEAR(h.Invoicedate) ,
MONTH(h.Invoicedate)
| Year | Month | COUNT |
|-----------|----------|-------------|
| 2014 | Jan | 6 |
| 2014 | Feb | 6 |
| 2014 | Mar | 6 |
| 2014 | Apr | 4 |
| 2014 | May | 6 |
| 2014 | Jun | 6 |
View the table on SQL Fiddle
Here it is.
WITH dt AS (
-- set invoice to BOM to get one row per month
SELECT DISTINCT DATEADD(mm,DATEDIFF(mm,0,InvoiceDate),0) AS InvoiceDate
FROM salesdata
)
SELECT YEAR(dt.InvoiceDate) AS YEAR
, MONTH(dt.InvoiceDate) AS MONTH
, COUNT(DISTINCT CustomerId) AS PARTYCOUNT
FROM salesdata s
INNER JOIN dt
-- Define your window
ON s.InvoiceDate >= DATEADD(MM, -2, dt.InvoiceDate)
AND s.InvoiceDate < DATEADD(MM, 1, dt.InvoiceDate)
GROUP BY YEAR(dt.InvoiceDate)
, MONTH(dt.InvoiceDate)
ORDER BY 1, 2
You need to apply some aggregate function to invoicedate.
This should work: fiddle
SELECT
-- get the first of the current month and substract two months
dateadd(month, -2, DATEADD(day, -day(h.Invoicedate) + 1, h.Invoicedate)) as first_of_month,
PartyCount = ( SELECT COUNT(DISTINCT s.CustomerID)
FROM salesdata s
WHERE s.Invoicedate >= dateadd(month, -2, DATEADD(day, -day(h.Invoicedate) + 1, h.Invoicedate))
AND s.Invoicedate < min(dateadd(month, 1, DATEADD(day, -day(h.Invoicedate) + 1, h.Invoicedate)))
)
FROM salesdata h
group by
dateadd(month, -2, DATEADD(day, -day(h.Invoicedate) + 1, h.Invoicedate))
order by 1
I would prefer to create a table with the month names and date ranges first and then simply use this instead.