I have a table as shown below
Is it possible to insert the above table data into a table in separate rows?
I tried using split function on each column and stored each column result on a temp table. I have no clue how to insert into new table combining all these rows and columns as per the id. Any help or suggestion would help.
Try this answer. Hope this helps you.
DECLARE #Table TABLE(ID INT, NAME VARCHAR(10),TITLE VARCHAR(10))
INSERT INTO #Table VALUES (1,';a;b;c',';12;13;14')
DECLARE #ID INT=1
SELECT #ID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN1 INTO #T1 FROM dbo.split((SELECT NAME FROM #Table WHERE id=#ID),';')
SELECT #ID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN2 INTO #T2 FROM dbo.split((SELECT TITLE FROM #Table WHERE id=#ID),';')
SELECT T1.ID,T1.Items NAME,T2.Items TITLE
FROM #T1 T1 INNER JOIN #T2 T2 ON T1.RN1=T2.RN2
DROP TABLE #T1
DROP TABLE #T2
If you want all the values, you just try the looping method like WHILE.
DECLARE #Table TABLE(ID INT, NAME VARCHAR(10),TITLE VARCHAR(10))
INSERT INTO #Table VALUES (1,';a;b;c',';12;13;14'),(2,';c;f;u',';67;56;34'),(3,';l;k;m',';90;70;60')
DECLARE #MinID INT,#MaxID INT
SELECT #MinID=MIN(ID),#MaxID=MAX(ID) FROM #Table
CREATE TABLE #T1(ID INT,Items VARCHAR(10),RN1 INT)
CREATE TABLE #T2(ID INT,Items VARCHAR(10),RN2 INT)
WHILE #MinID<=#MaxID
BEGIN
INSERT INTO #T1
SELECT #MinID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN1
FROM dbo.split((SELECT NAME FROM #Table WHERE id=#MinID),';')
INSERT INTO #T2
SELECT #MinID ID,Items,ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) RN2
FROM dbo.split((SELECT TITLE FROM #Table WHERE id=#MinID),';')
SET #MinID=#MinID+1
END
SELECT T1.ID,T1.Items NAME,T2.Items TITLE
FROM #T1 T1 INNER JOIN #T2 T2 ON T1.ID=T2.ID AND T1.RN1=T2.RN2
DROP TABLE #T1
DROP TABLE #T2
This will produce the result, what you exactly want:
ID NAME TITLE
----------- ---------- ----------
1 a 12
1 b 13
1 c 14
2 c 67
2 f 56
2 u 34
3 l 90
3 k 70
3 m 60
Here is the split function, I used to split the Strings:
CREATE FUNCTION [dbo].[Split]
(#String VARCHAR (max), #Delimiter CHAR (1))
RETURNS
#temptable TABLE (
[items] VARCHAR (max) COLLATE SQL_Latin1_General_CP1_CI_AS NULL)
AS
begin
declare #idx int
declare #slice varchar(max)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
Here is another method of CTE with help of XML node
There will no need to create any function.
WITH cte AS (
SELECT ID,
split.a.value('.', 'NVARCHAR(MAX)') [name],
ROW_NUMBER() OVER(ORDER BY ( SELECT 1)) RN
FROM
(
SELECT ID,
CAST('<A>'+REPLACE(name, ';', '</A><A>')+'</A>' AS XML) AS [name]
FROM <table_name>
) a
CROSS APPLY name.nodes('/A') AS split(a)),
CTE1 AS (
SELECT ID,
split.a.value('.', 'NVARCHAR(MAX)') [title],
ROW_NUMBER() OVER(ORDER BY ( SELECT 1 )) RN
FROM
(
SELECT ID,
CAST('<A>'+REPLACE(title, ';', '</A><A>')+'</A>' AS XML) AS [title]
FROM <table_name>
) aa
CROSS APPLY title.nodes('/A') AS split(a))
SELECT C.ID, C.name, C1.title FROM CTE C
JOIN CTE1 C1 ON C1.RN = C.RN
WHERE C.name != '' AND C1.title != '';
Result :
ID name title
1 a 12
1 b 13
1 s 45
2 c 67
2 f 56
2 u 34
3 l 90
3 k 70
3 m 60
Try below way .. this will save time and memory also!
This T-SQL block has dependency on dbo.SplitString function ..
T1 is my Source table T2 is my Destination table
DECLARE #c_s AS CURSOR;
DECLARE #id INT;
DECLARE #name VARCHAR(1000);
DECLARE #value VARCHAR(1000);
SET #c_s = CURSOR FOR SELECT * FROM T1;
OPEN #c_s;
FETCH #c_s INTO #id, #name, #value
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO T2
SELECT #id
, a.Value NAME
, b.value value
FROM dbo.SplitString(#name, ';') a
INNER JOIN dbo.SplitString(#value, ';') b
ON a.OrdinalPosition = b.OrdinalPosition
FETCH NEXT FROM #c_s INTO #id, #name, #value
END
here is the dbo.SplitString
CREATE FUNCTION [dbo].[SplitString](#givenString VARCHAR(8000) , #separator VARCHAR(100))
RETURNS TABLE AS
RETURN (
WITH data([start], [end]) AS (
SELECT 0 AS [start]
, CHARINDEX(#separator, #givenString) AS [end]
UNION ALL
SELECT [end] + 1
, CHARINDEX(#separator, #givenString, [end] + 1)
FROM data
WHERE [end] > 0
)
SELECT ROW_NUMBER() OVER (
ORDER BY OrdinalPosition
) OrdinalPosition
, RTRIM(LTRIM(Value)) Value
FROM (
SELECT ROW_NUMBER() OVER (
ORDER BY [start]
) OrdinalPosition
, SUBSTRING(#givenString, [start], COALESCE(NULLIF([end], 0), len(#givenString) + 1) - [start]) Value
FROM data
) r
WHERE RTRIM(Value) <> ''
AND Value IS NOT NULL
)
You can achieve this by writing a table-valued function that will split the strings according to your requirements. Once you have created this object, then you can use the T-SQL in second code snippet to get your final required table.
The definition of this table-valued function is as given below. Just copy and paste this into SSMS and run it against your database.
Split a string function
-- =============================================
-- Author: B Vidhya
-- Create date: Nov 7, 2017
-- Description: Splits a string and returns a table
-- =============================================
CREATE FUNCTION [dbo].[SplitAString]
(
#string NVARCHAR(MAX),
#delimiter CHAR(1)
)
RETURNS #splitTable TABLE (
ItemNumber INT IDENTITY(1,1),
Item NVARCHAR(1000)
)
AS
BEGIN
DECLARE #startIndex INT,#endIndex INT
SET #startIndex = 1
IF SUBSTRING(#string, LEN(#string) - 1, LEN(#string)) <> #delimiter
BEGIN
SET #string = #string + #delimiter
END
WHILE CHARINDEX(#delimiter, #string) > 0
BEGIN
SET #endIndex = CHARINDEX(#delimiter, #string)
INSERT INTO #splitTable(Item)
SELECT SUBSTRING(#string, #startIndex, #endIndex - 1)
SET #string = SUBSTRING(#string, #endIndex + 1, LEN(#string))
END
RETURN
END
GO
In T-SQL below, I have called the original table StackOverflowTable1 and you can replace this table name with your actual table name. Also, I am inserting final rows into a table variable. If you wanted to insert into your custom table, then you could use perform an INSERT into your table after the END of WHILE loop.
T-SQL to get your final table
DECLARE #myTable TABLE
(Id INT,
Name VARCHAR(5000),
Title VARCHAR(5000)
);
DECLARE #lastId INT= 0, #id INT, #name VARCHAR(5000), #title VARCHAR(5000);
--for each record in table perform splitting and insertion in new table
WHILE EXISTS
(
SELECT 1
FROM StackOverFlowTable1 soft
WHERE Id > #lastId
)
BEGIN
SELECT TOP (1) #id = Id,
#name = Name,
#title = Title
FROM StackOverFlowTable1 soft
WHERE Id > #lastId
ORDER BY Id;
SET #lastId = #id;
INSERT INTO #myTable
(Id,
Name,
Title
)
SELECT #id,
ss1.Item,
ss2.Item
FROM dbo.SplitString(#name, ';') ss1
INNER JOIN dbo.SplitString(#title, ';') ss2 ON ss1.ItemNumber = ss2.ItemNumber
WHERE ss1.Item <> ''
AND ss2.Item <> '';
END;
SELECT * FROM #myTable;
I do not agree with Dinesh script because it is based on RBAR.
I have very similar Split function with also return row_number along with item.
so test my script along with other sample data.
DECLARE #Table TABLE(ID INT, NAME VARCHAR(10),TITLE VARCHAR(10))
INSERT INTO #Table VALUES (1,',a,b,c',',12,13,14')
SELECT id
,t.RowVal
,a.RowVal
FROM (
SELECT t.id
,a.RowNum
,a.RowVal
,t.TITLE
FROM #Table t
CROSS APPLY (
SELECT *
FROM dbo.FN_SPLIT_VALUE(t.NAME)
) a
) t
CROSS APPLY (
SELECT *
FROM dbo.FN_SPLIT_VALUE(t.TITLE)
WHERE t.RowNum = RowNum
) a
WHERE t.RowVal <> ''
Related
I have column called MemberID which contains list of memberid like
( 1|2|3|12|23|12 ) all Id separated by | I want can i pass one ID so get all row which contain provided ID.
Please try this
Supported Starting from SQL 2016
CREATE TABLE #Test
(
RowID INT IDENTITY(1,1),
EID INT,
Sampl VARCHAR(50)
)
INSERT INTO #Test VALUES (1,'1|2|3|4')
INSERT INTO #Test VALUES (2,'1|2|3|5')
INSERT INTO #Test VALUES (3,'1|2|3|4')
INSERT INTO #Test VALUES (4,'1|2|3|6')
GO
SELECT * FROM #Test
CROSS APPLY STRING_SPLIT ( Sampl , '|' )
WHERE Value IN ('4')
RowID EID Sampl value
----------- ----------- -------------------------------------------------- --------------------------------------------------
1 1 1|2|3|4 4
3 3 1|2|3|4 4
For lower versions of SQL you may need to create a function to do the process
CREATE FUNCTION dbo.SplitString(#String NVARCHAR(MAX), #Delimiter CHAR(1))
RETURNS #Results TABLE (Result NVARCHAR(MAX))
AS
BEGIN
DECLARE #Index INT
DECLARE #Data NVARCHAR(MAX)
SELECT #Index = 1
IF #String IS NULL RETURN
WHILE #Index != 0
BEGIN
SELECT #Index = CHARINDEX(#Delimiter, #String)
IF #Index != 0
SELECT #Data = LEFT(#String, #Index - 1)
ELSE
SELECT #Data = #String
INSERT INTO #Results(Result) VALUES (#Data)
SELECT #String = RIGHT(#String, LEN(#String) - #Index)
IF LEN(#String) = 0 BREAK
END
RETURN
END
SELECT * FROM #Test
CROSS APPLY Gemini.dbo.SplitString ( Sampl , '|' )
WHERE Result IN ('4')
haven't tested it but this should work:
declare #Id varchar(10)='1'
select * from content_membeparticipation where charindex('|' + #Id + '|','|' + memberId + '|' )>0
but I agree with the comments, if at all possible, the best option is normalise the tables
i'm trying to split this string "1,1_5,2_3,4" first on '_' and then split the sub string on ',' so it will be like "1,1_5,2_3,4" split-ed on '_' first so it will give this sub string "1,1" then this sub string split-ed on ',' and then insert the split-ed sub string "1 1" into temp table like this
INSERT INTO [dbo].[TEST] ([X],[Y])
VALUES (#X,#Y)
i did this with only split on one delimiter ',' like this
first i create a table Split function
ALTER FUNCTION [dbo].[Split]
(
-- Add the parameters for the function here
#RowData nvarchar(2000),
#SplitOn nvarchar(5)
)
RETURNS #RtnValue table
(
Id int identity(1,1),
Data nvarchar(100)
)
AS
BEGIN
-- Declare the return variable here
Declare #Cnt int
Set #cnt = 1
While(charindex(#SplitOn,#RowData) > 0)
Begin
Insert Into #RtnValue (data)
Select
Data = ltrim(RTRIM(SUBSTRING(#RowData,1,CHARINDEX(#SplitOn,#RowData)-1)))
set #RowData = SUBSTRING(#RowData,CHARINDEX(#SplitOn,#RowData)+1,len(#RowData))
set #Cnt = #Cnt + 1
End
insert into #RtnValue(data)
select Data = ltrim(RTRIM(#RowData))
RETURN
END
then i create this stored procedure
ALTER PROCEDURE [dbo].[uspTest]
-- Add the parameters for the stored procedure here
#StringOFXIDs nvarchar(2000)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #SOMETABLE TABLE(ID INT IDENTITY(1,1) UNIQUE, XID INT);
DECLARE #XCOUNT INT;
DECLARE #XCURRENT INT;
DECLARE #XCOUNTER INT = 1;
-- Insert statements for procedure here
INSERT INTO #SOMETABLE([XID])
SELECT [Data] FROM [dbo].[Split](#StringOFXIDs,',');
SELECT #XCOUNT = COUNT(1) FROM #SOMETABLE;
WHILE (#XCOUNTER <= #XCOUNT)
BEGIN
SELECT #XCOUNTER = [XID]
FROM #SOMETABLE
WHERE [ID] = #XCOUNTER
INSERT INTO [dbo].[TEST] ([X])
VALUES (#XCOUNTER)
SELECT #XCOUNTER +=1;
END
END
--EXEC [dbo].[uspTest] '1,2,3,4'
and then execute this stored procedure and every thing work but i can't figure out how to split the string on two characters or delimiters and then inserted to temp table thanks for any help in advance.
You can use XML functionality to split into rows, then split into columns:
DECLARE #string VARCHAR(100) = '1,1_5,2_3,4'
;WITH cte AS (SELECT RTRIM(LTRIM(Split.a.value('.', 'VARCHAR(100)'))) AS Txt
,ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS RN
FROM (SELECT CAST ('<M>' + REPLACE(#string, '_', '</M><M>') + '</M>' AS XML) AS DATA
) AS A
CROSS APPLY Data.nodes ('/M') AS Split(a)
)
,cte2 AS (SELECT CONVERT(XML,'<String><Section>'+ REPLACE(REPLACE(Txt ,'|',','),',', '</Section><Section>') + '</Section></String>') AS Txt
,RN
FROM cte
)
SELECT Txt.value('/String[1]/Section[1]','varchar(100)') AS Col1
,Txt.value('/String[1]/Section[2]','varchar(100)') AS Col2
FROM cte2
ORDER BY RN
Outputs:
Col1 Col2
---------------
1 1
5 2
3 4
Currently I'm working on a project where we just added the ability to have multiple values stored within a field. Previously, it stored a single value, but now it contains multiple. Below is an example of what I'm talking about.
Ex. A person's name is passed in (John Smith). Now the user can pass in multiple people's names, delimited by a ';' (John Smith;John Doe;Jane Tandy).
My issue is that we have a data source for a drop down that currently feeds off this field. Below is the SQL for it.
Select Distinct Content
From LIB_DocumentAttribute A
inner join LIB_PublicDocument B on A.PublicDocumentId = B.PublicDocumentId
Where AttributeId = (Select AttributeId From LIB_Attribute Where FieldName='Author')
and B.Status = 'Approved'
This somewhat works now. Content is the field that contains the multiple names. Now when the drop down is loaded, it pulls back the concatenated string of names (the longer one from above). I want to break it apart for the data source. So far, my only ideas is to split out the data based on the ';'. However, I need to take that split out data and apply it to the table that returns the rest of the data. Below is where I have gotten to but have become stuck on.
CREATE TABLE #Authors
(
Content varchar(MAX)
)
CREATE TABLE #Temp1
(
Content varchar(MAX)
)
CREATE TABLE #Temp2
(
Content varchar(MAX)
)
CREATE TABLE #Temp3
(
Content varchar(MAX)
)
--Load Authors table to store all Authors
INSERT INTO #Authors
Select Distinct Content
From LIB_DocumentAttribute A
inner join LIB_PublicDocument B on A.PublicDocumentId = B.PublicDocumentId
Where AttributeId = (Select AttributeId From LIB_Attribute Where FieldName='Author')
and B.Status = 'Approved'
--Take multiple Authors separated by '; ' and add to Temp1
INSERT INTO #Temp1
SELECT REPLACE(Content, '; ', ';') FROM #Authors WHERE Content LIKE '%; %'
--Remove multiple Authors separated by '; ' from Authors table
DELETE FROM #Authors
WHERE Content LIKE '%; %'
--Take multiple Authors separated by ';' and add to Temp2
INSERT INTO #Temp2
SELECT Content FROM #Authors WHERE Content LIKE '%;%'
--Remove multiple Authors separated by ';' from Authors table
DELETE FROM #Authors
WHERE Content LIKE '%;%'
--Somewhow split data and merge back together
DROP TABLE #Authors
DROP TABLE #Temp1
DROP TABLE #Temp2
DROP TABLE #Temp3
Edit:
So in the end, I came up with a solution that utilized some of the pieces that Kumar suggested. I created a function for splitting the string as he suggested and added some personal changes to make it work. Mind you this is in a table return function, with the table called #Authors, and it has one column called Content.
BEGIN
DECLARE #Temp TABLE
(
Content varchar(MAX)
)
--Load Authors table to store all Authors
INSERT INTO #Authors
Select Distinct Content
From LIB_DocumentAttribute A
inner join LIB_PublicDocument B on A.PublicDocumentId = B.PublicDocumentId
Where AttributeId = (Select AttributeId From LIB_Attribute Where FieldName='Author')
--Take multiple Authors separated by ', ' and add to Temp
INSERT INTO #Temp
SELECT REPLACE(Content, ', ', ',')
FROM #Authors;
--Remove multiple Authors separated by ', ' from Authors table
DELETE FROM #Authors
WHERE Content LIKE '%,%';
--Readd multiple Authors now separated into Authors table
INSERT INTO #Authors
SELECT s.Content
FROM #Temp
OUTER APPLY SplitString(Content,',') AS s
WHERE s.Content <> (SELECT TOP 1 a.Content FROM #Authors a WHERE s.Content = a.Content)
RETURN
END
Check the demo in fiddler link http://sqlfiddle.com/#!3/390f8/11
Create table test(name varchar(1000));
Insert into test values('AAA BBB; CCC DDD; eee fff');
CREATE FUNCTION SplitString
(
#Input NVARCHAR(MAX),
#Character CHAR(1)
)
RETURNS #Output TABLE (
Item NVARCHAR(1000)
)
AS
BEGIN
DECLARE #StartIndex INT, #EndIndex INT
SET #StartIndex = 1
IF SUBSTRING(#Input, LEN(#Input) - 1, LEN(#Input)) <> #Character
BEGIN
SET #Input = #Input + #Character
END
WHILE CHARINDEX(#Character, #Input) > 0
BEGIN
SET #EndIndex = CHARINDEX(#Character, #Input)
INSERT INTO #Output(Item)
SELECT SUBSTRING(#Input, #StartIndex, #EndIndex - 1)
SET #Input = SUBSTRING(#Input, #EndIndex + 1, LEN(#Input))
END
RETURN
END
Declare #name varchar(100)
Declare #table as table(name varchar(1000))
Declare cur cursor for
Select name from test
Open cur
fetch next from cur into #name
while (##FETCH_STATUS = 0)
begin
Insert into #table
Select * from dbo.splitstring(#name,';')
fetch next from cur into #name
end
close cur
deallocate cur
Select * from #table
this might work.
drop table authors
GO
create table Authors (Author_ID int identity (1,1),name varchar (255), category varchar(255))
GO
insert into authors
(name,category)
select
'jane doe','nonfiction'
union
select
'Jules Verne; Mark Twain; O. Henry', 'fiction'
union
select
'John Smith; John Doe', 'nonfiction'
GO
DECLARE #table TABLE (
names VARCHAR(255)
,id INT
)
DECLARE #category VARCHAR(255)
SET #category = 'nonfiction'
DECLARE #Author_ID INT
DECLARE AuthorLookup CURSOR
FOR
SELECT Author_ID
FROM authors
WHERE category = #category
OPEN AuthorLookup
FETCH NEXT
FROM AuthorLookup
INTO #Author_ID
WHILE ##FETCH_STATUS = 0
BEGIN
IF (
SELECT CHARINDEX(';', NAME, 0)
FROM authors
WHERE Author_ID = #Author_ID
) = 0
BEGIN
INSERT INTO #table
SELECT NAME
,Author_ID
FROM authors
WHERE Author_ID = #Author_ID
END
ELSE
BEGIN
DECLARE #value VARCHAR(255)
SELECT #value = NAME
FROM authors
WHERE Author_ID = #Author_ID
WHILE len(#value) > 0
BEGIN
INSERT INTO #table
SELECT substring(#value, 0, CHARINDEX(';', #value, 0))
,#Author_ID
SELECT #value = replace(#value, substring(#value, 0, CHARINDEX(';', #value, 0) + 2), '')
IF CHARINDEX(';', #value, 0) = 0
BEGIN
INSERT INTO #table
SELECT #value
,#Author_ID
SET #value = ''
END
END
END
FETCH NEXT
FROM AuthorLookup
INTO #Author_ID
END
CLOSE AuthorLookup
DEALLOCATE AuthorLookup
SELECT *
FROM #table
You can create function to split your data
create function SplitString
(
#data nvarchar(max),
#sep char(1)
)
returns #result table (data nvarchar(max))
as
begin
declare #i int
while 1 = 1
begin
select #i = charindex(#sep, #data)
if #i = 0
begin
insert into #result
select #data
break
end
insert into #result
select rtrim(left(#data, #i - 1))
select #data = ltrim(right(#data, len(#data) - #i))
end
return
end
and use it like this:
select s.data
from test as t
outer apply SplitString(t.data,';') as s
If you're sure that you don't have special characters inside your data, you can also consider trick with xml:
;with cte as (
select
cast('<s>' + replace(data, ';', '</s><s>') + '</s>' as xml) as data
from test
)
select
t.c.value('.', 'nvarchar(max)') as data
from cte
outer apply data.nodes('s') as t(c)
sql fiddle demo
Need help on how to improve my SQL script for better performance. dbo.Products table has a million rows. I'm hesitant to rewrite it using dynamic SQL. Thanks!
DECLARE
#Brand varchar(MAX) = 'Brand 1, Brand 2, Brand 3',
#ItemCategory varchar(MAX) = 'IC1, IC2, IC3, IC4, IC5'
--will return all records if params where set to #Brand = NULL, #ItemCategory = NULL
SELECT
[Brand],
SUM([Amount]) AS [Amount]
FROM dbo.Products (NOLOCK)
LEFT JOIN [dbo].[Split](#Brand, ',') FilterBrand ON Brand = [FilterBrand].[Items]
LEFT JOIN [dbo].[Split](#ItemCategory, ',') FilterItemCategory ON ItemCategory = [FilterItemCategory].[Items]
WHERE
(#Brand IS NULL OR (#Brand IS NOT NULL AND [FilterBrand].[Items] IS NOT NULL)) AND
(#ItemCategory IS NULL OR (#ItemCategory IS NOT NULL AND [FilterItemCategory].[Items] IS NOT NULL))
GROUP BY
[Brand]
Below is the split table-valued function that I found on the web:
CREATE function [dbo].[Split]
(
#String varchar(8000),
#Delimiter char(1)
)
RETURNS #Results TABLE (Items varchar(4000))
AS
BEGIN
IF (#String IS NULL OR #String = '') RETURN
DECLARE #i int, #j int
SELECT #i = 1
WHILE #i <= LEN(#String)
BEGIN
SELECT #j = CHARINDEX(#Delimiter, #String, #i)
IF #j = 0
BEGIN
SELECT #j = len(#String) + 1
END
INSERT #Results SELECT RTRIM(SUBSTRING(#String, #i, #j - #i))
SELECT #i = #j + LEN(#Delimiter)
END
RETURN
END
Following solution are with out using functions
Declare #IDs Varchar(100)
SET #IDs = '2,4,6'
Select IsNull(STUFF((Select ', '+ CAST([Name] As Varchar(100)) From [TableName]
Where CharIndex(','+Convert(Varchar,[ID])+',', ','+#IDs+',')> 0
For XML Path('')),1,1,''),'') As [ColumnName]
Here is the function I use. I also have another that wraps this to return numeric values which I find helpful as well.
Edit: Sorry, as for how to improve the performance of the query, I usually split the values into table variables and perform my joins to that but that probably won't change your performance, just your readability. The only thing I can see in terms of performance is your double checking whether your joins produce anything. You really can't get much better performance with two conditional left joins on two tables. It basically boils down to indexes at that point.
(#Brand IS NULL OR [FilterBrand].[Items] IS NOT NULL)
Function:
ALTER FUNCTION [dbo].[fn_SplitDelimittedList]
(
#DelimittedList varchar(8000),
#Delimitter varchar(20)
)
RETURNS
#List TABLE
(
Item varchar(100)
)
AS
BEGIN
DECLARE #DelimitterLength INT
SET #DelimitterLength = LEN(#Delimitter)
-- Tack on another delimitter so we get the last item properly
set #DelimittedList = #DelimittedList + #Delimitter
declare #Position int
declare #Item varchar(500)
set #Position = patindex('%' + #Delimitter + '%' , #DelimittedList)
while (#Position <> 0)
begin
set #Position = #Position - 1
set #Item = LTRIM(RTRIM(left(#DelimittedList, #Position)))
INSERT INTO #List (Item) VALUES (#Item)
set #DelimittedList = stuff(#DelimittedList, 1, #Position + #DelimitterLength, '')
set #Position = patindex('%' + #Delimitter + '%' , #DelimittedList)
end
RETURN
END
Hey just try the split function I have created without using any while loops here.And just use this in place of your split function and use col to match in LEFT join.
ALTER function dbo.SplitString(#inputStr varchar(1000),#del varchar(5))
RETURNS #table TABLE(col varchar(100))
As
BEGIN
DECLARE #t table(col1 varchar(100))
INSERT INTO #t
select #inputStr
if CHARINDEX(#del,#inputStr,1) > 0
BEGIN
;WITH CTE as(select ROW_NUMBER() over (order by (select 0)) as id,* from #t)
,CTE1 as (
select id,ltrim(rtrim(LEFT(col1,CHARINDEX(#del,col1,1)-1))) as col,RIGHT(col1,LEN(col1)-CHARINDEX(#del,col1,1)) as rem from CTE
union all
select c.id,ltrim(rtrim(LEFT(rem,CHARINDEX(#del,rem,1)-1))) as col,RIGHT(rem,LEN(rem)-CHARINDEX(#del,rem,1))
from CTE1 c
where CHARINDEX(#del,rem,1)>0
)
INSERT INTO #table
select col from CTE1
union all
select rem from CTE1 where CHARINDEX(#del,rem,1)=0
END
ELSE
BEGIN
INSERT INTO #table
select col1 from #t
END
RETURN
END
DECLARE #Brand varchar(MAX) = 'Brand 1,Brand 2,Brand 3',
#ItemCategory varchar(MAX) = ' IC1 A ,IC2 B , IC3 C, IC4 D' --'IC1, IC2, IC3, IC4, IC5'
select * from dbo.SplitString(#ItemCategory,',')
I've got some data in the following format:
-1,-1,-1,-1,701,-1,-1,-1,-1,-1,304,390,403,435,438,439,442,455
I need to insert it into a temp table like this:
CREATE TABLE #TEMP
(
Node int
)
So that I can use it in a comparison with data in another table.
The data above represents separate rows of the "Node" column.
Is there an easy way to insert this data, all in one command?
Also, the data will actually being coming in as seen, as a string... so I need to be able to just concat it into the SQL query string. I can obviously modify it first if needed.
Try something like
CREATE TABLE #TEMP
(
Node int
)
DECLARE #textXML XML
DECLARE #data NVARCHAR(MAX),
#delimiter NVARCHAR(5)
SELECT #data = '-1,-1,-1,-1,701,-1,-1,-1,-1,-1,304,390,403,435,438,439,442,455 ',
#delimiter = ','
SELECT #textXML = CAST('<d>' + REPLACE(#data, #delimiter, '</d><d>') + '</d>' AS XML)
INSERT INTO #TEMP
SELECT T.split.value('.', 'nvarchar(max)') AS data
FROM #textXML.nodes('/d') T(split)
SELECT * FROM #TEMP
DROP TABLE #TEMP
You can create a query dynamically like this:
declare #sql varchar(1000)
set #sql = 'insert into #TEMP select ' + replace(#values, ',', ' union all select ')
exec #sql
As always when creating queries dynamically, you have to be careful so that you only use trusted data.
I would create a function that would return a table variable and then join that function into the select
Use:
select * from myTable a
inner join dbo.buildTableFromCSV('1,2,3') on a.id = b.theData
Here is my function for doing this
CREATE FUNCTION [dbo].[buildTableFromCSV] ( #csvString varchar(8000) ) RETURNS #myTable TABLE (ID int identity (1,1), theData varchar(100))
AS BEGIN
DECLARE #startPos Int -- position to chop next block of chars from
DECLARE #currentPos Int -- position to current character we're examining
DECLARE #strLen Int
DECLARE #c char(1) -- current subString
-- variable initalization
-- -------------------------------------------------------------------------------------------------------------------------------------------------
SELECT #csvString = #csvString + ','
SELECT #startPos = 1
SELECT #currentPos = 1
SELECT #strLen = Len(#csvString)
-- loop over string and build temp table
-- -------------------------------------------------------------------------------------------------------------------------------------------------
WHILE #currentPos <= #strLen BEGIN
SET #c = SUBSTRING(#csvString, #currentPos, 1 )
IF ( #c = ',' ) BEGIN
IF ( #currentPos - #startPos > 0 ) BEGIN
INSERT
INTO #myTable ( theData )
VALUES ( CAST( SUBSTRING ( #csvString, #startPos, #currentPos - #startPos) AS varchar ) )
END
ELSE
begin
INSERT
INTO #myTable ( theData )
VALUES ( null )
end
SELECT #startPos = #currentPos + 1
END
SET #currentPos = #currentPos + 1
END
delete from #myTable where theData is null
return
END