Order By Month Name in Sql-Server - sql

I have Below Table named session
SessionID SessionName
100 August
101 September
102 October
103 November
104 December
105 January
106 May
107 June
108 July
I executed the following query I got the output as below.
Select SessionID, SessionName
From dbo.Session
SessionID SessionName
100 August
101 September
102 October
103 November
104 December
105 January
106 May
107 June
108 July
the results get ordered by Session ID. But I need the output as below,
SessionID SessionName
106 May
107 June
108 July
100 August
101 September
102 October
103 November
104 December
105 January
How to achieve this in sql-server? thanks for the help

I'd use a case expression, like:
order by case SessionName when 'August' then 1
when 'September' then 2
...
when 'Juty' then 12
end
August has 1 because "in the application logic a session started with august", easy to renumber if you want to start with January and end with December.

To avoid any hassel with culture dependencies, you might get the month's index out of sys languages with a query like this:
(I'd eventually create a TVF from this and pass in the langid as parameter)
SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) MyMonthIndex
,Mnth.value('.','varchar(100)') AS MyMonthName
FROM
(
SELECT CAST('<x>' + REPLACE(months,',','</x><x>') + '</x>' AS XML) AS XmlData
FROM sys.syslanguages
WHERE langid=0
) AS DataSource
CROSS APPLY DataSource.XmlData.nodes('/x') AS The(Mnth)
The result
1 January
2 February
3 March
4 April
5 May
6 June
7 July
8 August
9 September
10 October
11 November
12 December
EDIT: An UDF for direct usage (e.g. in an order by)
CREATE FUNCTION dbo.GetMonthIndexFromMonthName(#MonthName VARCHAR(100),#langId INT)
RETURNS INT
AS
BEGIN
RETURN
(
SELECT MyMonthIndex
FROM
(
SELECT CAST(ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS INT) MyMonthIndex
,Mnth.value('.','varchar(100)') AS MyMonthName
FROM
(
SELECT CAST('<x>' + REPLACE(months,',','</x><x>') + '</x>' AS XML) AS XmlData
FROM sys.syslanguages
WHERE langid=#langId
) AS DataSource
CROSS APPLY DataSource.XmlData.nodes('/x') AS The(Mnth)
) AS tbl
WHERE MyMonthName=#MonthName
);
END
GO
SELECT dbo.GetMonthIndexFromMonthName('February',0)

I think you can do something like this:
SELECT * FROM session
ORDER BY MONTH(session.SessionName + ' 1 2014')
The part:
MONTH(session.SessionName + ' 1 2014')
Will return 3 of the month is march and you do not really need to care about the year in this case

Try CAST your Month Name (SessionName) into DATETIME format, just like this
SELECT * FROM table
ORDER BY DATEPART(mm, CAST(SessionName + '1900' AS DATETIME)) asc

You can convert the string month into number and sort by numer form month
SELECT SessionID, SessionName, DATEPART(MM, SessionName)
FROM dbo.Session
ORDER BY DATEPART(MM, SessionName)
otherwise, if you have something like "DateCreation" column, you can do something like this
SELECT SessionID, SessionName, MONTH(DateCreation)
FROM dbo.Session
ORDER BY MONTH(DateCreation)

Try with this .. I am assuming that you want next month of current Date is starting point so I am using "Month(GetDate()" in script otherwise you can hard-code to 4 to get desired result
Declare #Session Table (SessionID INT, SessionName Varchar(20))
Insert Into #Session Values (100,'August'),(101,'September'),(102,'October'),(103,'November'),(104,'December')
,(105,'January'),(106,'May'),(107,'June'),(108,'July')
Select * From #Session
ORDER BY CASE WHEN Month(CAST('01-' + SessionName + ' 2016' AS DateTime)) - Month(GetDate()) <= 0
THEN 12 + Month(CAST('01-' + SessionName + ' 2016' AS DateTime)) - Month(GetDate())
ELSE Month(CAST('01-' + SessionName + ' 2016' AS DateTime)) - Month(GetDate()) END
For GERMAN Language ..use below script .. In this i used soundex for month comparision
SET LANGUAGE GERMAN;
Declare #Session Table (SessionID INT, SessionName Varchar(20))
Insert Into #Session Values (100,'August'),(101,'September'),(102,'October'),(103,'November'),(104,'December')
,(105,'January'),(106,'May'),(107,'June'),(108,'July')
Select * From #Session S
INNER JOIN
(SELECT Number + 1 as [MonthNumber],
DateName(mm,DATEADD(mm,Number,0)) as [MonthName]
FROM master..spt_values
WHERE Type = 'P' and Number < 12
)M ON SoundEx(M.MonthName)=SoundEx(S.SessionName)
ORDER BY CASE WHEN [MonthNumber] - Month(GetDate()) <= 0
THEN 12 + [MonthNumber] - Month(GetDate())
ELSE [MonthNumber] - Month(GetDate()) END

Related

Get previous month date values from data stored within SQL Server table

My table structure in SQL Server looks as below.
id startdate enddate value
---------------------------------------
1 2019-02-06 2019-02-07 11
1 2019-01-22 2019-02-05 10
1 2019-01-15 2019-01-21 14
1 2018-12-13 2018-01-14 15
1 2018-12-09 2018-12-12 14
1 2018-08-13 2018-12-08 17
1 2018-07-19 2018-08-12 19
1 2018-06-13 2018-07-18 20
Now my query needs to display value from highest start date for that month. Which is fine and I know what needs to be done but Not start just highest date value for that month, if no value is there for that start date, we carry forward value from last month. So basically if you notice on above data, after December 2018 values, there are no values for November, October, September etc but I want to return MM/YYYY values for that month in result but value for those months should be what we found on earlier month which is August values which in this example is 17. Please note that enddate will always be as of one day before new start date begins. Probably that can be used for back filling and carry forwarding missing month values?
So my result should look like below.
id date value
----------------------------
1 2019-02 11
1 2019-01 10
1 2018-12 15
1 2018-11 17
1 2018-10 17
1 2018-09 17
1 2018-08 17
1 2018-07 19
1 2018-06 20
Do you think this can be done without using cursor here?
Alexander Volok's answer is solid, so I won't go into too much extra code. But I thought I'd explain the reasoning. In essence, what you need to do is create a skeleton date table containing all the dates and primary keys you want returned. I'm guessing you have more than one id value in your real data, so probably something like this (whether you choose to persist it or not is up to you)
create table #skelly
(
id int,
_year int,
_month int
primary key (id, _year, _month)
)
You can get much more precise if you need to be, by only including dates which fall between the min and max StartDate per id, but that's an exercise I leave up to you.
From there, it's then just a matter of filling in the values you care about against that skeleton table. You can do this in a number of ways; by joining, cross applying or a correlated subquery (as Alexander Volok used).
DECLARE #start DATE, #end DATE;
SELECT #start = '20180601', #end = GETDATE();
;WITH Months AS
(
SELECT EOMONTH(DATEADD(month, n-1, #start)) AS DateValue FROM (
SELECT TOP (DATEDIFF(MONTH, #start, #end) + 1)
n = ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_objects
) D
)
, InputData AS
(
SELECT 1 AS id, '2019-02-06' startdate, '2019-02-07' as enddate, 11 AS [value] UNION ALL
SELECT 1, '2019-01-22', '2019-01-25', 10 UNION ALL
SELECT 1, '2019-01-15', '2019-01-17', 14 UNION ALL
SELECT 1, '2018-12-13', '2018-12-19', 15 UNION ALL
SELECT 1, '2018-12-09', '2018-12-10', 14 UNION ALL
SELECT 1, '2018-08-13', '2018-12-08', 17 UNION ALL
SELECT 1, '2018-07-19', '2018-07-25', 19 UNION ALL
SELECT 1, '2018-06-13', '2018-07-18', 20
)
SELECT FORMAT(m.DateValue, 'yyyy-MM') AS [Month]
, (SELECT TOP 1 I.value FROM InputData I WHERE I.startdate < M.DateValue ORDER BY I.startdate DESC ) [Value]
FROM months m
ORDER BY M.DateValue DESC
Results to:
Month Value
2019-02 11
2019-01 10
2018-12 15
2018-11 17
2018-10 17
2018-09 17
2018-08 17
2018-07 19
2018-06 20

Get most recent year of records on year and month field

I have a table such as this:
Id | Year | Month
1 | 2018 | 1
2 | 2018 | 2
3 | 2018 | 3
I need to select the most recent 12 months of ids and here is what I came up with:
SELECT TOP 12 [Id],
YEAR([DateReported]) AS [Year], MONTH([DateReported]) AS [Month]
FROM (
SELECT [Id], CAST(CAST([Year] AS VARCHAR(10)) + '/' + CAST([Month] AS VARCHAR(10)) + '/1' AS DATETIME) AS DateReported
FROM [MyTable]
) T
ORDER BY T.[DateReported] DESC
However, I'm doing far too much casting, is there a more efficient way?
Assuming your Select returns the correct result there's no need for any kind of calculation, simply order using:
select top 12 Id, [year], [month]
from MyTable
order by [year] desc, [month] desc
How about this:
select t.*
from t
where year * 12 + month >= year(getdate()) * 12 + month(getdate()) * 12 - 12;
Looks like you don't want the most recent 12, you want the ones from the most recent 12 months (aka a year). You don't need to do any casting at all....
declare #t table(id int identity(1,1), year int, month int);
insert into #t(year, month)
values (2017,11)
,(2017,11)
,(2017,12)
,(2017,12)
,(2018,1)
,(2018,2)
,(2018,1)
select *
from #t t
where format(t.year , '0000')+'-' +format(t.month, '00') >= format(dateadd(year, -1, getdate()), 'yyyy-MM')
I would sort by month (descending), then I would get first 12 rows, as in:
select top 12 id
from my_table
order by year * 12 + month desc

Incremental and decremental count based on a date

Name Start_date end_date
aaa 01/02/2017 05/03/2017
bbb 03/05/2017 07/07/2017
ccc 02/01/2017 10/09/2017
I want to write a query that calculates the number of people who exist in the DB in a certain month/year.
Answer:
Jan 2017 1
Feb 2017 2
Mar 2017 3
Apr 2017 3
May 2017 2 (one person - aaa ,ended in May 2017)
Jun 2017 2
Jul 2017 1 (bbb ended in July 2017)
How do I write a PSQL query to get the desired output?
Thanks.
First, get the max and min dates in order to declare the dates range.
Second, with etc select all the month in the range.
Third, sum the number of the records in each dates.
Like:
declare #date date
declare #toDate date
select #date = min(Start_date),
#toDate = max(end_date)
from table_name
;With dt As
(
Select #date As [TheDate]
Union All
Select DateAdd(month, 1, TheDate) From dt Where [TheDate] < #toDate
)
select month(dt.TheDate),
year(dt.TheDate),
sum(case when table_name.Name is not null then 1 else 0 end)
from dt
left join table_name
on table_name.Start_date >= dt.TheDate
and table_name.end_date < dateadd(day,-1,dateAdd(month,1,dt.TheDate))

SQL self join pairwise

Suppose I have a table consisting of entries like
ID Arrival Date Arrival City Departure Date Departure City
1 Jun 27 2015 Berlin Jun 20 2015 Paris
1 Jul 1 2015 Rome Jun 29 2015 Berlin
1 Jul 30 2015 Vienna Jul 15 2015 Rome
2 Jun 28 2015 Prague Jun 23 2015 Vienna
2 Jul 1 2015 Rome Jun 29 2015 Prague
2 Jul 30 2015 Vienna Jul 15 2015 Moscow
...
and for each ID I want to join this data on itself such that observations with subsequent Departure Date and Arrival Date are grouped pairwise - i.e. a departure is paired with the previous arrival for each ID.
In the example above (where the observations are sorted for convenience) the 2nd row would be appended to the 1st, the 3rd to the 2nd, the 5th to the 4th and the 6th to the 5th (thus producing 4 rows with fields ID Arrival Date Arrival City Departure Date Departure City Arrival Date2 Arrival City2 Departure Date2 Departure City2).
There could potentially be more than three departures for each ID so a general approach is required. Also please note that there can be holes in the data where Arrival City and Departure City does not match - e.g. the Arrival City of the 5th row is not the Departure City of the 6th row but they should still be merged. In fact a major goal is to get a better view of how many holes there are in the data.
A solution is to use a CTE and consider that the difference between two consecutive rows (identified by the rowno) is 1 all the time (and also consider the dates):
;WITH CTE AS (
SELECT
rownum = ROW_NUMBER() OVER (ORDER BY t.ID, t.arrivalDate),
t.ID,
t.arrivalDate,
t.arrivalCity,
t.departureDate,
t.departureCity
FROM #test t
)
SELECT *
FROM CTE c1
JOIN CTE c2
ON c1.ID = c2.ID
AND c2.departureDate > c1.arrivalDate
AND c2.rownum - c1.rownum = 1
GO
-- structure of the #test table
CREATE TABLE #test (
ID int,
arrivalDate date,
arrivalCity varchar(30),
departureDate date,
departureCity varchar(30)
)
SQL fiddle here: SQLFiddle
Try this:
SELECT a.id
,a.arrival_date
,a.arrival_city
,a.departure_date
,a.departure_city
,b.arrival_date arrival_date_2
,b.arrival_city arrival_city_2
,b.departure_date departure_date_2
,b.departure_city departure_city_2
FROM triptable a
JOIN triptable b ON a.id = b.id
AND a.departure_date = (SELECT min(departure_date) FROM so34815894 x WHERE x.departure_date > b.arrival_date AND x.id = b.id)
Edited based on your comment to:
find the record with the earliest departure date after the previous record's
arrival date, and
ignore the fact that the sample data's 6th record has a different
departure city than the 5th record's arrival city.
Not entirely sure what result set your looking for.. but I thought I'd give this a shot and see if any of these help you out.
drop table #t1
create table #t1 (id int, ArrivalDate datetime, ArrivalCity varchar(50), Departuredate datetime, DepartureCity varchar(50))
insert into #t1
values (1, 'Jun 27 2015', 'Berlin', 'Jun 20 2015','Paris'),
(1, 'Jul 1 2015', 'Rome','Jun 29 2015','Berlin'),
(1, 'Jul 30 2015', 'Vienna','Jul 15 2015','Rome'),
(2, 'Jun 28 2015','Prague','Jun 23 2015','Vienna'),
(2, 'Jul 1 2015','Rome','Jun 29 2015','Prague'),
(2, 'Jul 30 2015','Vienna','Jul 15 2015','Moscow')
select *, case when lead(departurecity) over (partition by id order by Arrivaldate) = ArrivalCity or lead(departurecity) over (partition by id order by Arrivaldate) is null then 1 else 0 end as PairID into #t2 from #t1
update #t2
set PairID = id
where pairid != id
and pairid != 0
This is the code to start up..
select * from #t2
will result in:
id ArrivalDate ArrivalCity Departuredate DepartureCity PairID
1 2015-06-27 Berlin 2015-06-20 Paris 1
1 2015-07-01 Rome 2015-06-29 Berlin 1
1 2015-07-30 Vienna 2015-07-15 Rome 1
2 2015-06-28 Prague 2015-06-23 Vienna 2
2 2015-07-01 Rome 2015-06-29 Prague 0
2 2015-07-30 Vienna 2015-07-15 Moscow 2
Any location where the pair id = 0 ... you have a gap/baddata however you want to put it..
You could also:
select *, lead(departurecity) over (partition by ID order by ArrivalDate) as PreviousDepartureCity, lead(Departuredate) over (partition by ID order by ArrivalDate) as PreviousDepartureDate from #t2
This will add previous departure city and date.. and you can do what you want with the nulls.. they will signify the first flight.. or a gap if the subsequent pair id = 0 ...
The select options become endless.... if null and lag(pairid) = 0 then you have the row with the gap.. if null and pair id = id.. and lag(pairid) = id then you have your first flight..
I mean I can keep going.. and give you more details but I'm not sure this is what your looking for.. Hope it helped anyway..
Good luck!
P.S Didn't see why you needed to join the table to itself .. maybe I missed the whole point..lol..sorry if that's the case..
It sounds to me like you're wanting to pivot the results and put the results in additional columns. I used ROW_NUMBER() for the ordering. I concatenated the columns in a row prior to the pivot, pivoted, then used a function to reverse the concatenation.
SELECT
p.ID,
dbo.SplitString(p.[1], CHAR(13), 1) AS arrivalDate1,
dbo.SplitString(p.[1], CHAR(13), 2) AS arrivalCity1,
dbo.SplitString(p.[1], CHAR(13), 3) AS departureDate1,
dbo.SplitString(p.[1], CHAR(13), 4) AS departureCity1,
*
FROM
(
SELECT *
FROM
(
SELECT
ID,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY arrivalDate) RowNum,
CAST(arrivalDate AS VARCHAR(MAX)) + CHAR(13)
+ arrivalCity + CHAR(13)
+ CAST(departureDate AS VARCHAR(MAX)) + CHAR(13)
+ departureCity TripDetails
FROM trip t
) t
PIVOT (MIN(t.TripDetails) FOR t.RowNum IN ([1], [2], [3], [4], [5] /* , ... */)) p
) p;
using this SplitString function
CREATE FUNCTION dbo.SplitString (
#stringToSplit VARCHAR(MAX),
#delim VARCHAR(255),
#occurence INT )
RETURNS VARCHAR(MAX) AS
BEGIN
DECLARE #name NVARCHAR(255);
DECLARE #pos INT;
DECLARE #orderNum INT;
SET #orderNum=0;
WHILE CHARINDEX(#delim, #stringToSplit) > 0
BEGIN
SELECT #orderNum=#orderNum+1;
SELECT #pos = CHARINDEX(#delim, #stringToSplit) ;
SELECT #name = SUBSTRING(#stringToSplit, 1, #pos-1);
IF #orderNum = #occurence
BEGIN
RETURN #name;
END
SELECT #stringToSplit = SUBSTRING(#stringToSplit, #pos+1, LEN(#stringToSplit)-#pos)
END
SELECT #orderNum=#orderNum+1;
IF #orderNum = #occurence
BEGIN
RETURN #stringToSplit;
END
RETURN NULL;
END
This should work:
with cte as(select *, row_number() over(partition by id order by date) rn from table)
select * from cte c1
join cte c2 on c1.id = c2.id and c1.rn = c2.rn - 1
try this,
declare #t table(ID int,ArrivalDate datetime, ArrivalCity varchar(50)
,DepartureDate datetime,DepartureCity varchar(50))
insert into #t values
(1, 'Jun 27 2015', 'Berlin', 'Jun 20 2015', 'Paris ')
,(1, 'Jul 1 2015 ', 'Rome ', 'Jun 29 2015', 'Berlin ')
,(1, 'Jul 30 2015', 'Vienna', 'Jul 15 2015', 'Rome ')
,(2, 'Jun 28 2015', 'Prague', 'Jun 23 2015', 'Vienna ')
,(2, 'Jul 1 2015 ', 'Rome ', 'Jun 29 2015', 'Prague ')
,(2 , 'Jul 30 2015', 'Vienna', 'Jul 15 2015', 'Moscow ')
;WITH CTE
AS (
SELECT *
,ROW_NUMBER() OVER (
ORDER BY id
,arrivaldate
) rn
FROM #t
)
SELECT A.arrivaldate
,a.arrivalcity
,a.DepartureDate
,a.DepartureCity
,b.arrivaldate
,b.arrivalcity
,b.DepartureDate
,b.DepartureCity
FROM CTE A
LEFT JOIN CTE b ON a.rn + 1 = b.rn

SQL days available using a not exists on current financial year

I have a table called 'holiday table' which basically contains dates for all days where employees will not be in work (e.g bank holidays etc)
The below query is basically looking at the current financial year and working out how many days are available firstly by month, and then using the unuion all cummulatively, (e.g April-May, April-June) I dont need one for April though as I can use the non-cumulative for this.
See query:
DECLARE #StartDate DATETIME,
#EndDate DATETIME
--available days
--current – start of this financial year
SELECT #StartDate = (select
case when month(getdate()) >= 4 then
convert(datetime, cast(year(getdate()) as varchar) + '-4-1')
else
convert(datetime, cast(year(getdate())-1 as varchar) + '-4-1')
end),
--current – end of this financial year
#EndDate = (select
case when month(getdate()) < 4 then
convert(datetime, cast(year(getdate()) as varchar) + '-3-31')
else
convert(datetime, cast(year(getdate())+1 as varchar) + '-3-31')
end)
CREATE TABLE #data
(
firstday DATETIME NOT NULL PRIMARY KEY,
workingdays INT NOT NULL
);
WITH dayscte ([Date])
AS (SELECT #StartDate
UNION ALL
SELECT Dateadd(DAY, 1, [Date])
FROM dayscte
WHERE [Date] <= #Enddate)
INSERT INTO #data
SELECT MIN([Date]),
COUNT(*) [Day]
FROM dayscte
LEFT JOIN dbo.Holiday_Table
ON [Date] BETWEEN dbo.Holiday_Table.sch_cal_d AND dbo.Holiday_Table.sch_cal_ed
where
NOT EXISTS (
SELECT sch_id,sch_cal_d,sch_cal_ed FROM dbo.Holiday_Table WHERE
sch_id ='66291100Ks'
AND
[date] <= sch_cal_d
AND
[date] >= sch_cal_ed
)
AND Datename(weekday, [Date]) NOT IN ( 'Saturday', 'Sunday' )
GROUP BY Datepart(MONTH, [Date]),
Datepart(YEAR, [Date])
OPTION (MAXRECURSION 366)
DECLARE #Date DATETIME
SET #Date = (SELECT MIN(firstday)
FROM #data)
SELECT Period,
workingdays [Days_Available_Minus_Holidays] ,
year (firstday) AS [Year]
FROM (SELECT Datename(MONTH, firstday) [Period],
workingdays,
0 [SortField],
firstday
FROM #data
UNION
SELECT Datename(MONTH, #Date) + '-' + Datename(MONTH, firstday),
(SELECT SUM(workingdays)
FROM #data b
WHERE b.firstday <= a.firstday ) [WorkingDays],
1 [SortField],
firstday
FROM #data a
WHERE
firstday > #Date) data
ORDER BY sortfield,
firstday
DROP TABLE #data
GO
The results for this are as follows:
Period Days_Available_Minus_Holidays Year
April 19 2012
May 22 2012
June 19 2012
July 22 2012
August 22 2012
September 20 2012
October 23 2012
November 22 2012
December 19 2012
January 23 2013
February 20 2013
March 21 2013
April 1 2013
April-May 41 2012
April-June 60 2012
April-July 82 2012
April-August 104 2012
April-September 124 2012
April-October 147 2012
April-November 169 2012
April-December 188 2012
April-January 211 2013
April-February 231 2013
April-March 252 2013
April-April 253 2013
My problem is when I get to the cumulative, it does another 'April' and then at the bottom it does an 'April-April' I do not need a cumulative for April as it is only one month do basically I dont want the first or last cumulative values as April is covered by the non-cumulatives, or if the second 'April' must stay, then it should not read '1' as days available, by be the same as the non-cumulative, which is 19 as this is how many days are actually available.
Try removing the equals in your WITH clause
Change WHERE [Date] <= #Enddate to WHERE [Date] < #Enddate
It seems your adding a day to the date before the WHERE clause therefore it is overstepping by a day.