T-SQL to check and update - tsql

Getting error:
Msg 512, Level 16, State 1, Line 48
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Msg 512, Level 16, State 1, Line 87
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Need to check each record in the table and update a table if there is no records = to 0,00. Then update an existing table with current values. I want to be able to track current data and compare with old data.
DECLARE #LocTime DATETIME;
DECLARE #MinDateTime DATETIME;
DECLARE #SystemDateTime DATETIME;
DECLARE #LIR MONEY;
DECLARE #LOAR MONEY;
SELECT #SystemDateTime = SYSDATETIME(); --= '2016-12-07 23:30:00'
SELECT #MinDateTime = DATEADD(mi,-30,#SystemDateTime) --go back half hour of reads, job is running every 10 minutes for overlap
--select #MinDateTime, #SystemDateTime
IF OBJECT_ID(N'tempdb..##LastOver2') IS NOT NULL
BEGIN
DROP TABLE ##LastOver2
END
--make temp table to hold found data
CREATE TABLE ##LastOver2 (ReadDate DATETIME
, FacilityName NVARCHAR(100)
, FacilityID UNIQUEIDENTIFIER
, LastInstantRead MONEY
, LastOverAllRead MONEY
, FacilityTimeZone INT
, FacilityTime DATETIME)
INSERT INTO ##LastOver2 (ReadDate, FacilityName
, FacilityID, LastInstantRead
, LastOverAllRead
, FacilityTimeZone, FacilityTime)
SELECT DISTINCT --why distinct?
fmr.ReadDate, f.Name
, f.FacilityID, fm.LastInstantRead
, fm.LastOverAllRead
, f.Timezone
, #LocTime
FROM [dbo].[Facilities] f WITH (NOLOCK)
JOIN [dbo].[FacilityMeters] fm WITH (NOLOCK)
ON F.FacilityID = FM.FacilityID
JOIN FacilityMeterReadings fmr WITH (NOLOCK)
ON FM.FacilityMeterID = FMR.FacilityMeterID
WHERE --fm.FacilityMeterID = '9268d1af-cc29-432c-9cdb-06c158180d2f'
(fmr.ReadDate >= #MinDateTime and ReadDate <= #SystemDateTime) --including on both side to continue with overlap
and (fm.IsVirtual = 0 and fm.ParentMeterID is NULL)
--and (fm.LastInstantRead = 0.00 and fm.LastOverAllRead = 0.00)
AND f.SuppressMonitoring = 0
select * from ##LastOver2
IF (select LastInstantRead from ##LastOver2) = 0.00 OR (SELECT LastOverAllRead FROM ##LastOver2) = 0.00
BEGIN
--UPDATE dbo.Facilities
--SET SuppressMonitoring = 1
-- FROM dbo.Facilities F
-- JOIN ##LastOver L
-- ON F.FacilityID = l.FacilityID
-- WHERE F.FacilityID = l.FacilityID
DECLARE #body_content NVARCHAR(150);
DECLARE #StartHour NVARCHAR(8) = '08:30:00' ;
DECLARE #StopHour NVARCHAR(8) = '16:00:00';
--why distinct, is it already distinct. Is is supposed to distinct the facility or meter?
DECLARE #SQLScript NVARCHAR(200) = 'SELECT distinct * FROM ##LastOver2 WHERE CONVERT(TIME, FacilityTime) BETWEEN '
+ CHAR(39) + #StartHour + CHAR(39) +' AND ' + CHAR(39) + #StopHour + CHAR(39);
--only looking for reads during day hours? shouldn't use between.
DECLARE #copyRecipients NVARCHAR(100)
SET #body_content = 'Please check the attached file. This was from server: ' + ##SERVERNAME
DECLARE #fileName nvarchar(255);
select #fileName = 'BadReading_' + replace(replace(convert(nvarchar(19),getdate(), 126),':',''),'-','') + '.txt';
EXEC msdb.dbo.sp_send_dbmail
#profile_name = 'SQLSupport'
,#recipients = 'Btest#test.com'
--, #recipients = 'jira#cleanenergycollective.atlassian.net'
--, #copy_recipients= #copyRecipients
--, #copy_recipients= 'Bill.Lugaila#easycleanenergy.com;yvonne.lewis#easycleanenergy.com;don.munroe#easycleanenergy.com;Justin.Reed#easycleanenergy.com'
, #query = #SQLScript
, #subject = 'Facility Meter Check and Updating table' --change so easier to see in emails
, #body= #body_content
, #importance= 'High'
, #attach_query_result_as_file = 1
, #query_attachment_filename = #fileName ;
--select #SQLScript
END
ELSE IF (select LastInstantRead from ##LastOver2) != 0.00 OR (SELECT LastOverAllRead FROM ##LastOver2) != 0.00
BEGIN
UPDATE [dbo].[_LastInstant_OverAll_Read]
SET [FacilityName] = lo.FacilityName,
[LastInstantRead] = lo.LastInstantRead,
[LastOverAllRead]= lo.LastOverAllRead,
[Time_Date] = (SELECT CONVERT(DateTime, SysDateTime()))
FROM ##LastOver2 lo
WHERE lo.FacilityName = [dbo].[_LastInstant_OverAll_Read].FacilityName
AND lo.LastInstantRead != [dbo].[_LastInstant_OverAll_Read].LastInstantRead
AND lo.LastOverAllRead != [dbo].[_LastInstant_OverAll_Read].LastOverAllRead
END

the following could be returning multiple rows:
.
.
.
IF
(
SELECT LastInstantRead
FROM ##LastOver2
) = 0.00
OR
(
SELECT LastOverAllRead
FROM ##LastOver2
) = 0.00
.
.
.
IF
(
SELECT LastInstantRead
FROM ##LastOver2
) != 0.00
OR
(
SELECT LastOverAllRead
FROM ##LastOver2
) != 0.00
if you are expecting them to return only 1 row then you will need to fix the issue with the following query:
SELECT DISTINCT --why distinct?
fmr.ReadDate,
f.Name,
f.FacilityID,
fm.LastInstantRead,
fm.LastOverAllRead,
f.Timezone,
#LocTime
FROM [dbo].[Facilities] f WITH (NOLOCK)
JOIN [dbo].[FacilityMeters] fm WITH (NOLOCK) ON F.FacilityID = FM.FacilityID
JOIN FacilityMeterReadings fmr WITH (NOLOCK) ON FM.FacilityMeterID = FMR.FacilityMeterID
WHERE --fm.FacilityMeterID = '9268d1af-cc29-432c-9cdb-06c158180d2f'
(fmr.ReadDate >= #MinDateTime
AND ReadDate <= #SystemDateTime) --including on both side to continue with overlap
AND (fm.IsVirtual = 0
AND fm.ParentMeterID IS NULL)
--and (fm.LastInstantRead = 0.00 and fm.LastOverAllRead = 0.00)
AND f.SuppressMonitoring = 0;

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

T-SQL Different Comparison in WHERE Clause Using CASE

I wanted to do different comparison expression in where clause, but it keep prompt me invalid syntax. The reason i want to use this because i wanted to enforce the evaluation according to the Type. If i use ((t1.Amount <= #valueB) OR (t1.Type IN (1) AND t1.Amount > 0 AND #valueB < 0)) , it seems to be not expression i trying to impose into my query. If first evaluation success, i would not want the following to execute, however OR in sql is not short-circuit operator.
DECLARE #name AS VARCHAR(200) = 'super'
DECLARE #valueA AS INT = 1 -- Can be 1 or 2
DECLARE #valueB AS DECIMAL = 0.5
SELECT * FROM TABLE_1 t1 WITH(NOLOCK)
WHERE t1.Name = #name
AND
CASE WHEN t1.Type = 1 THEN (t1.Amount > 0 AND #valueB < 0)
ELSE (t1.Amount <= #valueB)
END
You can't use it that way.
TRY THIS
SELECT * FROM TABLE_1 t1 WITH(NOLOCK)
WHERE t1.Name = #name AND
((t1.Type = 1 AND t1.Amount > 0 AND #valueB < 0)
OR (t1.Amount <= #valueB))
If could elaborate further what you are trying to achieve , I'll be able to help.
Zohar Peled's comment rightly points out that CASE is an expression in T-SQL, it is not a flow-of-control statement.
What you are trying to do is:
IF <expression_A> = <value_A>
THEN <apply_condition_B>
ELSE <apply_condition_C>
To achieve the following in SQL, you'd need to write it out using Boolean logic using the AND and OR operators as:
(
( <expression_A> = <value_A> AND <apply_condition_B> )
OR ( <expression_A> is not <value_A> AND <apply_condition_C> )
)
The first AND that comes before <apply_condition_B> acts like the THEN keyword, while the combination of the OR and the second AND act like the ELSE keyword.
Taking your query into consideration, we get the following mapping:
<expression_A> = t1.Type
<value_A> = 1
<apply_condition_B> = (t1.Amount > 0 AND #valueB < 0)
<apply_condition_C> = (t1.Amount <= #valueB)
Putting that into query form would give us:
WHERE t1.Name = #name
AND
(
( t1.Type = 1 AND (t1.Amount > 0 AND #valueB < 0) )
OR ( t1.Type != 1 AND (t1.Amount <= #valueB) )
)
-- Note - this is not fully complete, as it assumes t1.Type is not NULL
The above is a simple rewrite of your intended logic. However, do note that SQL operates on three-valued logic which means that t1.Type can be 1, it can be a value other than 1, or it can be NULL. The above code fragment handles only two of these 3 possibilities. To correctly handle t1.Type being NULL you can use the COALESCE() function to return a specified value (zero, below) in case it is NULL:
WHERE t1.Name = #name
AND
(
( t1.Type = 1 AND (t1.Amount > 0 AND #valueB < 0) )
OR ( COALESCE(t1.Type, 0) != 1 AND (t1.Amount <= #valueB) )
)
Tip: Before using this sort of construct directly in your actual query, I'd suggest you play around with it using simpler sample data to understand how it works.
Try this
DECLARE #name AS VARCHAR(200) = 'super'
DECLARE #valueA AS INT = 1 -- Can be 1 or 2
DECLARE #valueB AS DECIMAL = 0.5
SELECT *
FROM TABLE_1 t1 WITH (NOLOCK)
WHERE t1.NAME = #name
AND (t1.Type = 1 AND (t1.Amount > 0 AND #valueB < 0)
OR t1.Type <> 1 AND (t1.Amount <= #valueB))
GO
OR Use Dynamic SQL if you feel like it
DECLARE #name AS VARCHAR(200) = 'super'
DECLARE #valueA AS INT = 1 -- Can be 1 or 2
DECLARE #valueB AS DECIMAL = 0.5
DECLARE #SQL VARCHAR(MAX)
DECLARE #Type INT
SELECT TOP 1 #Type = t1.Type
FROM TABLE_1 t1 WITH (NOLOCK)
WHERE t1.NAME = #name
SET #SQL = 'SELECT *
FROM TABLE_1 t1 WITH (NOLOCK)
WHERE t1.NAME = #name'
SET #SQL = CASE
WHEN #Type = 1
THEN #SQL + CHAR(13)+CHAR(10) + ' AND (t1.Amount > 0 AND #valueB < 0)'
ELSE #SQL + CHAR(13)+CHAR(10) + ' AND (t1.Amount <= #valueB)'
END
--PRINT #SQL
EXEC (#SQL)
GO

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