Split string in columns by delimiter using SQL Server - sql-server

I need to split my one column value in column using delimiter, below is the table structure.
create table #a
(
id int,
timeline varchar(100)
)
insert into #a
values (1, 'Semi Annual Q2 (May/June/July) & Q4 (Nov/Dec/Jan)'),
(2, 'Semi Annual Q1 (Feb/Mar/Apr) & Q3 (Aug/Sep/Oct)'),
(3, 'Annual Q3 (Aug/Sep/Oct)'),
(4, 'Annual Q2 (May/June/July)'),
(5, 'Annual Q4 (Nov/Dec/Jan)'),
(6, 'Semi Annual Q1 (Jan/Feb/Mar) & Q3 (July/Aug/Sep)')
select * from #a
Output I want to split timeline values by ' / ' delimiter and make separate column for separate month and all month should be in sequence, which look like a below sample.
ID M1 M2 M3 M4 M5 M6
---------------------------------------
1 May June July Nov Dec Jan
2 Feb Mar Apr Aug Sep Oct
3 Aug Sep Oct NULL NULL NULL
4 May June July NULL NULL NULL
5 Nov Dec Jan NULL NULL NULL
6 Jan Feb Mar July Aug Sep
So far, I have tried this:
select
timeline,
substring((substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline) - 1)), 1, charindex('/', substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline) - 1)) - 1) as M1,
replace(replace(right(substring(substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline)), 1, charindex(')', substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline)))), charindex('/', reverse(substring(substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline)), 1, charindex(')', substring(timeline, CHARINDEX('(', timeline) + 1, len(timeline))))), 4)), '/', ''), ')', '') as M3
from
#a;
which is not a code and too tedious also. please help if you have efficient way to do this.

The SplitCSVToTable8K function being used in the following solution, is the same DelimitedSplit8K function mentioned above...
Here's how to use it in the solution:
WITH
cte_ParseTimeline AS (
SELECT
a.id,
rn = ROW_NUMBER() OVER (PARTITION BY a.id ORDER BY sc.ItemNumber),
sc.Item
FROM
#a a
CROSS APPLY dbo.SplitCSVToTable8K(REPLACE(REPLACE(a.timeline, '(', '/'), ')', '/'), '/') sc
WHERE
sc.Item LIKE ('[A-Z][a-z][a-z]')
OR
sc.Item LIKE ('[A-Z][a-z][a-z][a-z]')
)
SELECT
pt.id,
M1 = MAX(CASE WHEN pt.rn = 1 THEN pt.Item END),
M2 = MAX(CASE WHEN pt.rn = 2 THEN pt.Item END),
M3 = MAX(CASE WHEN pt.rn = 3 THEN pt.Item END),
M4 = MAX(CASE WHEN pt.rn = 4 THEN pt.Item END),
M5 = MAX(CASE WHEN pt.rn = 5 THEN pt.Item END),
M6 = MAX(CASE WHEN pt.rn = 6 THEN pt.Item END)
FROM
cte_ParseTimeline pt
GROUP BY
pt.id;
Results...
id M1 M2 M3 M4 M5 M6
----------- ----- ----- ----- ----- ----- -----
1 May June July Nov Dec Jan
2 Feb Mar Apr Aug Sep Oct
3 Aug Sep Oct NULL NULL NULL
4 May June July NULL NULL NULL
5 Nov Dec Jan NULL NULL NULL
6 Jan Feb Mar July Aug Sep

Related

SQL Server take a time duration and parse the time into the hour parts of that duration

I am posting this question again because the project has changed and the previous answers don't return the desired results. Ambulances and fire trucks have the dispatch time when an emergency occurred and an end time for when the emergency was declared over.
Event 1 starts on May 1, 2021 10:17:33 and ends at may 1, 2021 10:33:41.
Event 2 starts on May 1, 2021 11:50:52 and ends at May 1, 2021 13:18:21.
I would like to parse the amount of time from the start to the end and place it into the hour parts when it occurs. For example; Event 1 starts at 10:17 and ends at 10:33. It would place 16 minutes minutes in the 10:00 hour part for that day. Event 2 would place 10 minutes in the 11:00 hour part, 60 minutes in the 12:00 hour part and 18 minutes in the 13:00 hour part. Place the minutes in the hours during which the event occured.
The results should look the following. Although I am flexible. For example, if the name of the truck cannot be returned in the results that would be ok because if the EventID is there, I could relate back to the original table.
EventID
Ambulance
EventDayOfYear
EventHour
MinutesAllocated
1
Medic10
121
10
16
1
Medic10
121
11
10
2
Ladder73
121
11
10
2
Ladder73
121
12
60
2
Ladder73
121
13
18
3
Engine41
121
13
33
3
Engine41
121
14
21
4
Medic83
121
15
32
4
Medic83
121
16
5
5
Rescue32
121
16
33
6
Medic09
121
23
16
6
Medic09
122
0
39
7
Engine18
121
23
28
7
Engine18
122
0
60
7
Engine18
122
1
34
8
Rescue63
122
0
35
The following SQL code comes close to working to deliver the right result. But it does not overlap days. There are many emergency events that start at 2300 hours and last until 0300 hours the following day.
DECLARE #tempFireEvents TABLE
(
EventID INT NOT NULL
, Apparatus VARCHAR(10) NOT NULL
, StartDateTime DATETIME NOT NULL
, EndDateTime DATETIME NOT NULL
, DurationInSeconds INT NOT NULL
)
INSERT INTO #tempFireEvents
VALUES
(1, 'Medic10', 'may 1, 2021 10:17:33', 'may 1, 2021 10:33:41', 968) /*This event is entirely within 1000 hours*/
, (2, 'Ladder73', 'may 1, 2021 11:50:52', 'may 1, 2021 13:18:21', 5249) /*This event spans 1100, 1200 and 1300 hours*/
, (3, 'Engine41', 'may 1, 2021 13:27:17', 'may 1, 2021 14:21:18', 3241) /*This event overlaps 1300 and 1400 hours*/
, (4, 'Medic83', 'may 1, 2021 15:28:08', 'may 1, 2021 16:05:48', 2260) /*This event overlaps 1500 and 1600 hours*/
, (5, 'Rescue32', 'may 1, 2021 16:20:43', 'may 1, 2021 16:53:28', 1965) /*This event is entirely within the 1600 hour part*/
, (6, 'Medic09', 'may 1, 2021 23:44:06', 'may 2, 2021 00:39:52', 3346) /*Notice this overlaps the 2300 and 0000 hours into the following day*/
, (7, 'Engine18', 'may 1, 2021 23:32:58', 'may 2, 2021 01:34:13', 7275) /*Notice this overlaps the 2300, 0000 and 0100 hours into the following day*/
, (8, 'Rescue63', 'may 2, 2021 00:17:45', 'may 2, 2021 00:52:09', 2064) /*Notice this is the 00 hour of the day and does not show in the results*/
;
WITH AllHours AS
(
SELECT 1 AS hourInt
UNION ALL
SELECT hourInt + 1
FROM AllHours
WHERE hourInt < 23
)
,
Combined AS
(
SELECT
T.EventID
, H.hourInt
, CASE WHEN DATEPART(HOUR, T.StartDateTime) = H.hourInt THEN 1 ELSE 0 END AS isStart
, CASE WHEN DATEPART(HOUR, T.EndDateTime) = H.hourInt THEN 1 ELSE 0 END AS isEnd
, T.StartDateTime
, T.EndDateTime
FROM #tempFireEvents AS [T]
JOIN AllHours AS [H] ON H.hourInt BETWEEN DATEPART(HOUR, T.StartDateTime) AND DATEPART(HOUR,T.EndDateTime)
)
SELECT
EventID
, hourInt
, CASE WHEN isStart = 1 AND isEnd = 0 THEN 60 - DATEPART(MINUTE, StartDateTime)
WHEN isStart = 0 AND isEnd = 1 THEN DATEPART(MINUTE, EndDateTime)
WHEN isStart = 1 AND isEnd = 1 THEN DATEPART(MINUTE, EndDateTime) - DATEPART(MINUTE, StartDateTime)
ELSE 60
END AS MinutesInThisHour
FROM Combined
ORDER BY EventID ASC, hourint ASC
;
I have a suspiction that SQL Server may not be the best method to achieve the goal. It may need to be written in Python with increment and decrement and counters.
If it helps, I have a calendar table that looks like:
May 1, 2021 00:00:00
May 1, 2021 01:00:00
May 1, 2021 02:00:00
May 1, 2021 03:00:00
May 1, 2021 04:00:00
May 1, 2021 05:00:00
May 1, 2021 06:00:00
Would a calendar table be useful in solving this problem?
…
CREATE TABLE tempFireEvents
(
EventID VARCHAR(8) NOT NULL,
StartDateTime DATETIME NOT NULL,
EndDateTime DATETIME NOT NULL
)
INSERT INTO tempFireEvents
VALUES
('fire0001', 'november 1, 2018 10:45:00', 'november 2, 2018 11:30:00'),
('fire0002', 'november 1, 2018 11:50:00', 'november 1, 2018 13:10:00'),
('fire0003', 'november 1, 2018 13:20:00', 'november 1, 2018 14:20:00'),
('fire0004', 'november 1, 2018 15:25:00', 'november 1, 2018 16:05:00'),
('fire0005', 'november 1, 2018 16:20:00', 'november 2, 2018 17:00:00'),
('fire0006', 'november 1, 2018 16:20:00', 'november 1, 2018 17:01:00');
select e.*, hr.ld,
60 - case when e.startdatetime > hr.ld then datepart(minute, e.startdatetime) else 0 end
+ case when e.enddatetime < hr.ud then datepart(minute, e.enddatetime)-60 else 0 end as allocatedminutes
from tempFireEvents as e
cross apply
(
select
dateadd(hour, datepart(hour,e.startdatetime)+t.rn-1, cast(cast(e.startdatetime as date) as datetime)) as ld,
dateadd(hour, datepart(hour,e.startdatetime)+t.rn, cast(cast(e.startdatetime as date) as datetime)) as ud,
rn
from
(
-- a tally, max 100 rows .. max 100 hours duration
select top (1+datediff(hour,e.startdatetime,dateadd(minute, -1, e.enddatetime))) row_number() over(order by ##spid) as rn
from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) as a(n)
cross join (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) as b(n)
) as t
) as hr;

How to generate employee timesheet in sql server

I have five tables:
Employees(EmpId, FirstName, LastName, DeptId,…)
Attendance(LogId, PunchIn, PunchOut,...)
Leave(RequestId, StartDate, endDate, LeaveType,...)
Holiday(HolidayId, Descriptions, StartDate, EndDate,....)
Business Travel(Request_Id, RequestDate, StartDate, EndDate,....)
I am trying to generate a monthly/Annual time-sheet report, time-sheet must represent an employee (or group of employees) status at the corresponding date like: on duty, later arrived, leave vacation, sick leave etc.
I create the following function
Create Function dbo.fn_GetEmployeeStatus(#LogDate Date, #BadgeNo int)
Returns char(3)
As
begin
Declare #status char(3) = null;
Declare #punchin DateTime;
Declare #punchout DateTime;
Declare #ShiftId tinyint;
Declare #LateEarly smallint;
Declare #id int = null;
Set #status = null;
Select #ShiftId = Shift_Id From Staff
Where Badge_No = Badge_No;
Select #id = Max(Holiday_Id) From Holiday
Where #LogDate Between StartDate And EndDate
if(#id is not null)
Set #status = 'HOL' --Holiday
else Set #status = null;
if(#status is null)
begin
Set #id = null;
Select #id = Max(Request_Id) From BusinessTravel
Where #LogDate Between Start_Date And End_Date
And Badge_No = #BadgeNo
if(#id is not null)
Set #status = 'BST' --Business Travel
else Set #status = null;
end
if(#status is null)
begin
Set #id = null;
Select #id = Max(LeaveCategory_Id) From Leaves
Where #LogDate Between Start_Date And End_Date
And Badge_No = #BadgeNo
if(#id is not null)
begin
Set #status = Case #id
When 1 Then 'ANV' --Annual Leave
When 2 Then 'SIC' --Sick leave
When 3 Then 'SLV' --Study leave
When 4 Then 'ELV' --Emergency leave
When 5 Then 'ULV' --Unpaid leave
When 6 Then 'HLV' --Haj Leave
When 7 Then 'MLV' --Marriage leave
When 8 Then 'DLV' --Death of a relative
When 9 Then 'PLV' --Paternity leave
When 7 Then 'MLV' --Materinty Leave
else 'UNK' end; --Unknown
end else Set #status = null;
end
if(#status is null)
begin
Select #punchin = Min(Log_Date), #punchout = Max(Log_Date) From AttendanceLog
Where Cast(Log_Date as Date) = #LogDate
And Badge_No = #BadgeNo
if(#punchin is not null)
begin
Select #LateEarly = dbo.fun_GetEarlyLateAmt(#punchin, 1, #ShiftId)
if(#LateEarly > 20)
begin
Select #LateEarly = dbo.fun_GetEarlyLateAmt(#punchout, 0, #ShiftId)
if(#LateEarly > 10)
Set #status = 'L&E'; --Late Arrive & Early left
else Set #status = 'LTE'; --Late Arrive
end else Set #status = 'ODU'; --On Duty
end --else Set #status = null;
end
if(#status is null)
begin
if ((DatePart(dw, #LogDate) + ##DateFirst) % 7) in(0, 6)
Set #status = 'WKE'; --Weekend
Set #status = 'ABS';
end --else Set #status = 'ABS';
Return #status;
end
--But the following failed
Declare
#BadgeNo int = 2523,
#Year int = 2019
;With data as (
Select DateName(month, InTime) Mon, LogInDay = DATEPART(DAY, DATEADD(HOUR, -6, InTime))
,LogYear = DATEPART(YEAR, InTime), LogMonth = DATEPART(MONTH, InTime),
dbo.fn_GetEmployeeStatus_2(InTime, Badge_No) eStatus From v_EmpAttendanceStatus--v_AttendanceLog
Where DATEPART(YEAR, InTime) = #Year
And Badge_No = #BadgeNo)
Select Mon, LogYear, LogMonth, IsNull([1], 0)[1], IsNull([2], 0)[2], IsNull([3], 0)[3], IsNull([4], 0)[4], IsNull([5], 0)[5], IsNull([6], 0)[6], IsNull([7], 0)[7], IsNull([8], 0)[8], IsNull([9], 0)[9], IsNull([10], 0)[10], IsNull([11], 0)[11], IsNull([12], 0)[12], IsNull([13], 0)[13], IsNull([14], 0)[14], IsNull([15], 0)[15], IsNull([16], 0)[16], IsNull([17], 0)[17], IsNull([18], 0)[18], IsNull([19], 0)[19], IsNull([20], 0)[20], IsNull([21], 0)[21], IsNull([22], 0)[22], IsNull([23], 0)[23], IsNull([24], 0)[24], IsNull([25], 0)[25], IsNull([26], 0)[26], IsNull([27], 0)[27], IsNull([28], 0)[28], IsNull([29], 0)[29], IsNull([30], 0)[30], IsNull([31], 0)[31]
From data
Pivot (
Max(eStatus)
For LogInDay In
([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23], [24], [25], [26], [27], [28], [29], [30], [31])
) as piv
Order By LogMonth
Above cte result not shown holidays and leave vacation. I need the report like bellow image.
--Sample Data
1- Employee Table:
#Badge_No FirstName LastName DeptId#
2523 Name1 Last1 1001
2524 Name2 Last2 1001
2525 Name3 Last3 1006
-------------------------------------------------------------------
2- Holiday Table:
ID Sescriprions StartDate EndDate
1 Holidy1 '02 Jun 2019' '08 Jun 2019'
1 Holidy2 '23 Sep 2019' '24 Sep 2019'
---------------------------------------------------------------------
3- Leave Table:
#Request_Id Badge_No StartDate EndDate LeaveCategory#
1 2525 '15 Dec 2018' '22 Jan 2019' 2 --Sick leave
...
101 2524 '01 Jan 209' '03 Jan 209' 2 --Sick Leave
2061 2523 '09 Jun 2019' '04 Jul 2019' 1 --Annual Leave
..
2125 2523 '22 Jul 2019' '22 Jul 2019' 5 --Unpaid leave
--------------------------------------------------------------
4- Attendance Table:
#Badge_No Punch-In Punch-Out Shift_Id#
2523 '01 Jan 2019 07:04' '01 Jan 2019 16:01' 1
2524 '01 Jan 2019 07:45 '01 Jan 2019 15:56' 1
.......
2523 '28 Jan 2019 06:50' '28 Jan 2019 16:03' 1
2524 '28 Jan 2019 07:02' '28 Jan 2019 16:00' 1
2525 '28 Jan 2019 07:42' '28 Jan 2019 16:00' 1
2523 '29 Jan 2019 07:30' '29 Jan 2019 15:59' 1
2524 '29 Jan 2019 06:58' '29 Jan 2019 16:01' 1
2523 '30 Jan 2019 07:13' '30 Jan 2019 07:18' 1
2525 '30 Jan 2019 09:18' '30 Jan 2019 07:18' 1
2524 '30 Jan 2019 08:11' '30 Jan 2019 16:00' 1
2525 '30 Jan 2019 10:26' '30 Jan 2019 16:00' 1
2523 '31 Jan 2019 11:27' '31 Jan 2019 15:54' 1
2524 '31 Jan 2019 07:02' '31 Jan 2019 15:51' 1
.......
2523 '25 Jul 2019 08:17' '25 Jul 2019 17:23' 1
--------------------------------------------------------------
5- BusinessTravel Table:
#Badge_No StartDate EndDate#
2523 '07 Jan 2019' '09 Jan 2019'
2525 '03 Mar 2019' '07 Mar 2019'

SQL - Display Months as columns

I have a SQL Server table that has Start (1-1-2017) and End (1-1-2022) dates for contracts with invoices being generated each month for current and past months.
I would like to display months as columns even when no invoice has been generated, is that possible with just SQL / Pivot tables or a table with dates as calendar must be created?
I have worked with this code so far.
WITH CTE_MyTable AS
(
SELECT
FORMAT(MIN(StartDate), 'yyyy-MM') AS [MyDate]
FROM
MyTable
UNION ALL
SELECT
FORMAT(MIN(DATEADD(month, 1, StartDate)), 'yyyy-MM') AS [MyDate]
FROM
MyTable
WHERE
FORMAT(DATEADD(month, 1, StartDate),'yyyy-MM') <= (SELECT FORMAT(MAX(EndDate), 'yyyy-MM') AS [MyDate] FROM MyTable)
)
SELECT [MyDate]
FROM CTE_ MyTable
GROUP BY MyDate
OPTION (MAXRECURSION 0);
So let's say your table looked like this (using temp variable so you can just copy/paste/test):
DECLARE #sale TABLE(saledate DATE, saleamt MONEY);
INSERT #sale VALUES ('20170103',500),('20170128',266),('20170303',4002),('20170409',25);
Note that I'm only doing 6 months for simplicity. The following query get the count of sales per month (first query) and the sum of the sales for the second query:
DECLARE #sale TABLE(saledate DATE, saleamt MONEY);
INSERT #sale VALUES ('20170103',500),('20170128',266),('20170303',4002),('20170409',25);
SELECT
[201701] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 1 THEN 1 END),
[201702] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 2 THEN 1 END),
[201703] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 3 THEN 1 END),
[201704] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 4 THEN 1 END),
[201705] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 5 THEN 1 END),
[201706] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 6 THEN 1 END)
FROM #sale t;
SELECT
[201701] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 1 THEN t.saleamt END),0),
[201702] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 2 THEN t.saleamt END),0),
[201703] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 3 THEN t.saleamt END),0),
[201704] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 4 THEN t.saleamt END),0),
[201705] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 5 THEN t.saleamt END),0),
[201706] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 6 THEN t.saleamt END),0)
FROM #sale t;
These queries return:
201701 201702 201703 201704 201705 201706
----------- ----------- ----------- ----------- ----------- -----------
2 0 1 1 0 0
201701 201702 201703 201704 201705 201706
--------------------- --------------------- --------------------- --------------------- --------------------- ---------------------
766.00 0.00 4002.00 25.00 0.00 0.00
There is a way to pivot columns in SQL- using the Pivot() function (Microsoft Documentation at: https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-2017)
To display months as columns (1 to 12) the Pivot() function assigns values to (hard-coded) columns. Implemented correctly, there are NULLS where the aggregation doesn't occur due to lack of records. The implied way to replace NULLS with zeroes is by using the COALESCE() function.
The year values of the pivoted data should be grouped and while there is no specific Group By for a pivot, this is implied by how the SourceTable query is written.
In this example code I use the same exact format as the official documentation; with the exception that I additionally group by year and COALESCE NULLs with 0's.
What I am doing is counting the number of records for a given month and year:
SELECT yearval
,COALESCE([1], 0) [Jan]
,COALESCE([2], 0) [Feb]
,COALESCE([3], 0) [Mar]
,COALESCE([4], 0) [Apr]
,COALESCE([5], 0) [May]
,COALESCE([6], 0) [Jun]
,COALESCE([7], 0) [Jul]
,COALESCE([8], 0) [Aug]
,COALESCE([9], 0) [Sep]
,COALESCE([10], 0) [Oct]
,COALESCE([11], 0) [Nov]
,COALESCE([12], 0) [Dec]
FROM
(SELECT YEAR([Your_Date_Column_Here]) AS [yearval]
,MONTH([Your_Date_Column_Here]) AS [monthval]
FROM [Your_Table_Name_Here]) AS SourceTable
PIVOT
(
COUNT(monthval) FOR monthval IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) AS PivotTable
Would produce this output in my test database:
yearval Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
2015 1952 1122 1678 2364 1125 1308 1414 2103 1031 1340 2506 1015
2016 1123 1413 1568 1421 1278 1252 1048 1290 1251 1571 2647 1253
2017 0 0 0 3 0 1241 2377 2714 6724 1388 1521 1243
2018 2127 2118 2449 2330 2687 3833 3279 883 0 0 0 0

sql crosstab for graphing data output

I am trying to get a crosstab of data so that the columns are months of the year and the rows are the years themselves based on value sold in that month.
Therefore:
year jan feb mar apr etc
2014 0 1 5 9
2015 11 12 0 14
using this SQL - (excuse the crudeness)
SELECT distinct
case
when year(fsivho.InvoiceDate) = 2014 then 2014
when year(fsivho.InvoiceDate) = 2015 then 2015
when year(fsivho.InvoiceDate) = 2016 then 2016
when year(fsivho.InvoiceDate) = 2017 then 2017
when year(fsivho.InvoiceDate) = 2018 then 2018
when year(fsivho.InvoiceDate) = 2019 then 2019
when year(fsivho.InvoiceDate) = 2020 then 2020
when year(fsivho.InvoiceDate) = 2021 then 2021
when year(fsivho.InvoiceDate) = 2022 then 2022
when year(fsivho.InvoiceDate) = 2023 then 2023
when year(fsivho.InvoiceDate) = 2024 then 2024
when year(fsivho.InvoiceDate) = 2025 then 2025
end as Month1,
(select sum(cast(fsivl.ShipQuantity * fsivl.InvoiceLocalUnitPrice as decimal(8,2))) as Value
from sqlinvline fsivl , sqlinvheaher fsivh
where fsivl.HeaderKey = fsivh.HeaderKey
and fsivh.InvoiceType = 'I'
and year(fsivh.InvoiceDate)* 100 + month(fsivh.InvoiceDate)
= year(fsivho.InvoiceDate)* 100 + month(fsivho.InvoiceDate)
and MONTH(fsivh.InvoiceDate) = 1
) as 'Jan',
(select sum(cast(fsivl.ShipQuantity * fsivl.InvoiceLocalUnitPrice as decimal(8,2))) as Value
from sqlinvline fsivl , sqlinvheaher fsivh
where fsivl.HeaderKey = fsivh.HeaderKey
and fsivh.InvoiceType = 'I'
and year(fsivh.InvoiceDate)* 100 + month(fsivh.InvoiceDate)
= year(fsivho.InvoiceDate)* 100 + month(fsivho.InvoiceDate)
and MONTH(fsivh.InvoiceDate) = 2
) as 'Feb',
from sqlinvline fsivlo , sqlinvheader fsivho
where fsivlo.HeaderKey = fsivho.InvoiceHeaderKey
and fsivho.InvoiceType = 'I'
and year(fsivho.InvoiceDate) >= year(DATEadd(yyyy, -2, GETDATE()))
group by YEAR(fsivho.InvoiceDate)
order by Month1
(I've shortened the code for brevity but the other months are formatted the same way)
When I run it, I get multiple lines for the years with an entry for a value against each month and the rest as nulls...e.g.
year jan feb mar apr etc
2014 nul 1 nul nul
2014 6 nul nul nul
2014 nul nul 7 nul
etc
What am I doing wrong?
all sorted by rewriting the query after many hours looking at pivot examples.
SELECT * from
(select year(fsivho.InvoiceDate) as Yr, cast(datename(MONTH ,fsivho.InvoiceDate) as CHAR(3)) as Mth,
cast(fsivlo.ShipQuantity * fsivlo.InvoiceLocalUnitPrice as decimal(8,2)) as Value
from ARInvoiceLine fsivlo , ARInvoiceHeader fsivho
where fsivlo.HeaderKey = fsivho.HeaderKey
and fsivho.InvoiceType = 'I'
and year(fsivho.InvoiceDate) >= year(DATEadd(yyyy, -4, GETDATE()))) src
PIVOT
(sum(Value)
for Mth in ([Jan],[Feb],[Mar],[Apr],[May],[Jun],[Jul],[Aug],[Sep],[Oct],[Nov],[Dec])) as pvt
order by Yr;
so now have rows of years with months across the top and values in the correct place!

Get last 6 months monthname.month number and Years in simple select statement

How to get last 6 months month name, month number and Years in simple select statement in sqlserver .
The no of months is 6, and is fixed
12 Dec 2015
11 Nov 2015
10 Oct 2015
9 Sep 2015
8 Aug 2015
7 Jul 2015
6 Jun 2015
This should handle year end boundaries
say, if the current month is Feb 2016, the result should give 2015 months.
2 Feb 2016
1 Jan 2016
12 Dec 2015
11 Nov 2015
10 Oct 2015
9 Sep 2015
8 Aug 2015
You can do it with the following:
SELECT MONTH(DATEADD(mm, -m, GETDATE())) AS m,
LEFT(DATENAME(mm, DATEADD(mm, -m, GETDATE())), 3) AS n,
YEAR(DATEADD(mm, -m, GETDATE())) AS y
FROM (VALUES (0),(1),(2),(3),(4),(5),(6)) t(m)
Output:
m n y
12 Dec 2015
11 Nov 2015
10 Oct 2015
9 Sep 2015
8 Aug 2015
7 Jul 2015
6 Jun 2015
Try this
;with cte as
(
select 0 as num
union all
select num+1 from cte where num<6
)
select month(dates),datename(month,dates),year(dates)
from
(
select dateadd(mm,-num,datadd(dd,1,eomonth(getdate(),-1))) as dates
from cte
) A
SQL FIDDLE DEMO
select datepart(m,GETDATE()) MonthNumber,left(datename(month,GETDATE()),3) as Month,year(GETDATE()) as Year union all
select datepart(m,DATEADD(month,-1,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-1,GETDATE())),3) as Month,year(DATEADD(month,-1,GETDATE())) as Year union all
select datepart(m,DATEADD(month,-2,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-2,GETDATE())),3) as Month,year(DATEADD(month,-2,GETDATE())) as Year union all
select datepart(m,DATEADD(month,-3,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-3,GETDATE())),3) as Month,year(DATEADD(month,-3,GETDATE())) as Year union all
select datepart(m,DATEADD(month,-4,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-4,GETDATE())),3) as Month,year(DATEADD(month,-4,GETDATE())) as Year union all
select datepart(m,DATEADD(month,-5,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-5,GETDATE())),3) as Month,year(DATEADD(month,-5,GETDATE())) as Year union all
select datepart(m,DATEADD(month,-6,GETDATE())) MonthNumber,left(datename(month,DATEADD(month,-6,GETDATE())),3) as Month,year(DATEADD(month,-6,GETDATE())) as Year
The above query will work for most of the RDBMS.
For SQL Server specific use the below query.
SELECT MONTH(DATEADD(month, -month, GETDATE())) AS MonthNumber ,
LEFT(DATENAME(MONTH, DATEADD(month, -month, GETDATE())), 3) AS MonthName,
YEAR(DATEADD(month, -month, GETDATE())) AS Year
FROM ( VALUES (0), (1), (2), (3), (4), (5),(6) ) t ( month )
Try DATEADD:
Select * FROM Table where YourDate>=DATEADD(m, -6, GETDATE())

Resources