SQL Server - parameter with unknown number of multiple values - sql-server

I'm creating a grid that has two columns: Name and HotelId. The problem is that data for this grid should be sent with a single parameter of VARCHAR type and should look like this:
#Parameter = 'Name1:5;Name2:10;Name3:6'
As you can see, the parameter contains Name and a number that represents ID value and you can have multiple such entries, separated by ";" symbol.
My first idea was to write a query that creates a temp table that will have two columns and populate it with data from the parameter.
How could I achieve this? It seems like I need to split the parameter two times: by the ";" symbol for each row and then by ":" symbol for each column.
How should I approach this?
Also, if there is any other more appropriate solution, I'm open to suggestions.

First Drop the #temp table if Exists...
IF OBJECT_ID('tempdb..#temp', 'U') IS NOT NULL
/*Then it exists*/
DROP TABLE #temp
Then create #temp table
CREATE TABLE #temp (v1 VARCHAR(100))
Declare all the #Paramter....
DECLARE #Parameter VARCHAR(50)
SET #Parameter= 'Name1:5;Name2:10;Name3:6'
DECLARE #delimiter nvarchar(1)
SET #delimiter= N';';
Here, Inserting all #parameter value into #temp table using ';' separated..
INSERT INTO #temp(v1)
SELECT * FROM(
SELECT v1 = LTRIM(RTRIM(vals.node.value('(./text())[1]', 'nvarchar(4000)')))
FROM (
SELECT x = CAST('<root><data>' + REPLACE(#Parameter, #delimiter, '</data><data>') + '</data></root>' AS XML).query('.')
) v
CROSS APPLY x.nodes('/root/data') vals(node)
)abc
After inserting the value into #temp table..get all the value into ':' seprated...
select Left(v1, CHARINDEX(':', v1)-1) as Name , STUFF(v1, 1, CHARINDEX(':', v1), '') as HotelId FROM #temp
Then you will get this type of Output

Related

T-SQL, Select string between 4th and 5th comma

How to get the string between 5th and 6th comma, without using a function, because I am selecting other columns as well, so using a function will not help me
This is the string value
"RBC,Dev,PROM0006581,T85230,P0263240,**Dev**,CHG0048754_DYN_DIF,Code changes as part of DYN -Tiered/Scaled & Bonus Interest for DIF Products
"
can someone please help me
You can do following:
DECLARE #Values VARCHAR(MAX) = 'RBC,Dev,PROM0006581,T85230,P0263240,**Dev**,CHG0048754_DYN_DIF,Code changes as part of DYN -Tiered/Scaled & Bonus Interest for DIF Products'
IF OBJECT_ID('TEMPDB..#Values') IS NOT NULL DROP TABLE #Values;
CREATE TABLE #Values (ID INT IDENTITY(1,1),Item VARCHAR(100));
DECLARE #Insert VARCHAR(MAX) = 'INSERT INTO #Values VALUES ('''+REPLACE(#Values,',','''),(''')+''');';
EXEC (#Insert);
SELECT * FROM #Values
Resulting:
And
SELECT Item FROM #Values WHERE ID=5
Resulting
This should get you what you're looking for...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #testData;
CREATE TABLE #TestData (
String VARCHAR(1000) NOT NULL
);
INSERT #TestData (String) VALUES ('RBC,Dev,PROM0006581,T85230,P0263240,**Dev**,CHG0048754_DYN_DIF,Code changes as part of DYN -Tiered/Scaled & Bonus Interest for DIF Product');
SELECT *,
RequestedValue = SUBSTRING(td.String, c4.Comma + 1, ISNULL(c5.Comma - c4.Comma - 1, 1000))
FROM
#TestData td
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(',', td.String, 1), 0)) ) c1 (Comma)
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(',', td.String, c1.Comma + 1), 0)) ) c2 (Comma)
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(',', td.String, c2.Comma + 1), 0)) ) c3 (Comma)
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(',', td.String, c3.Comma + 1), 0)) ) c4 (Comma)
CROSS APPLY ( VALUES (NULLIF(CHARINDEX(',', td.String, c4.Comma + 1), 0)) ) c5 (Comma);
HTH, Jason
Here is a simple in-line and XML Safe approach (not just &'s).
Grabbing the 5th value. Not 100% sure if this needs to be adjusted up or down
Sample Data
Declare #YourTable table (ID int,SomeColumn varchar(max))
Insert Into #YourTable values
(1,'RBC,Dev,PROM0006581,T85230,P0263240,**Dev**,CHG0048754_DYN_DIF,Code changes as part of DYN -Tiered/Scaled & Bonus Interest for DIF Products')
The Query
Select ID
,SomeValue = Cast('<x>' + replace((Select replace(SomeColumn,',','|||') as [*] For XML Path('')),'|||','</x><x>')+'</x>' as xml).query('.').value('/x[5]','varchar(max)')
From #YourTable
Returns
ID SomeValue
1 P0263240
There is one solution that involve replacing comma , with xml tags and thereby converting column into an XML datatype.
Below query will give you 6th item (i.e between 5th and 6th comma as you want)
select *
, Column2 = convert(XML,'<s>' + REPLACE(REPLACE(MyColumn,'&','&'),',','</s><s>') + '</s>').value('/s[6]','varchar(200)')
FROM [dbo].[Table1]
SQL Fiddle Demo
But you have to escape any reserved XML character in your data by replacing it with entity references otherwise XML cast will fail. like & is replaced with &amp in the above query
XML reserved character on Technet
Updated Query
As #John mentioned in his answer FOR XML Path is a neat and elegant way to escape special XML characters.So my updated query would be.
SELECT *
, Column2 = convert(XML,'<s>' + REPLACE((SELECT ISNULL(MyColumn,'') FOR XML Path('')),',','</s><s>') + '</s>').value('/s[6]','varchar(200)')
FROM [dbo].[Table1]
Write a table-valued function that accepts the string and a delimiter character as parameters and returns the parsed elements of the string and the element's position as a two-column table. You can then JOIN to an invocation of that function, which SQL will treat just like a table, so if you want the text between the 5th and 6th comma then you'd put "and position = 6" in the JOIN clause.

Most effective way to check sub-string exists in comma-separated string in SQL Server

I have a comma-separated list column available which has values like
Product1, Product2, Product3
I need to search whether the given product name exists in this column.
I used this SQL and it is working fine.
Select *
from ProductsList
where productname like '%Product1%'
This query is working very slowly. Is there a more efficient way I can search for a product name in the comma-separated list to improve the performance of the query?
Please note I have to search comma separated list before performing any other select statements.
user defined functions for comma separation of the string
Create FUNCTION [dbo].[BreakStringIntoRows] (#CommadelimitedString varchar(max))
RETURNS #Result TABLE (Column1 VARCHAR(max))
AS
BEGIN
DECLARE #IntLocation INT
WHILE (CHARINDEX(',', #CommadelimitedString, 0) > 0)
BEGIN
SET #IntLocation = CHARINDEX(',', #CommadelimitedString, 0)
INSERT INTO #Result (Column1)
--LTRIM and RTRIM to ensure blank spaces are removed
SELECT RTRIM(LTRIM(SUBSTRING(#CommadelimitedString, 0, #IntLocation)))
SET #CommadelimitedString = STUFF(#CommadelimitedString, 1, #IntLocation, '')
END
INSERT INTO #Result (Column1)
SELECT RTRIM(LTRIM(#CommadelimitedString))--LTRIM and RTRIM to ensure blank spaces are removed
RETURN
END
Declare #productname Nvarchar(max)
set #productname='Product1,Product2,Product3'
select * from product where [productname] in(select * from [dbo].[![enter image description here][1]][1][BreakStringIntoRows](#productname))
Felix is right and the 'right answer' is to normalize your table. Although, maybe you have 500k lines of code that expect this column to exist as it is. So your next best (non-destructive) answer is:
Create a table to hold normalize data:
CREATE TABLE ProductsList2 (ProductId INT, ProductName VARCHAR)
Create a TRIGGER that on UPDATE/INSERT/DELETE maintains ProductList2 by splitting the string 'Product1,Product2,Product3' into three records.
Index your new table.
Query against your new table:
SELECT *
FROM ProductsList
WHERE ProductId IN (SELECT x.ProductId
FROM ProductsList2 x
WHERE x.ProductName = 'Product1')

I am not getting values by passing variable using IN query in SQL

I am passing string values from my code like '12th Standard/Ordinary National Diploma,Higher National Diploma' to SQL query, but I am not getting any values and nothing showing any result.
My SQL query:
declare #qua varchar(250),#final varchar(250),#Qualification varchar(250)
set #Qualification= '12th Standard/Ordinary National Diploma,Higher National Diploma'
set #qua =replace(#Qualification,',',''',''')
set #final= ''''+#qua+''''
select * from mytablename in(#final)
Result: Data is not displaying
Thank you in advance.
Instead do it using a table variable like
declare #tbl table(qual varchar(250));
insert into #tbl
select '12th Standard/Ordinary National Diploma'
union
select 'Higher National Diploma';
select * from mytablename where somecolumn in(select qual from #tbl);
Despite trying to put quote marks in there, you're still only passing a single string to the IN. The string just contains embedded quotes and SQL Server is looking for that single long string.
You also don't seem to be comparing a column for the IN.
Your best bet is to pass in multiple string variables, but if that's not possible then you'll have to write a function that parses a single string into a resultset and use that. For example:
SELECT
Column1, -- Because we never use SELECT *
Column2
FROM
MyTableName
WHERE
qualification IN (SELECT qualification FROM dbo.fn_ParseString(#qualifications))
You can insert all your search criteria in one table and then can easily do a lookup on the main table, example below:
DECLARE #MyTable TABLE (Name VARCHAR(10), Qualification VARCHAR(50))
DECLARE #Search TABLE (Qualifications VARCHAR(50))
INSERT INTO #MyTable VALUES ('User1','12th Standard'), ('User2','Some Education'),
('User3','Ordinary National Diploma'), ('User4','Some Degree'),
('User5','Higher National Diploma')
INSERT INTO #Search VALUES ('12th Standard'),('Ordinary National Diploma'),('Higher National Diploma')
SELECT MT.*
FROM #MyTable MT
INNER JOIN (SELECT Qualifications FROM #Search) S ON S.Qualifications = MT.Qualification
As previous said, you are passing a string with commas, not comma separated values. It needs to be split up into separate values.
You can do this by passing the qualification string into XML which you can use to turn it into separate rows of data.
The IN parameter will then accept the data as separate values.
DECLARE #Qualifications as varchar(150) = '12th Standard/Ordinary National Diploma,Higher National Diploma'
Declare #Xml XML;
SET #Xml = N'<root><r>' + replace(#Qualifications, char(44),'</r><r>') + '</r></root>';
select *
from MyTableName
Where MyTableName.Qualification in
(select r.value('.','varchar(max)') as item
from #Xml.nodes('//root/r') as records(r))
Alternatively you can create a table-valued function that splits according to input like in your case its ',' and then INNER JOIN with the returnColumnname and that particular column that you want to filter
SELECT COLUMNS, . . . .
FROM MyTableName mtn
INNER JOIN dbo.FNASplitToTable(#qualifications, ',') csvTable
ON csvTable.returnColumnName = mtn.somecolumn
Table Valued function might be like:
CREATE FUNCTION dbo.FNASplitToTable (#string varchar(MAX), #splitType CHAR(1))
RETURNS #result TABLE(Value VARCHAR(100))
AS
BEGIN
DECLARE #x XML
SELECT #x = CAST('<A>' + REPLACE(#string, #splitType, '</A><A>') + '</A>' AS XML)
INSERT INTO #result
SELECT LTRIM(t.value('.', 'VARCHAR(100)')) AS inVal
FROM #x.nodes('/A') AS x(t)
RETURN
END
GO

What is the best way to concatonate three varchar fields, each of which is either null or contains a comma separated string?

We have a result set that has three fields and each of those fields is either null or contains a comma separated list of strings.
We need to combine all three into one comma separated list and eliminate duplicates.
What is the best way to do that?
I found a nice function that can split a string and return a table:
T-SQL split string
I tried to create a UDF that would take three varchar parameters and call that split string function three times, combine them into one table, and then use a FOR XML from there and return it as one comma separated string.
But SQL is complaining about having a SELECT in a function.
Here's an example using the SplitString function you referenced.
DECLARE
#X varchar(max) = 'A, C, F'
, #Y varchar(max) = null
, #Z varchar(max) = 'A, D, E, A'
;WITH SplitResults as
(
-- Note: the function does not remove leading spaces.
SELECT LTRIM([Name]) [Name] FROM SplitString(#X)
UNION
SELECT LTRIM([Name]) [Name] FROM SplitString(#Y)
UNION
SELECT LTRIM([Name]) [Name] FROM SplitString(#Z)
)
SELECT STUFF((
SELECT ', ' + [Name]
FROM SplitResults
FOR XML PATH(''), TYPE
-- Note: here we're pulling the value out in case any characters were escaped, ie. &
-- and then STUFF is removing the leading ,<space>
).value('.', 'nvarchar(max)'), 1, 2, '')
I would not store data as a comma separated string in a single field. Separate the string to a new table and combine it to a string again when you need to.
Finding duplicates and managing the data will also be much easier.
I've used this function before (I didn't write it, and unfortunately cannot remember where I found it) to split a string and add a key (in this case an int) to the data as a separate table, linking back to the original table's PK
CREATE FUNCTION SplitWithID (#id int, #sep VARCHAR(10), #s VARCHAR(MAX))
RETURNS #t TABLE
(
id int,
val VARCHAR(MAX)
)
AS
BEGIN
DECLARE #xml XML
SET #XML = N'<root><r>' + REPLACE(#s, #sep, '</r><r>') + '</r></root>'
INSERT INTO #t(id,val)
SELECT #id, r.value('.','VARCHAR(40)') as Item
FROM #xml.nodes('//root/r') AS RECORDS(r)
RETURN
END
GO
Once you have the data on separate rows you can use any duplicate removal technique to clean the data before applying a primary key to the table.

Select just first line of chars up to CR/LF from a text column

Is it possible to select or substring just the first line of chars in a SQL Server text column, to then prepend as the first line of chars in another text field in another table?
If you are running SQL Server 2005 or higher:
In the LEFT command, use CHARINDEX on CHAR(13) to find the position of the first line feed character, as in the following example:
declare #a table(id int identity(1,1) not null, lines text); --Source
declare #b table(id int identity(1,1) not null, lines text); --Target
insert into #a(lines) values ('1111111'+char(13)+char(10)+'222222')
insert into #b(lines) values ('aaaaa');
update b
set lines=LEFT(cast(a.lines as varchar(max)),CHARINDEX(char(13),cast(a.lines as varchar(max)),1)-1)+cast(b.lines as varchar(max))
from #a a
join #b b on a.id=b.id;
select * from #b;
I suggest also updating your TEXT data types to varchar(max), if possible. varchar(max) is much more robust.
Yes, do a substring or left till the first newline of the text field.
You could easily assign this via subquery for use in insert or update statements.
SELECT ( CASE WHEN CHARINDEX(CHAR(13), action_Item.Description) = 0
THEN action_Item.Description
ELSE SUBSTRING(action_Item.Description, 0,
CHARINDEX(CHAR(13), action_Item.Description))
END ) AS [Description] FROM action_Item
Where I am selecting the first line if "Description" field from a table called "action_Item"
DECLARE #crlf char(2);
SET #crlf = CHAR(13) + CHAR(10);
UPDATE table1
SET LEFT(table2.fieldWithCRLF, CHARINDEX(table2.fieldWithCRLF, #crlf, 0) - 1) + table1.fieldToPrepend
FROM table1
INNER JOIN table2
ON table1.sharedKey = table2.sharedKey
WHERE CHARINDEX(table2.fieldWithCRLF, #crlf, 0) > 0

Resources