How to create stored procedure for alternative working Saturdays? - sql-server

I want to create a stored procedure for displaying working Saturdays.
For example:
Last working Saturday 08/08/2015 (dd/mm/yyyy), then next working Saturday should be 22/08/2015 (dd/mm/yyyy)
It should omit between Saturday (15/08/2015). Likewise, I want to show for particular year

Try this. The assumption is, 3rd Jan 2015 was the first working Saturday.
DECLARE #firstsaturday date = '2015-01-03'
;WITH CTE AS
(
SELECT #firstsaturday AS StartDate
UNION ALL
SELECT DATEADD(WK,2,StartDate)
FROM CTE
WHERE YEAR(StartDate) = '2015'
)
SELECT * FROM CTE
WHERE YEAR(StartDate) = '2015'

Try something like this
DECLARE #start_sat_day DATE = Dateadd(d, -( Datepart(dw, Getdate()) % 7 ), Getdate());
WITH cte
AS (SELECT #start_sat_day AS sat_days
UNION ALL
SELECT Dateadd(dd, 14, sat_days)
FROM cte
WHERE Year(sat_days) = Year(#start_sat_day))
SELECT sat_days
FROM cte
WHERE Year(sat_days) = Year(#start_sat_day)
Result :
sat_days
---------
2015-08-08
2015-08-22
2015-09-05
2015-09-19
2015-10-03
2015-10-17
2015-10-31
2015-11-14
2015-11-28
2015-12-12
2015-12-26

Related

SQL - Counting incidents at time period (done) but including another variable

I'm newish to SQL so sorry if the code is a little scruffy.
Basically I am creating a count of fire engines in use on every hour, which I have done, and that bit works. So I have a count of this for the past five years. Sorted.
But now I want to run it for a specific group of incidents (about 300 of them), showing how many engines were at that incident, every hour, and how many others were in use at the same time, but somewhere else.
My basic working code (that I modified from https://stackoverflow.com/a/43337534/5880512) is as follows. It just counts all P1 and P2 mobilisations at the defined time.
DECLARE #startdate datetime = '2018-05-03 00:00:00'
DECLARE #enddate datetime = '2018-05-05 00:00:00'
;with cte as
(
select #startdate startdate
union all
select DATEADD(minute, 60, startdate)
FROM cte
WHERE DATEADD(minute, 60, startdate) < #enddate
)
select convert(varchar(20), startdate, 120) as CreationTime, (select count(*) FROM MB_MOBILISATIONS WHERE MB_SEND < startdate and MB_LEAVE > startdate And (MB_CALL_SIGN Like '%P1' Or MB_CALL_SIGN Like '%P2')) as Count
from cte
option (maxrecursion 0)
To split these up for a particular incident, I can put the incident ref into the where clause, one as = so it will give me engines at that incident, and one as <> so it gives me the rest. This bit works too.
select convert(varchar(20), startdate, 120) as CreationTime, (select count(*) FROM MB_MOBILISATIONS WHERE MB_SEND < startdate and MB_LEAVE > startdate And (MB_CALL_SIGN Like '%P1' Or MB_CALL_SIGN Like '%P2') and MB_IN_REF = 1704009991) as 'At Incident'
, select convert(varchar(20), startdate, 120) as CreationTime, (select count(*) FROM MB_MOBILISATIONS WHERE MB_SEND < startdate and MB_LEAVE > startdate And (MB_CALL_SIGN Like '%P1' Or MB_CALL_SIGN Like '%P2') and MB_IN_REF <> 1704009991) as 'Other Incident'
The bit I can't work out to do, is to make this work for multiple incidents, without having to change the incident reference manually in the where clause for all 300.
The incident references I want to use will be stored in a temporary table. Ideally, I would like it to pick an ID, set the variables #startdate and #enddate, from the start and end of that incident, then do the hourly count for the duration of that incident.
Hopefully the results would look something like this
IncidentRef DateTime At Incident Other Incident
A 2018-05-03 1:00 4 2
A 2018-05-03 2:00 7 3
A 2018-05-03 3:00 5 3
A 2018-05-03 4:00 2 4
B 2017-03-01 9:00 7 2
B 2017-03-01 10:00 8 3
B 2017-03-01 11:00 6 1
B 2017-03-01 12:00 4 2
I hope that makes sense.
Thanks :)
Use something like this to limit the scope of your search to a smaller list. I've just added and referenced another CTE with a filter. If you're looking to parameterize the list you'll need a different approach like storing those id values in another table first.
with cte as (
select #startdate startdate
union all
select dateadd(minute, 60, startdate)
from cte
where dateadd(minute, 60, startdate) < #enddate
), mobi as (
select * from MB_MOBILISATIONS
where MB_IN_REF in (<insert list here>)
)
select convert(varchar(20), startdate, 120) as CreationTime, m."Count"
from cte cross apply (
select count(*) as "Count" from mobi
where MB_SEND < startdate and MB_LEAVE > startdate and
(MB_CALL_SIGN like '%P1' or MB_CALL_SIGN like '%P2')
) m;
I went ahead and rewrote your scalar subquery but I guess that's just a personal preference.

Select Date query in SQL

i have a date column now i want to write "S" when there is sunday like if i have data like this
datecolumn
1-1-2017
2-1-2017
3-1-2017
4-1-2017
5-1-2017
6-1-2017
7-1-2017
8-1-2017
9-1-2017
10-1-2017
11-1-2017
12-1-2017
13-1-2017
in this if on date there is Sunday day then how to write "Sun" instead on date like this
select datecolumn from table1
datecolumn
1-1-2017
2-1-2017
3-1-2017
Sun
5-1-2017
6-1-2017
7-1-2017
8-1-2017
9-1-2017
10-1-2017
Sun
12-1-2017
13-1-017
If you are using SQL Server 2012+, using IIF Statement.
select
IIF(DATEPART(DW, DateVal) = 1, 'Sun', Convert(varchar(12), datecolumn, 103)) AS datecolumn
from tableName
Try something like this:
SELECT CASE WHEN DATEPART(DW, datecolumn) = 1 THEN 'Sun' ELSE CAST(datecolumn AS NVARCHAR(MAX)) END AS datecolumn
FROM table1
Hope it helps.
PS - might have to modify the CAST(datecolumn AS NVARCHAR(MAX)) to a CONVERT if you need the dates in a specific date format.

GROUP BY DAY, CUMULATIVE SUM

I have a table in MSSQL with the following structure:
PersonId
StartDate
EndDate
I need to be able to show the number of distinct people in the table within a date range or at a given date.
As an example i need to show on a daily basis the totals per day, e.g. if we have 2 entries on the 1st June, 3 on the 2nd June and 1 on the 3rd June the system should show the following result:
1st June: 2
2nd June: 5
3rd June: 6
If however e.g. on of the entries on the 2nd June also has an end date that is 2nd June then the 3rd June result would show just 5.
Would someone be able to assist with this.
Thanks
UPDATE
This is what i have so far which seems to work. Is there a better solution though as my solution only gets me employed figures. I also need unemployed on another column - unemployed would mean either no entry in the table or date not between and no other entry as employed.
CREATE TABLE #Temp(CountTotal int NOT NULL, CountDate datetime NOT NULL);
DECLARE #StartDT DATETIME
SET #StartDT = '2015-01-01 00:00:00'
WHILE #StartDT < '2015-08-31 00:00:00'
BEGIN
INSERT INTO #Temp(CountTotal, CountDate)
SELECT COUNT(DISTINCT PERSON.Id) AS CountTotal, #StartDT AS CountDate FROM PERSON
INNER JOIN DATA_INPUT_CHANGE_LOG ON PERSON.DataInputTypeId = DATA_INPUT_CHANGE_LOG.DataInputTypeId AND PERSON.Id = DATA_INPUT_CHANGE_LOG.DataItemId
LEFT OUTER JOIN PERSON_EMPLOYMENT ON PERSON.Id = PERSON_EMPLOYMENT.PersonId
WHERE PERSON.Id > 0 AND DATA_INPUT_CHANGE_LOG.Hidden = '0' AND DATA_INPUT_CHANGE_LOG.Approved = '1'
AND ((PERSON_EMPLOYMENT.StartDate <= DATEADD(MONTH,1,#StartDT) AND PERSON_EMPLOYMENT.EndDate IS NULL)
OR (#StartDT BETWEEN PERSON_EMPLOYMENT.StartDate AND PERSON_EMPLOYMENT.EndDate) AND PERSON_EMPLOYMENT.EndDate IS NOT NULL)
SET #StartDT = DATEADD(MONTH,1,#StartDT)
END
select * from #Temp
drop TABLE #Temp
You can use the following query. The cte part is to generate a set of serial dates between the start date and end date.
DECLARE #ViewStartDate DATETIME
DECLARE #ViewEndDate DATETIME
SET #ViewStartDate = '2015-01-01 00:00:00.000';
SET #ViewEndDate = '2015-02-25 00:00:00.000';
;WITH Dates([Date])
AS
(
SELECT #ViewStartDate
UNION ALL
SELECT DATEADD(DAY, 1,Date)
FROM Dates
WHERE DATEADD(DAY, 1,Date) <= #ViewEndDate
)
SELECT [Date], COUNT(*)
FROM Dates
LEFT JOIN PersonData ON Dates.Date >= PersonData.StartDate
AND Dates.Date <= PersonData.EndDate
GROUP By [Date]
Replace the PersonData with your table name
If startdate and enddate columns can be null, then you need to add
addditional conditions to the join
It assumes one person has only one record in the same date range
You could do this by creating data where every start date is a +1 event and end date is -1 and then calculate a running total on top of that.
For example if your data is something like this
PersonId StartDate EndDate
1 20150101 20150201
2 20150102 20150115
3 20150101
You first create a data set that looks like this:
EventDate ChangeValue
20150101 +2
20150102 +1
20150115 -1
20150201 -1
And if you use running total, you'll get this:
EventDate Total
2015-01-01 2
2015-01-02 3
2015-01-15 2
2015-02-01 1
You can get it with something like this:
select
p.eventdate,
sum(p.changevalue) over (order by p.eventdate asc) as total
from
(
select startdate as eventdate, sum(1) as changevalue from personnel group by startdate
union all
select enddate, sum(-1) from personnel where enddate is not null group by enddate
) p
order by p.eventdate asc
Having window function with sum() requires SQL Server 2012. If you're using older version, you can check other options for running totals.
My example in SQL Fiddle
If you have dates that don't have any events and you need to show those too, then the best option is probably to create a separate table of dates for the whole range you'll ever need, for example 1.1.2000 - 31.12.2099.
-- Edit --
To get count for a specific day, it's possible use the same logic, but just sum everything up to that day:
declare #eventdate date
set #eventdate = '20150117'
select
sum(p.changevalue)
from
(
select startdate as eventdate, 1 as changevalue from personnel
where startdate <= #eventdate
union all
select enddate, -1 from personnel
where enddate < #eventdate
) p
Hopefully this is ok, can't test since SQL Fiddle seems to be unavailable.

How to select the beginning of the following April in SQL Server?

Could you please help me select a date which is the beginning of a particular following month, e.g. April?
For example, if it is Jan 08 2013, it should select April 01 2013, but if it is June 08 2013, it should select April 01 2014.
Thanks.
I would create a calendar table, then you can simply do something like this:
select
min([Date])
from
dbo.Calendar
where
MonthNumber = 4 and
DayNumber = 1 and
[Date] > getdate()
Querying a calendar table is usually clearer, simpler and more flexible than using date functions. You might also want to consider what happens if today is April 1: do you want today's date, or next year's?
If you're interested in April 1 because it's the start of a financial year, you can add that information to your calendar table directly:
select
min([Date])
from
dbo.Calendar
where
IsStartOfFinancialYear = 0x1 and
[Date] > getdate()
Use the DATEADD and the DATEPART function, like this short example:
DECLARE #Date Datetime
SET #Date = '2013.01.08 00:00:00'
SELECT DATEADD(year,
CASE WHEN DATEPART(month, #Date) < 4
THEN 0
ELSE 1 END,
DATEADD(day, -DATEPART(day, #Date) + 1,
DATEADD(month, -DATEPART(month, #Date) + 4, #Date)))

Convert month name to month number in SQL Server

In T-SQL what is the best way to convert a month name into a number?
E.g:
'January' -> 1
'February' -> 2
'March' -> 3
Etc.
Are there any built in functions that can do this?
How about this?
select DATEPART(MM,'january 01 2011') -- returns 1
select DATEPART(MM,'march 01 2011') -- returns 3
select DATEPART(MM,'august 01 2011') -- returns 8
How about this:
SELECT MONTH('March' + ' 1 2014')
Would return 3.
Its quit simple,
Take the first 3 digits of the month name and use this formula.
Select charindex('DEC','JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC')/4+1
SELECT DATEPART(MM,'january '+'01 1900')
SELECT MONTH('january ' + '01 1900')
SELECT month(dateadd(month,DATEDIFF(month,0,'january 01 2015'),0))
You can create a function and then refer to it in the select statement.
The function may look similar to this:
if OBJECT_ID('fn_month_name_to_number', 'IF') is not null
drop function fn_month_name_to_number
go
create function fn_month_name_to_number (#monthname varchar(25))
returns int as
begin
declare #monthno as int;
select #monthno =
case #monthname
when 'January' then 1
when 'February' then 2
when 'March' then 3
when 'April' then 4
when 'May' then 5
when 'June' then 6
when 'July' then 7
when 'August' then 8
when 'September' then 9
when 'October' then 10
when 'November' then 11
when 'December' then 12
end
return #monthno
end
Then you can query it.
select fn_month_name_to_number ('February') as month_no
This query will return 2 as month number.
You can pass values from a column as parameters to the function.
select fn_month_name_to_number (*columnname*) as month_no from *tablename*
Have a good day!
There is no built in function for this.
You could use a CASE statement:
CASE WHEN MonthName= 'January' THEN 1
WHEN MonthName = 'February' THEN 2
...
WHEN MonthName = 'December' TNEN 12
END AS MonthNumber
or create a lookup table to join against
CREATE TABLE Months (
MonthName VARCHAR(20),
MonthNumber INT
);
INSERT INTO Months
(MonthName, MonthNumber)
SELECT 'January', 1
UNION ALL
SELECT 'February', 2
UNION ALL
...
SELECT 'December', 12;
SELECT t.MonthName, m.MonthNumber
FROM YourTable t
INNER JOIN Months m
ON t.MonthName = m.MonthName;
I recently had a similar experience (sql server 2012). I did not have the luxury of controlling the input, I just had a requirement to report on it. Luckily the dates were entered with leading 3 character alpha month abbreviations, so this made it simple & quick:
TRY_CONVERT(DATETIME,REPLACE(obs.DateValueText,SUBSTRING(obs.DateValueText,1,3),CHARINDEX(SUBSTRING(obs.DateValueText,1,3),'...JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC')/4))
It worked for 12 hour:
Feb-14-2015 5:00:00 PM 2015-02-14 17:00:00.000
and 24 hour times:
Sep-27-2013 22:45 2013-09-27 22:45:00.000
(thanks ryanyuyu)
I think you may even have a separate table like a monthdetails (Monthno int, monthnames char(15)) and include values:
1 January
2 February
.... and so on, and then join this table with your existing table in the monthnames column
SELECT t1.*,t2.Monthno from table1
left outer join monthdetails t2
on t1.monthname=t2.monthnames
order by t2.Monthno
You can use below code
DECLARE #T TABLE ([Month] VARCHAR(20))
INSERT INTO #T
SELECT 'January'
UNION
SELECT 'February'
UNION
SELECT 'March'`
SELECT MONTH('01-' + [Month] + '-2010') As MonthNumeric,[Month] FROM #T
ORDER BY MonthNumeric
You can try sth like this, if you have month_name which is string datetype.After converting, you can feel free to order by Month.
For example, your table like this:
month
Dec
Jan
Feb
Nov
Mar
.
.
.
My syntax is:
Month(cast(month+'1 2016' as datetime))
You can do it this way, if you have the date (e.g. SubmittedDate)
DATENAME(MONTH,DATEADD(MONTH, MONTH(SubmittedDate) - 1, 0)) AS ColumnDisplayMonth
Or you can do it this way, if you have the month as an int
DATENAME(MONTH,DATEADD(MONTH, #monthInt - 1, 0)) AS ColumnDisplayMonth
I know this may be a bit too late but the most efficient way of doing this through a CTE as follows:
WITH Months AS
(
SELECT 1 x
UNION all
SELECT x + 1
FROM Months
WHERE x < 12
)
SELECT x AS MonthNumber, DateName( month , DateAdd( month , x , -1 )) AS MonthName FROM Months
try this
SELECT EXTRACT(MONTH FROM TO_DATE(month_added, 'Month')) AS month_number
select Convert(datetime, '01 ' + Replace('OCT-12', '-', ' '),6)

Resources