IN Clause is not taking dynamic passed value in SQL 2008 - sql

I have below SQL Procedure where I am passing and setting dynamic values.
Code: SQL Procedure Name: GetArchivedData
ALTER PROCEDURE [dbo].[GetArchivedData](#PublicationURL varchar(100),#Number int,#Action varchar(max))
AS
DECLARE #TEST Varchar(max)
IF (#Action = 'ALL')
BEGIN
SET #TEST = '''ADD'''+','+'''UPD'''+','+'''DEL''';
END
ELSE
BEGIN
SET #TEST = #Action
END
IF (#Number !=0)
BEGIN
PRINT 'Inside'+ #TEST
BEGIN TRANSACTION TRAN1
SELECT
1 AS Tag,
NULL AS Parent,
NULL AS [root!1!],
NULL AS [Item!2!Id],
NULL AS [Item!2!Action],
NULL AS [Item!2!Publication_Id],
NULL AS [Item!2!Item_Reference_Id],
NULL AS [Item!2!Item_type],
convert( datetime, '9999-01-01' ) AS [Item!2!Last_Published_Date],
NULL AS [Item!2!Url],
NULL AS [Item!2!Schema_Id]
UNION
SELECT TOP (#Number)
2,
1,
'1',
T.ID,
T.ACTION,
T.PUBLICATION_ID,
T.ITEM_REFERENCE_ID,
T.ITEM_TYPE,
T.LAST_PUBLISHED_DATE,
T.URL,
T.SCHEMA_ID
FROM DBO.AUTN_ITEMS T WHERE FLAG=1 AND ACTION IN (#TEST) AND URL LIKE #PublicationURL+'%'
ORDER BY [Item!2!Last_Published_Date] DESC
FOR XML EXPLICIT
COMMIT TRANSACTION TRAN1
END
ELSE IF (#Number = 0)
BEGIN
PRINT 'Outside'+ #TEST
BEGIN TRANSACTION TRAN2
SELECT
1 AS Tag,
NULL AS Parent,
NULL AS [root!1!],
NULL AS [Item!2!Id],
NULL AS [Item!2!Action],
NULL AS [Item!2!Publication_Id],
NULL AS [Item!2!Item_Reference_Id],
NULL AS [Item!2!Item_type],
convert( datetime, '9999-01-01' ) AS [Item!2!Last_Published_Date],
NULL AS [Item!2!Url],
NULL AS [Item!2!Schema_Id]
UNION
SELECT
2,
1,
'1',
T.ID,
T.ACTION,
T.PUBLICATION_ID,
T.ITEM_REFERENCE_ID,
T.ITEM_TYPE,
T.LAST_PUBLISHED_DATE,
T.URL,
T.SCHEMA_ID
FROM DBO.AUTN_ITEMS T WHERE FLAG=1 AND ACTION IN (#TEST) AND URL LIKE #PublicationURL+'%'
ORDER BY [Item!2!Last_Published_Date] DESC
FOR XML EXPLICIT
COMMIT TRANSACTION TRAN2
END
RETURN
Excuting SQL Procedure:
DECLARE #return_value int
EXEC #return_value = [dbo].[GetArchivedData]
#PublicationURL = N'/in',
#Number = 0,
#Action = N'ALL'
SELECT 'Return Value' = #return_value
I can see the values are getting set properly, if I am printing it in the procedure however one value works perfectly but when I am setting SET #TEST = '''ADD'''+','+'''UPD'''+','+'''DEL'''; no results are returned
Please suggest!!

Use Table Variable instead of #Test as string like this,
DECLARE #ActionTbl table ([Action] varchar(3))
IF (#Action = 'ALL')
BEGIN
INSERT INTO #ActionTbl SELECT 'Add' AS ID
UNION ALL
SELECT 'UPD' AS ID
UNION ALL
SELECT 'DEL' ID
END
ELSE
BEGIN
INSERT INTO #ActionTbl VALUES(#Action)
END
And in query use
ACTION IN (Select Action from #ActionTbl)
Instead of
ACTION IN (#TEST)

First of all the main issue here - you CAN'T use IN to search one string in another. IN is used to search value in the row set of values. Also the #TEST STRING value is 'ADD,'UPD','DEL' (each item is with quotas). I guess the Action field contains values without quotas so additional y to other answers there is one more way:
Replace
ACTION IN (#TEST)
with
#TEST LIKE '%'''+ACTION+'''%'

Related

I am having the error with a Subquery returning more than one value. How do I reduce to one?

At the very end of the Stored procedure a SELECT statement is made to display the contents of the Table including function that will simultaneously populate fields in the table.
Here is the Select Statement:
IF #type = 'SH'
SELECT DISTINCT *
FROM #History
ORDER BY 1, 2, 3, 4, 5
ELSE
SELECT DISTINCT AmhazName
,Activity
,ServiceName
,Sarid
,PerformedDate
,UserRole
,Details
,dbo.ufn_SarHistoryActionText(sarid, status, performeddate) AS [ActionText]
,FullName
,CategoryDescription
,StatusDescription
,ActionPerformed
,Case
when Details like '%ProjManagerId%'
Then dbo.ufn_GetUserForHistoryReport (PerformedDate, SarId, '%ProjManagerId%')
Else
--when Details like '%UserId%'
dbo.ufn_GetUserForHistoryReport (PerformedDate, SarId, '%UserId%')
--(select 'no user') as [AssignedUser]
End as [AssignedUser]
--,dbo.ufn_GetPMForHistoryReport(PerformedDate, SarId) as [AssignedUser]
FROM #history
ORDER BY 1, 2, 3, 4, 5
DROP TABLE #Historyw
Here is the function I believe is causing problems:
ALTER FUNCTION [dbo].[ufn_SarHistoryActionText]
(
-- Add the parameters for the function here
#sarID int
, #status varchar(6)
, #statusDate datetime
)
RETURNS varchar(100)
AS
BEGIN
-- Declare the return variable here
DECLARE #Result varchar(100)
set #Result = (
SELECT C.ActionText
from LuStatusChange as C
WHERE C.FromStatus = dbo.ufn_SarHistoryPriorStatus(#sarID,#status,#statusDate)
AND C.ToStatus = #status
)
-- Return the result of the function
RETURN #Result
END
GO
As I debug and walk through loads of values, I haven't come across anything that resulted in multiple values. maybe I'm missing something.
Add TOP 1 in the select inside the function:
SELECT TOP 1 C.ActionText
Can you replace
set #Result = (
SELECT C.ActionText
from LuStatusChange as C
WHERE C.FromStatus = dbo.ufn_SarHistoryPriorStatus(#sarID,#status,#statusDate)
AND C.ToStatus = #status
)
as below:
#Result ***IN*** (
SELECT C.ActionText
from LuStatusChange as C
WHERE C.FromStatus = dbo.ufn_SarHistoryPriorStatus(#sarID,#status,#statusDate)
AND C.ToStatus = #status
)
If functionally your query should not written more than 1 row, something is wrong with your query.

Stored procedure in SQL Server with select query then if condition and then insert query

I want to create a stored procedure in which first select statement and depending on the selected parameter if valid, insert record in the another table else do nothing. How to write the stored procedure for this?
I have tried with stored procedure and it is executed with no errors, but when I tried to EXEC stored procedure, it doesn't do the task as written in procedure.
CREATE PROCEDURE sp_CreateExpiryDocumentFollowup
(#param INT = NULL,
#param2 INT = NULL,
#param3 INT = 1,
#param4 BIT = 0,
#followupid INT = NULL)
AS
BEGIN
SELECT
#param1 = [TABLE_A].[VEHICLE_ID],
#param2 = [TABLE_A].[VEHICLE_DOCUMENT_ID],
#followupid = [TABLE_B].[FOLLOWUP_ID]
FROM
[TABLE_A]
LEFT JOIN
[TABLE_B] ON [TABLE_B].[VEHICLE_DOCUMENT_ID] != [TABLE_A].[VEHICLE_DOCUMENT_ID]
WHERE
[TABLE_A].[STATUS] = 1;
IF #followupid = NULL
BEGIN
INSERT INTO [TABLE_B] (VALUE_1, VALUE_2, VALUE_3, VALUE_4)
VALUES (#param1, #param2, #param3, #param4)
END
END
GO
I expect the record to insert in the TABLE_B if #followupid is null. The #followupid is null as I executed the select statement only. But while executing whole stored procedure it will return the result 0 row which in my case should be 1. And I checked the table as well, no any record is inserted but stored procedure runs successfully.
Here's how I would write it:
CREATE PROCEDURE sp_CreateExpiryDocumentFollowup
(
--#param1 int = null -- this is no longer needed
--#param2 int = null -- this is no longer needed
#param3 int = 1,
#param4 bit = 0
--,#followupid int = null -- -- this is no longer needed
)
AS BEGIN
INSERT INTO [TABLE_B] (VALUE_1, VALUE_2, VALUE_3, VALUE_4)
SELECT [TABLE_A].[VEHICLE_ID], [TABLE_A].[VEHICLE_DOCUMENT_ID], #param3, #param4
FROM [TABLE_A]
LEFT JOIN [TABLE_B]
-- as stated in the comments to the questions,
-- Shouldn't this condition be `=` instead of `!=`?
ON [TABLE_B].[VEHICLE_DOCUMENT_ID] != [TABLE_A].[VEHICLE_DOCUMENT_ID]
WHERE [TABLE_A].[STATUS] = 1
AND [TABLE_B].[FOLLOWUP_ID] IS NULL
END
GO
Instead of first selecting and then inserting, you can do an insert...select operation which leads to a shorter, more readable code.
This way, if the select statement doesn't return any rows, nothing gets inserted into the target table.
Also, you can't use equality operators on NULL in SQL. Instead, you can only use IS NULL or IS NOT NULL (Or NOT IS NULL if you like that better).
Please note that if the select statement returns more than one row, all of them will be inserted into the target table.

SQL dynamic columns and Update multiple columns

I have a table UserPermission which has a number of columns of TINYINT type. e.g Read, Write, Update, Delete, Access etc.
I get three parameters in the stored procedure: #UserId, #ColNames, #ColValues where #ColNames and #ColValues are comma separated values.
How can I insert or update the table row (if already exists) with the passed column names and corresponding values.
I try to write the dynamic query which runs fine for INSERT but I was unable to write the UPDATE query dynamically with each column and its value to be concatenate.
Any response would be appreciated
Thanks in advance.
This is a somewhat dirty way to do what you require. However, if you create the following Stored Procedure:
CREATE FUNCTION [dbo].[stringSplit]
(
#String NVARCHAR(4000),
#Delimiter NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
WITH Split(stpos,endpos)
AS(
SELECT 0 AS stpos, CHARINDEX(#Delimiter,#String) AS endpos
UNION ALL
SELECT endpos+1, CHARINDEX(#Delimiter,#String,endpos+1)
FROM Split
WHERE endpos > 0
)
SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),
'Data' = SUBSTRING(#String,stpos,COALESCE(NULLIF(endpos,0),LEN(#String)+1)-stpos)
FROM Split
)
You can then use that Procedure to join the data together:
DECLARE #TotalCols INT
DECLARE #TotalVals INT
SET #TotalCols = (
SELECT COUNT(ID) AS Total
FROM dbo.stringSplit('department, teamlead', ',')
);
SET #TotalVals = (
SELECT COUNT(ID) AS Total
FROM dbo.stringSplit('IT, Bob', ',')
);
IF #TotalCols = #TotalVals
BEGIN
IF OBJECT_ID('tempdb..#temptable') IS NOT NULL
DROP TABLE #temptable
CREATE TABLE #temptable (
ColName VARCHAR(MAX) NULL
,ColValue VARCHAR(MAX) NULL
)
INSERT INTO #temptable
SELECT a.DATA
,b.DATA
FROM dbo.stringSplit('department, teamlead', ',') AS a
INNER JOIN dbo.stringSplit('IT, Bob', ',') AS b ON a.Id = b.Id
SELECT *
FROM #temptable;
END
It's not very efficient, but it will bring you the desired results.
You can then use the temp table to update, insert and delete as required.
Instead of having a comma delimited list I would create a separate parameter for each Column and make its default value to NULL and in the code update nothing if its null or insert 0. Something like this....
CREATE PROCEDURE usp_UserPermissions
#UserID INT
,#Update INT = NULL --<-- Make default values NULL
,#Delete INT = NULL
,#Read INT = NULL
,#Write INT = NULL
,#Access INT = NULL
AS
BEGIN
SET NOCOUNT ON;
Declare #t TABLE (UserID INT, [Update] INT,[Read] INT
,[Write] INT,[Delete] INT,[Access] INT)
INSERT INTO #t (Userid, [Update],[Read],[Write],[Delete],[Access])
VALUES (#UserID , #Update , #Read, #Write , #Delete, #Access)
IF EXISTS (SELECT 1 FROM UserPermission WHERE UserID = #UserID)
BEGIN
UPDATE up -- Only update if a value was provided else update to itself
SET up.[Read] = ISNULL(t.[Read] , up.[Read])
,up.[Write] = ISNULL(t.[Write] , up.[Write])
,up.[Update] = ISNULL(t.[Update] , up.[Update])
,up.[Delete] = ISNULL(t.[Delete] , up.[Delete])
,up.[Access] = ISNULL(t.[Access] , up.[Access])
FROM UserPermission up
INNER JOIN #t t ON up.UserID = t.UserID
END
ELSE
BEGIN
-- if already no row exists for that User add a row
-- If no value was passed for a column add 0 as default
INSERT INTO UserPermission (Userid, [Update],[Read],[Write],[Delete],[Access])
SELECT Userid
, ISNULL([Update], 0)
, ISNULL([Read], 0)
, ISNULL([Write], 0)
, ISNULL([Delete], 0)
, ISNULL([Access], 0)
FROM #t
END
END

Temp table has only one row inserted

Hi I have an SP in which i create a temporary table to store some values.
ALTER PROCEDURE [dbo].[test]
#id int,
#funds_limit money
AS
BEGIN
SET NOCOUNT ON;
DECLARE #threshold money;
CREATE TABLE #ConfigurationTemp
(id int,
name varchar(100) not null,
type varchar(100),
value varchar(100))
INSERT #ConfigurationTemp EXEC get_config #id, 'testType', null
select #threshold = value
from #ConfigurationTemp
where id=#id and name='testLimit'
print #threshold
IF (#funds_limit IS NOT NULL) AND (#threshold < #funds_limit)
BEGIN
DROP TABLE #ConfigurationTemp;
RETURN 1000;
END
select #threshold = value
from #ConfigurationTemp
where id=#id and name='testLimit1'
print #threshold
IF (#funds_limit IS NOT NULL) AND (#threshold < #funds_limit)
BEGIN
DROP TABLE #ConfigurationTemp;
RETURN 1001;
END
END
RETURN 0;
END
The temporary table have multiple rows.
eg:
1, fund_limit, testType, 10
2, fund_min_limit, testType, 20
I need to first validate the value for fund_limit (10) with the user input value (which will be an input parameter to the SP). If the validation fails, i return with an error code. If not, I go for the next check. i.e., fund_min_limit. I do the same with it and return a different error code. If no validation fails, i will return 0 which is considered to be a success.
In this case, I am getting same value for threshold always. i.e., the value of first row... 10.
How can I get the different threshold value from the temp table with respect to the name?
When you assign scalar variable with select - it may be not assigned (unchanged - may keep value from previous assignment) if this select returned zero rows. To ensure your variable changed it's value rewrite it as set expression.
So if you misspelled second threshold name you may be "getting" same #threshold value because second statement does not assign anything to your variable i.e. variable contains value from prior assignment (select). You may test it with additional variable for second threshold - it will be always NULL if i guessed the issue reason.
Also you are applying same #id filter which is a scalar variable. But your rows have different ids. So there is no chances right now to get any other threshold's value than for #id given.
set #threshold = (select t.value
from #ConfigurationTemp t
where t.name='testLimit')
print #threshold
IF #threshold < #funds_limit
RETURN 1000;
set #threshold = (select t.value
from #ConfigurationTemp t
where t.name='testLimit 2')
print #threshold
IF #threshold < #funds_limit
RETURN 1001;
If will succeed only when both arguments are NOT NULL.
One more approach:
declare
#threshold_a int,
#threshold_b int,
#threshold_c int
;with test as
(
select 'a' as name, 25 as value
union all
select 'b', 3
union all
select 'c', 100
union all
select 'd', -1
)
select
#threshold_a = case when t.name = 'a' then t.value else #threshold_a end,
#threshold_b = case when t.name = 'b' then t.value else #threshold_b end,
#threshold_c = case when t.name = 'c' then t.value else #threshold_c end
from test t
select
#threshold_a as [a],
#threshold_b as [b],
#threshold_c as [c]
GO
single select, several variables.
You have RETURN in your IF statment.
... RETURN 1000
and
... RETURN 1001
After insert a row the procedure end.
Maybe you want to assign a result to a variable
#return_Value = ''
#return_Value = #return_Value + '1000, '
....
#return_Value = #return_Value + '1001, '
RETURN #return_Value

Function return table variable

I'm trying to create a function that return a table variable.So firstly i get data from Table1 and put it in another table variable. Here i want check if this variable isempty the function return the parameter result else return the result of the table variable
The function script is bellow :
USE[DATABase1]
GO
IF OBJECT_ID (N'CodeFunc', N'TF') IS NOT NULL DROP FUNCTION dbo.CodeFunc;
GO
CREATE FUNCTION CodeFunc ( #Code nvarchar(4) , #Table nvarchar(40) = '' )
RETURNS #VirtualDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
AS
BEGIN
DECLARE #CodeM nvarchar(400)
DECLARE #imax INT SET #imax = ##ROWCOUNT
DECLARE #i INT SET #i = 1
DECLARE #SelectDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
INSERT #SelectDAT
SELECT Code FROM table1
WHERE table1.id = 41
IF(EXISTS (SELECT 1 FROM #SelectDAT))
BEGIN
WHILE (#i <= #imax)
BEGIN
SELECT #CodeM = Code FROM #SelectDAT WHERE RowID = #i
INSERT INTO #VirtualDAT(Code) VALUES (#CodeM)
SET #i = #i + 1
END
END
ELSE
INSERT INTO #VirtualDAT(Code) VALUES (#Code)
RETURN
END
So this script works without put it inside function.
And i test this function like this :SELECT * FROM dbo.CodeFunc( 'toto',Default ) the result is :
IF(EXISTS (SELECT 1 FROM #SelectDAT)) no record returned
esle the result is ok
As VR46 says. The ##ROWCOUNT will be set to 0 because there is no query before it. Any code executing in a function happens as a seperate set of queries. It was probably returning a value outside the function because you had previously used the query window for another unrelated query
You could re-factor this function quite dramatically. Look below, ##ROWCOUNT will work here as it is just after the insert query and will definitely have a value based on the insert.
I have not been able to test this, but I think something like this should do the same job.
USE[DATABase1]
GO
IF OBJECT_ID (N'CodeFunc', N'TF') IS NOT NULL DROP FUNCTION dbo.CodeFunc;
GO
CREATE FUNCTION CodeFunc ( #Code nvarchar(4) , #Table nvarchar(40) = '' )
RETURNS #VirtualDAT TABLE
(
RowID INT IDENTITY ( 1 , 1 ),
Code nvarchar(400)
)
AS
BEGIN
insert into #VirtualDAT
Select Code from table1 where table1.id = 41
if ##ROWCOUNT = 0
begin
INSERT INTO #VirtualDAT(Code) VALUES (#Code)
end
RETURN
END
Since you are assigning #imax with ##ROWCOUNT right after declaration of variable will be initialized with zero.
From MSDN ##ROWCOUNT
Returns the number of rows affected by the last statement.
If am not wrong you need to assign value to #imax after the insert into..select query.
INSERT #SelectDAT
SELECT Code FROM table1
WHERE table1.id = 41
SET #imax= ##ROWCOUNT
You can do the same in SET BASED APPROACH without using while loop.
CREATE FUNCTION Codefunc (#Code NVARCHAR(4),
#Table NVARCHAR(40) = '')
returns #VirtualDAT TABLE (
rowid INT IDENTITY ( 1, 1 ),
code NVARCHAR(400))
AS
BEGIN
IF EXISTS (SELECT code
FROM table1
WHERE table1.id = 41)
BEGIN
INSERT INTO #VirtualDAT
(code)
SELECT code
FROM table1
WHERE table1.id = 41
END
ELSE
INSERT INTO #VirtualDAT
(code)
VALUES (#Code)
RETURN
END