Adding a Date into a Table TSQL using Weighting - tsql

I have a Site Table where each site has a Rating between 1 and 4, I also have a capacity table where I want to add a start date using the rating from the site table, for example
A site with a rating of 1 the Start Date should be 01/05/2010 and a End Date 30/09/2010
A site with a rating of 2 and 3 the Start Date should be 01/04/2010 and a End Date 01/11/2010
A site with a rating of 4 the Start Date should be 01/01/2010 and a End Date 31/12/2010
I have had help with the first two columns already see code below, I now need to add the start date and end date. Can you help?
USE OCCUPANCY
CREATE TABLE Cap1
(PitchType_Skey int,
Site_Skey int)
DECLARE #PitchType_Skey INT
DECLARE #Site_Skey INT
SET #PitchType_Skey = 1
SET #Site_Skey = 1
WHILE (#Site_Skey < 127)
BEGIN
IF #PitchType_Skey = 8
BEGIN
SET #PitchType_Skey = 1
SET #Site_Skey = #Site_Skey + 1
END
IF (#Site_Skey < 127)
BEGIN
INSERT INTO dbo.Cap1 (PitchType_Skey, Site_Skey)
SELECT #PitchType_Skey, #Site_Skey
END
SET #PitchType_Skey = #PitchType_Skey + 1
END

In one command and using ISO dates
INSERT INTO dbo.Cap1 (PitchType_Skey, Site_Skey, StartDate, EndDate)
SELECT
ROW_NUMBER() OVER (ORDER BY Site_Skey),
Site_Skey /*from site table column*/,
CASE rating
WHEN 1 THEN '20100501'
WHEN 4 THEN '20100101'
ELSE '20100401'
END,
CASE rating
WHEN 1 THEN '20100930'
WHEN 4 THEN '20101231'
ELSE '20101101'
END
FROM Site

Related

T-SQL: Aggregate based on condition

there is a table:
start | end | zone
------|-----|-----
1 | 5 | 3
3 | 6 | 2
1 | 3 | 1
4 | 7 | 4
Start and end represent a range. I'd like to get a zone for value of 4, like
declare #value as int = 4
select zone from table where #value >= start and #value <= end
, and I obtain
3
2
4
, but I need only one zone from predefined range, for example 2 or 3 and 3 has priority, so if 2 and 3 are present, I'd like to get 3. So, zone 3 is desired result in my example.
How can I solve it?
If it's hard coded you can do something like this:
DECLARE #Value int = 4;
SELECT TOP 1 zone
FROM table
WHERE zone IN (3,2)
AND #Value >= start
AND #Value <= [end]
ORDER BY CASE WHEN zone = 3 THEN 0 ELSE 1 END
See a live demo on rextester
Update
SELECT TOP 1 CASE WHEN zone IN (3,2) THEN zone ELSE NULL END AS zone
FROM #T
WHERE #Value >= start
AND #Value <= [end]
ORDER BY CASE WHEN zone = 3 THEN 0 ELSE 1 END
Live demo for updated version
Update 2
Even though you wrote in your comment that you found a solution thanks to my post, I still want to post a solution here for future readers that might need it:
;WITH CTE AS
(
SELECT TOP 1 zone
FROM #T
WHERE zone IN (3,2)
AND #Value >= start
AND #Value <= [end]
ORDER BY CASE WHEN zone = 3 THEN 0 ELSE 1 END
)
SELECT zone
FROM CTE
UNION ALL
SELECT NULL
WHERE NOT EXISTS(
SELECT 1
FROM CTE
)

Find total number in a specific period of time SQL

I am trying to find the total number of members in a given period. Say I have the following data:
member_id start_date end_date
1 9/1/2013 12/31/2013
2 10/1/2013 11/12/2013
3 12/1/2013 12/31/2013
4 5/1/2012 8/5/2013
5 9/1/2013 12/31/2013
6 7/1/2013 12/31/2013
7 6/6/2012 12/5/2013
8 10/1/2013 12/31/2013
9 7/8/2013 12/31/2013
10 1/1/2012 11/5/2013
In SQL I need to create a report that will list out the number of members in each month of the year. In this case something like the following:
Date Members Per Month
Jan-12 1
Feb-12 1
Mar-12 1
Apr-12 1
May-12 2
Jun-12 3
Jul-12 3
Aug-12 3
Sep-12 3
Oct-12 3
Nov-12 3
Dec-12 3
Jan-13 3
Feb-13 3
Mar-13 3
Apr-13 3
May-13 3
Jun-13 3
Jul-13 5
Aug-13 4
Sep-13 6
Oct-13 8
Nov-13 6
Dec-13 6
So there is only 1 member from Jan-12 (member id 10) until May-12 when member id 4 joins making the count 2 and so on.
The date range can be all over so I can't specify the specific dates but it is by month, meaning that even if someone ends 12-1 it is considered active for the month for Dec.
I was able to create the following stored procedure that was able to accomplish what I needed:
USE [ValueBasedSandbox]
GO
/****** Object: StoredProcedure [dbo].[sp_member_count_per_month] Script Date: 01/08/2015 12:02:37 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Create date: 2015-08-01
-- Description: Find the counts per a given date passed in
-- =============================================
CREATE PROCEDURE [dbo].[sp_member_count_per_month]
-- Add the parameters for the stored procedure here
#YEAR int
, #ENDYEAR int
AS
DECLARE #FIRSTDAYMONTH DATETIME
DECLARE #LASTDAYMONTH DATETIME
DECLARE #MONTH INT = 1;
--Drop the temporary holding table if exists
IF OBJECT_ID('tempdb.dbo.##TEMPCOUNTERTABLE', 'U') IS NOT NULL
DROP TABLE dbo.##TEMPCOUNTERTABLE
CREATE TABLE dbo.##TEMPCOUNTERTABLE (
counter INT
, start_date DATETIME2
, end_date DATETIME2
)
--Perform this loop for each year desired
WHILE #YEAR <= #ENDYEAR
BEGIN
--Perform for each month of the year
WHILE (#MONTH <= 12)
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET #FIRSTDAYMONTH = DATEADD(MONTH, #MONTH - 1, DATEADD(YEAR, #YEAR-1900, 0))
SET #LASTDAYMONTH = DATEADD(MONTH, #MONTH, DATEADD(YEAR, #YEAR-1900, 0)-1)
INSERT INTO dbo.##TEMPCOUNTERTABLE(counter, start_date, end_date)
SELECT COUNT(*) AS counter
, #FIRSTDAYMONTH AS start_date
, #LASTDAYMONTH AS end_date
FROM dbo.member_table
WHERE start_date <= #LASTDAYMONTH
AND end_date >= #FIRSTDAYMONTH
--Increment through all the months of the year
SET #MONTH = #MONTH + 1
END -- End Monthly Loop
--Reset Month counter
SET #MONTH = 1
--Increment the desired years
SET #YEAR = #YEAR + 1
END -- End Yearly Loop
--Display the results
SELECT *
FROM dbo.##TEMPCOUNTERTABLE
-- Drop the temp table
IF OBJECT_ID('tempdb.dbo.##TEMPCOUNTERTABLE', 'U') IS NOT NULL
DROP TABLE dbo.##TEMPCOUNTERTABLE
GO
This should do the trick
with datesCte(monthStart,monthEnd) as
(
select cast('20120101' as date) as monthStart, cast('20120131' as date) as monthEnd
union all
select DATEADD(MONTH, 1, d.monthStart), dateadd(day, -1, dateadd(month, 1, d.monthStart))
from datesCte as d
where d.monthStart < '20140101'
)
select *
from datesCte as d
cross apply
(
select count(*) as cnt
from dbo.MemberDates as m
where m.startDate <= d.monthEnd and m.endDate > d.monthStart
) as x
order by d.monthStart

Whats an alternative to using a cursor as per this example in t-sql

I’m new to SQL (specifically t-sql and microsoft SQL Server 2008 R2) and had a problem my boss advised me to fix using a cursor. The problem being taking records (equating to shifts entered into a roster) that are over an hour (but divisible by an hour) and effectively splitting them into multiple shift records of an hour each for a report.
Below you can see the section of the query re the cursor logic that I used. My understanding is that cursors are very inefficient and frowned upon – but neither my boss nor myself could identify an alternative solution to this problem.
Can anyone demonstrate a way we could do this without cursors?
Open Curs;
FETCH NEXT FROM Curs INTO #ClientID, #RDNSID, #SvceType, #SDate, #ClientNm, #CHours, #StaffNm, #Package
WHILE (##Fetch_Status = 0)
BEGIN
SET #Hour = 60
SET #Num = #Chours
IF (#Num % 60 = 0)
BEGIN
WHILE (#Num >= 60)
BEGIN
INSERT INTO #ASRTable VALUES (#ClientID, #RDNSID, #SvceType, #SDate, #ClientNm, #Hour, #StaffNm, #Package)
SET #Num = #Num - 60
SET #SDate = DATEADD(HH, 1, #SDate)
END
END
ELSE
BEGIN
SET #Hour = 'INVALID SHIFT'
INSERT INTO #ASRTable VALUES (#ClientID, #RDNSID, #SvceType, #SDate, #ClientNm, #Hour, #StaffNm, #Package)
END
FETCH NEXT FROM Curs INTO #ClientID, #RDNSID, #SvceType, #SDate, #ClientNm, #CHours, #StaffNm, #Package
END
SELECT * FROM #ASRTable
DROP TABLE #ASRTable
CLOSE Curs
DEALLOCATE Curs
Well, you haven't given us sample data or expected results, but I think this follows the same logic:
declare #t table (
SDate datetime not null,
Chours int not null --Curiously, will store a number of minutes?
)
insert into #t (SDate,Chours) values ('2012-12-19T10:30:00',120),('2012-12-18T09:00:00',60),('2012-12-17T08:00:00',90)
;with shifts as (
select SDate,Chours,'60' as Hour from #t where Chours % 60 = 0
union all
select DATEADD(hour,1,SDate),CHours - 60,'60' from shifts where Chours > 0
)
select SDate,Hour from shifts
union all
select SDate,'Invalid Shift' from #t where CHours % 60 <> 0
Result:
SDate Hour
----------------------- -------------
2012-12-19 10:30:00.000 60
2012-12-18 09:00:00.000 60
2012-12-18 10:00:00.000 60
2012-12-19 11:30:00.000 60
2012-12-19 12:30:00.000 60
2012-12-17 08:00:00.000 Invalid Shift
Of course, I don't have all of your other columns, since I have no idea what they're meant to be.

T SQL use case when in parameter

I'm trying to set the variable value with a case statement to determine the financial year, depending on the month but I get a Null value returned for the financial year:
declare
#Costcentre varchar(50)
,#dt date
,#dty int
,#dtm int
select #Costcentre = 'CAM'
SELECT #dt = '2012-09-30'
select #dtm = DATEPART(month,#dt)
select
#dty = case when #dtm between 4 and 12 then DATEPART(year,#dt) + 1 end
,#dty = case when #dtm between 1 and 3 then DATEPART(year,#dt) end
select #dty
You only need to assign #dty once:
select #dty = case
when #dtm between 4 and 12 then DATEPART(year,#dt) + 1
when #dtm between 1 and 3 then DATEPART(year,#dt)
end
Otherwise, you're just overwriting #dty if #dtm isn't between 1 and 3.

Checking availability on a booking table in SQL

I'm writing a job vacancy database for a bit of fun (and to try and learn T-SQL/SQL Server and this is what I have in my applications table so far.
application_id name interviewer location_id from to
-----------------------------------------------------------------------------------------------------------
1 Joe Bloggs Sarah Saunders 100 2008-12-25 00:00:00 2008-12-26 00:00:00
2 Barry White Issac Hayes 100 2008-12-29 00:00:00 2008-12-30 00:00:00
It's easy enough to find out what bookings have been made for these dates; a simple select statement would find these out easily enough.
The only problem I have now is how to figure out what days DON'T contain bookings. I'd like to do a search on the following table to see what dates are available in the room with location_id 100 between "2008-12-25 00:00:00" and "2008-12-30 00:00:00" and have it return that there is no interview being held in the room from the 27th to the 28th.
I'm sure this is painfully easy, but please lay some SQL wisdom on me.
You can generate a temporary table containing your days (either in the upper layer or with a stored function, which would be better if that is for SQL-learning purpose), then OUTER JOIN it on the booking table and filter on the record having NULL matching application_id.
One way could be to put the date range in a table variable and join.
declare #startDate datetime, #endDate datetime
SET #startDate = '2009-05-01'
SET #endDate = '2009-05-31'
declare #dates table (date datetime)
insert into #dates values (#startDate)
while #startDate < #endDate
begin
set #startDate = #startDate + 1
insert into #dates values (#startDate)
end
select d.* from applications a
left join #dates d on d.date between a.from and a.to
where a.application_id is null
Not tested, but something like that might work.
First, I would start by breaking down your query "2008-12-25 00:00:00" to "2008-12-30 00:00:00" into "time periods" of one day each. This is relatively easy with a table variable and a while loop so I won't go into it here.
Then you could loop through each of the time periods from the table variable and see if it overlaps any of the existing bookings (you would only pull out the bookings that overlap the query time period). To do that I suggest using this helper function:
CREATE FUNCTION [dbo].[fn_TimePeriodsOverlap]
(
#pStartTP1 datetime,
#pEndTP1 datetime,
#pStartTP2 datetime,
#pEndTP2 datetime
)
RETURNS bit
AS
BEGIN
DECLARE #Result bit
SET #Result = 0
IF #pStartTP1 >= #pStartTP2 AND #pStartTP1 < #pEndTP2
SET #Result = 1
ELSE IF #pEndTP1 >= #pStartTP2 AND #pEndTP1 < #pEndTP2
SET #Result = 1
ELSE IF #pStartTP2 >= #pStartTP1 AND #pStartTP2 < #pEndTP1
SET #Result = 1
ELSE IF #pEndTP2 >= #pStartTP1 AND #pEndTP2 < #pEndTP1
SET #Result = 1
RETURN #Result
END
That will return 1 if two time periods overlap, and 0 otherwise. This has the advantage of working even if the booking blocks aren't always a full day.