Loop through a table without using a cursor or temp table finding records over 30 days old - sql-server-2012-express

Using SQL Server 2012. I need to loop through my table without using a cursor or a temp table. Also, I know the debate re: cursors... :) AND I have script at the bottom of this question using sys.databases and msdb.dbo.restorehistory getting the last restored, but this doesn't fix my immediate need for this specific table.
Scenario: I have a table that I created for restore tests:
CREATE Table RestoreTests (
RestoreTestID int Not NULL,
ServerName varchar (255) Not NULL,
DatabaseName varchar (255) Not NULL,
RestoreDate datetime Not NULL,
BackupFile varchar (1000) Not NULL)
I inserted 8 records with:
2 servers - 4 records/server,
.bak and .trn for each DatabaseName, and
dates < 30 days and dates > 30 days - 4 records each.
I want to identify all records on my table that are > 30 days.
SELECT * FROM RestoreTests WHERE RestoreDate DATEDIFF(Day,RestoreDate, GETDATE()) > 30
My syntax:
declare #RestoreTestID int
declare #ServerName varchar(255)
declare #DatabaseName varchar(255)
declare #RestoreDate datetime
declare #BackupFile varchar(1000)
declare #id int
set #id = 0
select top 1 #RestoreTestID = RestoreTestID,
#ServerName = ServerName,
#DatabaseName = DatabaseName,
#RestoreDate = RestoreDate,
#BackupFile = BackupFile
from dbo.RestoreTests
where RestoreTestID > #id and DATEDIFF(DAY,RestoreDate, GETDATE()) > 30
order by RestoreTestID asc
while ##ROWCOUNT > 0
begin
print 'loop RestoreTestID=' + cast(#RestoreTestID as varchar(255)) +
', ServerName=' + #ServerName +
', DatabaseName' + #DatabaseName +
', RestoreDate' + CAST(#RestoreDate as varchar(255)) +
', BackupFile' + #BackupFile
set #id = #RestoreTestID
select top 1 #RestoreTestID = RestoreTestID
from dbo.RestoreTests
where RestoreTestID > #id and DATEDIFF(DAY,RestoreDate, GETDATE()) > 30
order by RestoreTestID
end
I'm getting error message:
Msg 137, Level 15, State 1, Line 1
Must declare the scalar variable "#RestoreTestID".
Msg 137, Level 15, State 2, Line 13
Must declare the scalar variable "#RestoreTestID".
Msg 137, Level 15, State 2, Line 19
Must declare the scalar variable "#RestoreTestID".
Msg 137, Level 15, State 1, Line 21
Must declare the scalar variable "#RestoreTestID".
When I was dinking around earlier I did get a result set with all eight records and not the 4 that are over 30 days. Any assistance offered is greatly appreciated!!
Last Restore Syntax:
WITH LastRestores AS
(
SELECT
DatabaseName = [d].[name] ,
[d].[create_date] ,
[d].[compatibility_level] ,
[d].[collation_name] ,
r.*,
RowNum = ROW_NUMBER() OVER (PARTITION BY d.Name ORDER BY r.[restore_date] DESC)
FROM master.sys.databases d
LEFT OUTER JOIN msdb.dbo.[restorehistory] r ON r.[destination_database_name] = d.Name
)
SELECT *
FROM [LastRestores]
WHERE [RowNum] = 1
select * from [dbo].[RestoreTests]

Ok, I figured out what I needed. Here's the syntax I arrived at:
declare #RestoreTestID int
declare #id int
declare #ServerName varchar(255);
declare #DatabaseName varchar (255);
declare #RestoreDate datetime;
declare #BackupFile varchar (1000);
set #id = 0
select top 1
#RestoreTestID = RestoreTestID,
#ServerName = ServerName,
#DatabaseName = DatabaseName,
#RestoreDate = RestoreDate,
#BackupFile = BackupFile
from dbo.RestoreTests
where RestoreTestID > #id and RestoreDate <= DATEADD(day,-31,getdate())
order by RestoreTestID
while ##ROWCOUNT = 1
begin
print 'RestoreTestID = ' + cast(#RestoreTestID as varchar(255)) +
', ServerName = ' + #ServerName +
', Databasename = ' + #DatabaseName +
', RestoreDate = ' + cast(#RestoreDate as varchar (255)) +
', BackupFile = ' + #BackupFile
set #id = #RestoreTestID
select top 1 #RestoreTestID = RestoreTestID
from dbo.RestoreTests
where RestoreTestID > #id and RestoreDate <= DATEADD(day,-31,getdate())
order by RestoreTestID
end

Related

Executing a trigger with some linked server components

I have a row being inserted into [Server1].[DatabaseA].[dbo].[EMRVisit]. I need to add some information to the Visit record from linked server [Server2].[DatabaseB], so I set up an AFTER INSERT trigger.
Both servers are MSSQL 2016, databases are running at 2008 compatibility at the moment.
Currently, the trigger is blocking insertion of the row. I'm not seeing anything in Profiler, other that the Insert attempt and a rollback. Also, it's not generating any other errors. The code runs fine outside the trigger.
The frustrating part of this, is that the trigger was operating fine as is on older 2008 boxes.
ALTER TRIGGER [dbo].[Pt_Note_Edit]
ON [dbo].[EMRVisit]
AFTER INSERT
AS
IF ((SELECT TRIGGER_NESTLEVEL()) < 2)
BEGIN
DECLARE #med_rec_nbr varchar(15), #Days VARCHAR(20), #OldNote nvarchar(MAX), #NewNote nvarchar(MAX)
SET #med_rec_nbr = (SELECT ept.PatientChartNumber FROM INSERTED ev INNER JOIN [Server1].[DatabaseA].[dbo].[Patients] ept ON ev.PtID = ept.PtID)
--Collect ADS Data
--Convert #med_rec_nbr to #Account
DECLARE #Account varchar(15)
EXEC [Server2].[DatabaseB].[dbo].ADS_CleanUp_MRN #med_rec_nbr, #Account=#Account OUTPUT
SELECT *
INTO #PtChgs
FROM [Server2].[DatabaseB].[dbo].ADS_Charges
WHERE AccountNo = #Account
--Get Old Pt Note
SELECT #OldNote = ept.PatientNote
FROM [Server1].[DatabaseA].[dbo].[Patients] ept
WHERE ept.PatientChartNumber = #med_rec_nbr
--Edit OldNote
DECLARE #NotePart nvarchar(MAX), #Trans Int
SET #NotePart = CASE
WHEN #OldNote IS NOT NULL THEN SUBSTRING(#OldNote,CHARINDEX('~',#OldNote,1)+1,LEN(#OldNote))
ELSE ''
END
--Get Latest Post Op Charges
SELECT Charges.FromDate
,Charges.Modifiers
,DATEDIFF(DAY, Charges.FromDate, GETDATE()) AS DaysIn
INTO #PtPostOp
FROM [Server2].[DatabaseB].[dbo].[Charges] Charges
JOIN [Server2].[DatabaseB].[dbo].[ProcedureCodes] PC ON Charges.Cpt = PC.Code
WHERE Charges.AccountNo = #Account
AND Charges.FromDate > CONVERT(VARCHAR, DATEADD (DAY , -(PC.NumberOfDays + 10) , GETDATE()), 112)
AND PC.NumberOfDays <> 0
ORDER BY Charges.FromDate DESC
DECLARE #FromDate integer,#Modifiers VARCHAR(5), #DaysIn VARCHAR (2), #Enc_Rows int, #Mod varchar(3)
--Get count of encounters in Post Op period for current patient
SET #Enc_Rows = 0
SELECT #Enc_Rows = COUNT(*) FROM #PtPostOp
--Loop through records and concatenate rows
SET #Days = ''
WHILE #Enc_Rows > 0
BEGIN
--Get first record
SELECT TOP 1 #FromDate = FromDate
,#Modifiers = CASE
WHEN Modifiers LIKE '%RT%' THEN 'OD'
WHEN Modifiers LIKE '%LT%' THEN 'OS'
ELSE Modifiers
END
,#DaysIn = DaysIn
FROM #PtPostOp
ORDER BY FromDate DESC
--Concatenate Row
SET #Days = #Days + #Modifiers + ' ' + #DaysIn + ' days'
IF #Enc_Rows > 1
SET #Days = #Days + ': '
SET #Enc_Rows = #Enc_Rows -1
DELETE FROM #PtPostOp WHERE FromDate = #FromDate
END
IF #Days <> ''
SET #NewNote = ('**POST OP ' + #Days + ' ** ~' + #NotePart)
ELSE
SET #NewNote = #NotePart
--Update the PatientNote
UPDATE [Server1].[Database].[dbo].[Patients]
SET PatientNote = #NewNote
WHERE PatientChartNumber = #med_rec_nbr
END
As an addition to this - I'm also attempting to add a try - catch to my code, but that is generating DTC errors.

Calculating the business differences in sql in the form of day, hours and minutes - SQL Server

With the below function I getting the result as 00:09:10 however I want the result to be as 01:00:10.
So hours is considered as considered as 1 day.
Example if hours is 30 then it will be 03:03:00 and so on
fn_GetHolidayMinutes : Get holiday in minutes between two dates and country
CREATE FUNCTION [dbo].[fn_GetHolidayMinutes]
(#StartDate DATETIME,
#EndDate DATETIME,
#CountryId BIGINT)
RETURNS BIGINT
AS
BEGIN
DECLARE #OUTPUT BIGINT;
DECLARE #HolidayList TABLE (HolidaysDate DATE)
-- Create Table #HolidayList
-- (
-- HolidaysDate date
-- )
DECLARE #Date1 DATE, #Date2 DATE
DECLARE holiday_cursor CURSOR FOR
SELECT StartDate,EndDate
FROM Holidays
WHERE IsActive = 1
AND CountryId = #CountryId
AND ((StartDate BETWEEN #StartDate AND #EndDate) OR
(EndDate BETWEEN #StartDate AND #EndDate))
OPEN HOLIDAY_CURSOR
FETCH NEXT FROM HOLIDAY_CURSOR INTO #Date1, #Date2
WHILE ##FETCH_STATUS = 0
BEGIN
--INSERT INTO #HolidayList
INSERT INTO #HolidayList
SELECT DATEADD(DAY, number, #Date1) [Date]
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY, number, #Date1) <= #Date2
FETCH NEXT FROM HOLIDAY_CURSOR INTO #Date1, #Date2
END
CLOSE HOLIDAY_CURSOR;
DEALLOCATE HOLIDAY_CURSOR;
(SELECT #OUTPUT= COUNT(DISTINCT HolidaysDate)
FROM #HolidayList
WHERE HolidaysDate BETWEEN #StartDate AND #EndDate
AND DATEPART(dw, HolidaysDate) NOT IN (SELECT DISTINCT number
FROM master..spt_values
WHERE number BETWEEN 1 and 7
AND number NOT IN (SELECT WorkingDay
FROM WorkingDays
WHERE CountryId = #CountryId AND IsActive = 1)
))
---print #OUTPUT; --this will give in days
--get the output in minutes
RETURN #OUTPUT * (SELECT TOP 1 STUFF(WorkingHours, 2, 2, '')
FROM dbo.WorkingDays
WHERE CountryId = #CountryId) * 60;
END
fn_GetWorkingDayMinuts :
CREATE FUNCTION [dbo].[fn_GetWorkingDayMinuts]
(#StartDate DATETIME,
#EndDate DATETIME,
#CountryId BIGINT)
--RETURNS BIGINT
RETURNS VARCHAR(250)
AS
BEGIN
DECLARE #Temp BIGINT
SET #Temp = 0
DECLARE #FirstDay DATE
SET #FirstDay = CONVERT(DATE, #StartDate, 112)
DECLARE #LastDay DATE
SET #LastDay = CONVERT(DATE, #EndDate, 112)
DECLARE #StartTime TIME
SET #StartTime = CONVERT(TIME, #StartDate)
DECLARE #FinishTime TIME
SET #FinishTime = CONVERT(TIME, #EndDate)
DECLARE #WorkStart TIME
SET #WorkStart = (SELECT CONVERT(VARCHAR(5),CONVERT(TIME, CONVERT(VARCHAR,CONVERT(DATE, GETDATE()))+ ' ' + (SELECT TOP 1
WorkStartTime FROM WorkingDays WHERE CountryId=#CountryId), 120)))
DECLARE #WorkFinish TIME
SET #WorkFinish = (SELECT CONVERT(VARCHAR(5),CONVERT(TIME, CONVERT(VARCHAR,CONVERT(DATE, GETDATE()))+ ' ' + (SELECT TOP 1
WorkEndTime FROM WorkingDays WHERE CountryId=#CountryId), 120)))
DECLARE #DailyWorkTime BIGINT
SET #DailyWorkTime = DATEDIFF(MINUTE, #WorkStart, #WorkFinish)
IF (#StartTime<#WorkStart)
BEGIN
SET #StartTime = #WorkStart
END
IF (#FinishTime>#WorkFinish)
BEGIN
SET #FinishTime=#WorkFinish
END
IF (#FinishTime<#WorkStart)
BEGIN
SET #FinishTime=#WorkStart
END
IF (#StartTime>#WorkFinish)
BEGIN
SET #StartTime = #WorkFinish
END
DECLARE #CurrentDate DATE
SET #CurrentDate = #FirstDay
DECLARE #LastDate DATE
SET #LastDate = #LastDay
WHILE(#CurrentDate<=#LastDate)
BEGIN
--IF (DATEPART(dw, #CurrentDate)!=1 AND DATEPART(dw, #CurrentDate)!=7)
IF(DATEPART(dw, #CurrentDate) IN (SELECT distinct number FROM master..spt_values WHERE number BETWEEN 1 and 7
AND number NOT IN (SELECT
WorkingDay FROM WorkingDays where CountryId=#CountryId and IsActive=1)
))
BEGIN
IF (#CurrentDate!=#FirstDay) AND (#CurrentDate!=#LastDay)
BEGIN
SET #Temp = #Temp + #DailyWorkTime
END
--IF it starts at startdate and it finishes not this date find diff between work finish and start as minutes
ELSE IF (#CurrentDate=#FirstDay) AND (#CurrentDate!=#LastDay)
BEGIN
SET #Temp = #Temp + DATEDIFF(MINUTE, #StartTime, #WorkFinish)
END
ELSE IF (#CurrentDate!=#FirstDay) AND (#CurrentDate=#LastDay)
BEGIN
SET #Temp = #Temp + DATEDIFF(MINUTE, #WorkStart, #FinishTime)
END
--IF it starts and finishes in the same date
ELSE IF (#CurrentDate=#FirstDay) AND (#CurrentDate=#LastDay)
BEGIN
SET #Temp = DATEDIFF(MINUTE, #StartTime, #FinishTime)
END
END
SET #CurrentDate = DATEADD(day, 1, #CurrentDate)
END
-- Return the result of the function
IF #Temp<0
BEGIN
SET #Temp=0
END
--RETURN #Temp -(dbo.fn_GetHolidayMinutes (DATEADD(dd, 0, DATEDIFF(dd, 0, #StartDate)),DATEADD(dd, 0, DATEDIFF(dd, 0,
#EndDate)),#CountryId))
--RETURN #Temp
DECLARE #theMinutes INT
DECLARE #Result VARCHAR(250)
SET #theMinutes = #Temp -(dbo.fn_GetHolidayMinutes (DATEADD(dd, 0,
DATEDIFF(dd, 0, #StartDate)),DATEADD(dd, 0, DATEDIFF(dd, 0,
#EndDate)),#CountryId))
--SET #Result= concat((#theMinutes / 540),':' , (#theMinutes % 540) /
60, ':', (#theMinutes % 60))
SET #Result= concat((#theMinutes / ((SELECT TOP 1
STUFF(WorkingHours,2,2,'') FROM dbo.WorkingDays WHERE
CountryId=#CountryId) * 60)),':' , (#theMinutes % ((SELECT TOP 1
STUFF(WorkingHours,2,2,'') FROM dbo.WorkingDays WHERE
CountryId=#CountryId) * 60)) / 60, ':', (#theMinutes % 60))
RETURN #Result
END
So where to modify to the result as
BUSINESS HOURS CREATE DATE & TIME FIRST APPLY (DATE & TIME) TAT TIME CALCULATION RESULT
08h00-17h00: 9hrs/day 12-FEB-19 14:20 13-FEB-19 14:30 00:02:40 + 00:06:30 = 00:09:10 01:00:10

Cursor never ending in T-SQL

I have problems with a cursur that’s ends randomly. I want to produce a row for each element , so for every element there is a row for every month in 2012. If there is a row missing for a specific month, it should create that as well.
Now, it’s producing a row for the last element in every month until year 2057.
Now, I have a row for each element until in every month until year 2057 and only for one element in my table.
My table design:
create table tblDataUpdateTest
(
slno int identity(1,1),
cName varchar(50),
cRemarks varchar(50),
[Date] date,
)
insert into tblDataUpdateTest (cName, cRemarks,[Date]) values ('name1','Text1','2012-01-01'),
('name2','Text2','2012-01-01')
My code:
declare #y as int
declare #d as int
SET #y = 2012
SET #d = 1
Declare ##counter int
Declare ##month int
set ##counter=0
Declare ##slno int
Declare ##cRemarks varchar(100)
Declare ##cName varchar(50)
Declare ##Date date
set ##month = 1
Declare tmepTbl cursor
For
Select slno,cName,cRemarks,date from tblDataUpdateTest
Open tmepTbl /* Opening the cursor */
fetch next from tmepTbl
into ##slno,##cName,##cRemarks,##Date
while ##fetch_Status=-1
begin
if not exists (select cRemarks from tblDataUpdateTest where MONTH(Date) = ##month AND YEAR(Date) = 2012)
begin
insert into tblDataUpdateTest (cName, cRemarks,[Date]) values (##cName,'s',(DateAdd(yy, 2012-1900,DateAdd(m, ##month - 1, 01 - 1))))
end
fetch next from tmepTbl
into ##slno,##cName,##cRemarks,##Date
set ##month=##month+1
end
close tmepTbl
Deallocate tmepTbl
My table now
cName cRemarks Date
name1 Text1 2012-01-01
name2 Text2 2012-01-01
I want follwing to happen:
cName cRemarks Date
name1 Text1 2012-01-01
name1 Text1 2012-02-01
name1 Text1 2012-03-01
name2 Text2 2012-01-01
name2 Text2 2012-02-01
name2 Text2 2012-03-01
and so on.
Thanks!
Wouldn't be better to use a recursive CTE, instead of a cursor?
Here is an example:
DECLARE #y INT
DECLARE #m INT
DECLARE #n INT
SET #y = 2012
SET #m = 1
SET #n = 3
;WITH dates(d, m) AS(
SELECT CONVERT(DATE, CONVERT(VARCHAR(4), #y) + '-01-01'), 1
UNION ALL
SELECT CONVERT(DATE, CONVERT(VARCHAR(4), #y) + '-' + CASE WHEN m + 1 < 10 THEN '0' ELSE '' END + CONVERT(VARCHAR(2), m + 1) + '-01')
, m + 1
FROM dates
WHERE m < #n
)
INSERT INTO tblDataUpdateTest(cName, cRemarks,[Date])
SELECT cName, cRemarks, [date] FROM (
SELECT cName, cRemarks, dates.d AS [date]
FROM tblDataUpdateTest
CROSS JOIN dates) t
WHERE NOT(EXISTS(SELECT * FROM tblDataUpdateTest WHERE cName = t.cName AND [date] = t.[date]))
OPTION(MAXRECURSION 11)
SELECT * FROM tblDataUpdateTest ORDER BY cName

How to create a date lookup table to speed up stored procs?

I want to reduce the time it takes for one of my stored procs that currently uses the following logic to calculate the date field, both in Select and Group portion:
left(datename(month, a.QXP_REPORT_DATE), 3) + ' ''' + right(datename(year, a.QXP_REPORT_DATE), 2)
Would a simple lookup table take less time? If so, then how would I populate for the following fields for all dates in the last 2 years?
CREATE TABLE #CALENDAR(
FULLDATE DATETIME,
MONTHNAME NVARCHAR(3),
sYEAR SMALLINT
)
INSERT INTO #CALENDAR
SELECT '4/19/2011', left(datename(month, '4/19/2011'), 3), right(datename(year, '4/19/2011'), 2)
I'm starting to think maybe a function call would be better than a lookup table. Here is all of my SQL stored proc:
DECLARE
#FirstMonthDate DATETIME,
#LastMonthDate DATETIME,
#TheLevel INT,
#ProductGroup VARCHAR(255),
#TheCategory VARCHAR(255),
#ListNumber VARCHAR(50)
--AS
-- SET NOCOUNT ON;
--ComplaintTrendingDrillDown3p '3/1/10', '3/31/11 23:59:59', 3 , 'RealTime IVD', 'Procedure Not Followed', ''
SET #FirstMonthDate = '3/1/11'
SET #LastMonthDate = '3/31/11 23:59:59'
SET #TheLevel = 3
SET #ProductGroup = 'RealTime IVD'
SET #TheCategory = 'Procedure Not followed'
--SET #ListNumber = '2G31-90'
DECLARE #SelectedLevels table (LevelId int not null primary key)
declare #OneYearAgo datetime
set #OneYearAgo = dateadd(year, -1, #FirstMonthDate)
IF #TheLevel = 3
BEGIN
INSERT INTO #SelectedLevels (LevelId) VALUES (1)
INSERT INTO #SelectedLevels (LevelId) VALUES (2)
END
ELSE if #TheLevel = 5
BEGIN
INSERT INTO #SelectedLevels (LevelId) VALUES (0)
INSERT INTO #SelectedLevels (LevelId) VALUES (1)
INSERT INTO #SelectedLevels (LevelId) VALUES (2)
END
ELSE
BEGIN
INSERT INTO #SelectedLevels (LevelId) VALUES (#TheLevel)
END
SELECT count(distinct a.QXP_EXCEPTION_NO) AS QXP_EXCEPTION_NO, PRODUCT_CODE_STD, a.qxp_short_desc,
left(datename(month, a.QXP_REPORT_DATE), 3) + ' ''' +
right(datename(year, a.QXP_REPORT_DATE), 2) AS MonthYear ,
CASE WHEN a.QXP_SHORT_DESC = #TheCategory OR ISNULL(#TheCategory, '') = '' THEN 1 ELSE 0 END AS SELECTED_CATEGORY
FROM ALL_COMPLAINTS a
INNER JOIN #SelectedLevels F ON A.[LEVEL] = F.LevelId
LEFT OUTER JOIN MANUAL.PRODUCTS b ON a.EPA_PRD_CODE = b.LIST_NUMBER
LEFT OUTER JOIN SMARTSOLVE.V_CXP_CUSTOMER_PXP c ON a.QXP_ID = c.QXP_ID
WHERE a.QXP_REPORT_DATE >= #OneYearAgo AND
a.QXP_REPORT_DATE <= #LastMonthDate AND a.QXP_SHORT_DESC <> 'Design Control'
AND (c.QXP_EXCEPTION_TYPE <> 'Non-Diagnostic' OR c.QXP_EXCEPTION_TYPE IS NULL)
AND PRODUCT_GROUP= #ProductGroup
AND (PRODUCT_CODE_STD = #ListNumber OR ISNULL(#ListNumber, '') = '')
and left(datename(month, a.QXP_REPORT_DATE), 3) = 'may'
GROUP BY PRODUCT_CODE_STD, left(datename(month, a.QXP_REPORT_DATE), 3) + ' ''' + right(datename(year, a.QXP_REPORT_DATE), 2) , a.qxp_short_desc
order by left(datename(month, a.QXP_REPORT_DATE), 3) + ' ''' +
right(datename(year, a.QXP_REPORT_DATE), 2), product_code_std, qxp_short_desc
Execution plan recommendations:
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [SMARTSOLVE].[V_CXP_CUSTOMER_PXP] ([QXP_REPORT_DATE],[QXP_UDF_STRING_8],[QXP_XRS_DESCRIPTION])
INCLUDE ([QXP_ID],[QXP_EXCEPTION_NO],[QXP_BASE_EXCEPTION],[QXP_OCCURENCE_DATE],[QXP_COORD_ID],[QXP_ROOT_CAUSE],[QXP_DESCRIPTION],[QXP_QEI_ID],[QXP_EXCEPTION_TYPE],[QXP_UDF_STRING_2],[QXP_UDF_STRING_5],[CXP_ID],[CXP_AWARE_DATE],[QXP_XSV_CODE],[QXP_COORD_NAME],[QXP_ORU_NAME],[QXP_RESOLUTION_DESC],[QXP_CLOSED_DATE],[CXP_CLIENT_CODE],[CXP_CLIENT_NAME])

Implementing and applying a string split in T-SQL

I have this statement in T-SQL.
SELECT Bay From TABLE where uid in (
select B_Numbers from Info_Step WHERE uid = 'number'
)
I am selecting "multiple" BAYs from TABLE where their uid is equal to a string of numbers like this:
B_Numbers = 1:45:34:98
Therefore, I should be selecting 4 different BAYs from TABLE. I basically need to split the string 1:45:34:98 up into 4 different numbers.
I'm thinking that Split() would work, but it doesn't and I get a syntax error.
Any thoughts from the T-SQL gods would be awesome!
Here is an implementation of a split function that returns the list of numbers as a table:
http://rbgupta.blogspot.com/2007/03/split-function-tsql.html
Looks like this would set you on your way...
Here is a method that uses an auxiliary numbers table to parse the input string. The logic can easily be added to a function that returns a table. That table can then be joined to lookup the correct rows.
Step 1: Create the Numbers table
SET NOCOUNT ON
GO
IF EXISTS
(
SELECT 1
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'Numbers'
AND TABLE_SCHEMA = 'dbo'
AND TABLE_TYPE = 'BASE TABLE'
)
BEGIN
DROP TABLE dbo.Numbers
END
GO
CREATE TABLE dbo.Numbers
(
Number smallint IDENTITY(1, 1) PRIMARY KEY
)
GO
WHILE 1 = 1
BEGIN
INSERT INTO dbo.Numbers DEFAULT VALUES
IF SCOPE_IDENTITY() = 32767
BEGIN
BREAK
END
END
GO
Step 2: Parse the Input String
CREATE FUNCTION dbo.ParseString(#input_string varchar(8000), #delim varchar(8000) = " ")
RETURNS TABLE
AS RETURN
(
SELECT Number
FROM dbo.Numbers
WHERE CHARINDEX
(
#delim + CONVERT(VARCHAR(12),Number) + #delim,
#delim + #input_string + #delim
) > 0
)
GO
**EXAMPLE**
SELECT * FROM dbo.ParseString('1:45:34:98',':')
Step 3: Use the results however you want/need
Number
------
1
34
45
98
End-To-End Example
Create function that returns the appropriate BNumber (of course change it to use the commented out SQL)
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION dbo.GetBNumber (#uid int)
RETURNS VARCHAR(8000)
AS
BEGIN
RETURN '1:45:34:98'
--select B_Numbers from Info_Step WHERE uid = #uid
END
GO
Use the use functions to return the desired results
-- Using Test Data
SELECT N.Number FROM Numbers N
JOIN dbo.ParseString(dbo.GetBNumber(12345),':') Q ON Q.Number = N.Number
-- Using Your Data (Untested but should work.)
SELECT N.Bay
FROM TABLE N
JOIN dbo.ParseString(dbo.GetBNumber(ENTER YOU NUMBER HERE),':') Q ON Q.Number = N.uid
Results
Number
------
1
34
45
98
You should keep your arrays as rows but if I understand your question I think this will work.
SELECT
Bay
From
TABLE
join Info_Step
on B_Numbers like '%'+ uid +'%'
where
Info_Step.uid = 'number'
This query will do a full table scan because of the like operator.
What you can do is loop through the B_Numbers entries and do your own split on : Insert those entries into a temp table and then perform your query.
DECLARE #i int
DECLARE #start int
DECLARE #B_Numbers nvarchar(20)
DECLARE #temp table (
number nvarchar(10)
)
-- SELECT B_Numbers FROM Info_Step WHERE uid = 'number'
SELECT #B_Numbers = '1:45:34:98'
SET #i = 0
SET #start = 0
-- Parse out characters delimited by ":";
-- Would make a nice user defined function.
WHILE #i < len(#B_Numbers)
BEGIN
IF substring(#B_Numbers, #i, 1) = ':'
BEGIN
INSERT INTO #temp
VALUES (substring(#B_Numbers, #start, #i - #start))
SET #start = #i + 1
END
SET #i = #i + 1
END
-- Insert last item
INSERT INTO #temp
VALUES (substring(#B_Numbers, #start, #i - #start + 1))
-- Do query with parsed values
SELECT Bay FROM TABLE WHERE uid in (SELECT * FROM #temp)
You can even try this
declare #str varchar(50)
set #str = '1:45:34:98'
;with numcte as(
select 1 as rn union all select rn+1 from numcte where rn<LEN(#str)),
getchars as(select
ROW_NUMBER() over(order by rn) slno,
rn,chars from numcte
cross apply(select SUBSTRING(#str,rn,1) chars)X where chars = ':')
select top 1
Bay1 = SUBSTRING(#str,0,(select rn from getchars where slno = 1))
,Bay2 = SUBSTRING(#str,
(select rn from getchars where slno = 1) + 1,
(((select rn from getchars where slno = 2)-
(select rn from getchars where slno = 1)
)-1))
,Bay3 = SUBSTRING(#str,
(select rn from getchars where slno = 2) + 1,
(((select rn from getchars where slno = 3)-
(select rn from getchars where slno = 2)
)-1))
,Bay4 = SUBSTRING(#str,
(select rn from getchars where slno = 3)+1,
LEN(#str))
from getchars
Output:
Bay1 Bay2 Bay3 Bay4
1 45 34 98