Use a single SQL stored procedure parameter containing multiple expressions in a WHERE column_name IN (parameter) clause

The intention here is to allow users to search for information on a random quantity of assets via one call to a SQL stored procedure .  For example, the SP might receive 'A101' or 'A101,A102,A103,...' as the parameter and need to return info on each asset identified.

In simple testing, the following statement returns the expected results:

DECLARE @lcAssets CHAR(400)
SET @lcAssets = 'A101'

SELECT Assets.Number,AssetsHistory.dtMoved
FROM Assets LEFT OUTER JOIN AssetsHistory ON
    AssetsHistory.keyAssets = Assets.keyAssets
WHERE Assets.Number IN (@lcAssets)

However, if I change @lcAssets to 'A101,A102,A103' I don't get an error message or any results.  I have tried multiple variations for getting this to work, but seem to be stumped.
woodwynAsked:
Who is Participating?

Improve company productivity with a Business Account.Sign Up

x
 
RehanYousafConnect With a Mentor Commented:
Other option is to disect lcAssets and insert it into a variable table

-----------------------------------------------------
CREATE TABLE #Assets  (
	 Number VARCHAR(10)
	,keyAssets VARCHAR(10)
)

INSERT INTO #Assets VALUES ('A101', '1')
INSERT INTO #Assets VALUES ('A102', '2')
INSERT INTO #Assets VALUES ('A103', '3')
INSERT INTO #Assets VALUES ('A104', '2')
INSERT INTO #Assets VALUES ('A105', '3')

-----------------------------------------------------
CREATE TABLE #AssetsHistory (
	 dtMoved DATETIME
	,keyAssets VARCHAR(10)
)

INSERT INTO #AssetsHistory VALUES ('2012-12-05', '1')
INSERT INTO #AssetsHistory VALUES ('2012-12-10', '2')

-----------------------------------------------------
DECLARE @lcAssetsTable TABLE (
	lcAsset VARCHAR(400)
)
DECLARE @lcAssets VARCHAR(400)
DECLARE @x INT
DECLARE @y INT

-----------------------------------------------------
SET @lcAssets = 'A101,A102,A103'
SET @x = 1
SET @y = LEN(@lcAssets) - LEN(REPLACE(@lcAssets, ',', ''))

WHILE @x <= @y
BEGIN
	INSERT INTO @lcAssetsTable VALUES(LEFT(@lcAssets, CHARINDEX(',', @lcAssets) - 1))
	--PRINT LEFT(@lcAssets, CHARINDEX(',', @lcAssets) - 1) + ' - ' + @lcAssets + ' - ' + SUBSTRING(@lcAssets, CHARINDEX(',', @lcAssets) + 1,LEN(@lcAssets))
	SET @lcAssets = SUBSTRING(@lcAssets, CHARINDEX(',', @lcAssets) + 1,LEN(@lcAssets))
	SET @x = @x + 1
END

INSERT INTO @lcAssetsTable VALUES(@lcAssets)

-----------------------------------------------------
SELECT 
	 a.Number
	,h.dtMoved
FROM 
	#Assets a
	LEFT OUTER JOIN #AssetsHistory h ON h.keyAssets = a.keyAssets
WHERE 
	a.Number IN (SELECT lcAsset FROM @lcAssetsTable)
		
DROP TABLE #Assets
DROP TABLE #AssetsHistory

Open in new window

0
 
Jim P.Commented:
Since the input is a string, to use the IN in a query the match would have to be done as 'A101','A201','A301'.

So if you do it as:

select @lcAssets = replace(@lcAssets, Char(44), Char(39)+Char(44)+Char(39))

SELECT Assets.Number,AssetsHistory.dtMoved
FROM Assets LEFT OUTER JOIN AssetsHistory ON
    AssetsHistory.keyAssets = Assets.keyAssets
WHERE Assets.Number IN (@lcAssets)

Open in new window


That should get you results.
0
 
igordevelopCommented:
Hi,

@jimpen
That wouldn't work since the sql will see it as a one string so you can not use it in IN clause.

Either make something to split the string into different parameters so the IN clause can recognize this or just declare more parameters (I guess you know what might be the maximum).
0
What Kind of Coding Program is Right for You?

There are many ways to learn to code these days. From coding bootcamps like Flatiron School to online courses to totally free beginner resources. The best way to learn to code depends on many factors, but the most important one is you. See what course is best for you.

 
Jim P.Connect With a Mentor Commented:
igor,

Good point. You're right.

Add this function to your DB:
CREATE FUNCTION dbo.fnParseList
(
	@Delimiter CHAR,
	@Text TEXT
)
-- Grabbed from http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=76033
RETURNS @Result TABLE (RowID SMALLINT IDENTITY(1, 1) PRIMARY KEY, Data VARCHAR(8000))
AS

BEGIN
	DECLARE	@NextPos INT,
		@LastPos INT

	SELECT	@NextPos = CHARINDEX(@Delimiter, @Text, 1),
		@LastPos = 0

	WHILE @NextPos > 0
		BEGIN
			INSERT	@Result
				(
					Data
				)
			SELECT	SUBSTRING(@Text, @LastPos + 1, @NextPos - @LastPos - 1)

			SELECT	@LastPos = @NextPos,
				@NextPos = CHARINDEX(@Delimiter, @Text, @NextPos + 1)
		END

	IF @NextPos <= @LastPos
		INSERT	@Result
			(
				Data
			)
		SELECT	SUBSTRING(@Text, @LastPos + 1, DATALENGTH(@Text) - @LastPos)

	RETURN
END

Open in new window


Then your query becomes:

SELECT Assets.Number,AssetsHistory.dtMoved
FROM Assets LEFT OUTER JOIN AssetsHistory ON
    AssetsHistory.keyAssets = Assets.keyAssets
WHERE Assets.Number IN (SELECT Data FROM dbo.fnParseList(Char(44), @lcAssets) )

Open in new window

0
 
Saurabh BhadauriaCommented:
Better approach will be to convert the input parameter in table form and use it in inner join..

I have used XMl to convert input parameter to table form..

Complete code is below... have a look and let me know if you have any concerns...

create  procedure your_procedure (@lcAssets nvarchar(max))
as
begin


declare @ta table(assets nvarchar(100))
Declare @x xml =  cast('<Rows><Row>'+replace(@lcAssets,',','</Row><Row>') + '</Row></Rows>' as xml)    -- Convert input parameter to xml form 

insert into @ta
select a.b.value('.','nvarchar(100)')
 from @x.nodes('Rows/Row') a(b) ---- generate tabular for through xquery

--select * from @ta

SELECT Assets.Number,AssetsHistory.dtMoved
FROM @ta t join Assets on    ----input parameter table in inner join 
     t.assets=Assets.Number      
 LEFT OUTER JOIN AssetsHistory ON 
    AssetsHistory.keyAssets = Assets.keyAssets


end 

go

exec your_procedure 'A101,A102,A103'

Open in new window

0
 
RehanYousafConnect With a Mentor Commented:
One option is to use dynamic sql

CREATE TABLE #Assets  (
	 Number VARCHAR(10)
	,keyAssets VARCHAR(10)
)

INSERT INTO #Assets VALUES ('A101', '1')
INSERT INTO #Assets VALUES ('A102', '2')
INSERT INTO #Assets VALUES ('A103', '3')
INSERT INTO #Assets VALUES ('A104', '2')
INSERT INTO #Assets VALUES ('A105', '3')

CREATE TABLE #AssetsHistory (
	 dtMoved DATETIME
	,keyAssets VARCHAR(10)
)

INSERT INTO #AssetsHistory VALUES ('2012-12-05', '1')
INSERT INTO #AssetsHistory VALUES ('2012-12-10', '2')


DECLARE @SQL VARCHAR(MAX)
DECLARE @lcAssets VARCHAR(400)
SET @lcAssets = 'A101'',''A102'',''A103'

SET @SQL = '
	SELECT 
		 a.Number
		,h.dtMoved
	FROM 
		#Assets a
		LEFT OUTER JOIN #AssetsHistory h ON h.keyAssets = a.keyAssets
	WHERE 
		a.Number IN (''' + @lcAssets + ''')'
		
EXEC (@SQL)

DROP TABLE #Assets
DROP TABLE #AssetsHistory

Open in new window

0
 
krtyknmsqlCommented:
Check this..
DECLARE @lcAssets CHAR(400)
SET @lcAssets = 'A101,A102,A103';

WITH CTESplit
AS
(
    SELECT 
        0 AS Pos,
        1 AS PosStart,
        CHARINDEX(',',@lcAssets + ',') AS PosEnd
    UNION ALL
    SELECT
        Pos + 1 AS Pos,
        PosEnd + 1 AS PosStart,
        CHARINDEX(',', @lcAssets + ',',PosEnd + 1 ) AS PosEnd
    FROM CTESplit
    WHERE  CHARINDEX(',', @lcAssets + ',',PosEnd + 1 ) > 1
),
CTEValue
AS
(
    SELECT 
	    SUBSTRING(@lcAssets,PosStart,PosEnd - PosStart) Value
    FROM CTESplit  
)


SELECT Assets.Number,AssetsHistory.dtMoved
FROM Assets LEFT OUTER JOIN AssetsHistory ON 
    AssetsHistory.keyAssets = Assets.keyAssets
INNER JOIN CTEValue ON 
    CTEValue.Value = Assets.Number

Open in new window

0
 
woodwynAuthor Commented:
Thanks all.  I tested all these and most worked.  I also found one other solution that someone else might find useful.

CREATE TABLE [dbo].[Student](
      [Student ID]   [varchar] (6) Not NULL ,
      [Student Name] [varchar](50) NOT NULL)
GO
 
Insert into dbo.[Student] values ('STD001','Bob')
Insert into dbo.[Student] values ('STD002','Alexander')
Insert into dbo.[Student] values ('STD003','Hosanna')
Insert into dbo.[Student] values ('STD004','William')
Insert into dbo.[Student] values ('STD005','Hulda')
Insert into dbo.[Student] values ('STD006','Jacoba')

Declare  @SQL         VARCHAR(MAX)
Declare  @WhereClause VARCHAR(MAX)
Set @WhereClause='Bob,Hulda,Jacoba'
 
SET @WhereClause=REPLACE(@WhereClause,',',''',''')
Set @SQL='Select *
from dbo.[Student]
Where  [Student Name] In (''' + @WhereClause + ''')'

Taken from http://raresql.com/2011/12/21/how-to-use-multiple-values-for-in-clause-using-same-parameter-sql-server/
0
 
woodwynAuthor Commented:
Excellent Solutions!
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.