Split Comma seperated value other than split function - sql

FOR EACH LINE THERE IS AN VALID Patient_id ASSOCIATED WITH IT in Parent table
i try to validate whether correct Patient_id linked with line.
problem:
In child table Patient_id is comma seperated.
if i am using function to split value, the query is not completing even after
15 hours.
can anyone suggest some other alternate, without using finction Please if possible
DECLARE #PARENT_TABLE TABLE
(
Line VARCHAR (50),
Patient_id VARCHAR (50)
)
INSERT #PARENT_TABLE
SELECT 'ABSRB', 'E001' UNION ALL
SELECT 'ABSRB', 'E067' UNION ALL
SELECT 'ABSRB', 'E068'
DECLARE #CHILD_TABLE TABLE
(
LINE VARCHAR (50),
SKU VARCHAR (50),
Patient_id VARCHAR (50)
)
INSERT #CHILD_TABLE
SELECT 'ABSRB', 'PRS317580', NULL UNION ALL
SELECT 'ABSRB', 'RANRS7371', 'E001' UNION ALL
SELECT 'ABSRB', 'KYBKG54327', 'E067' UNION ALL
SELECT 'ABSRB', 'KYB344615', 'V048, V055, E068' UNION ALL
SELECT 'ABSRB', 'ZZZZ15245', 'S128, V048, V055'
SELECT * FROM #CHILD_TABLE
can anyone suggest some other alternate, without using finction Please if possible
i have not included split function code here.
Code:
CREATE FUNCTION [dbo].[fnSplitString](
#string NVARCHAR(MAX),
#delimiter CHAR(1),
#keylist nvarchar(50))
RETURNS TABLE
AS
RETURN(
WITH CTE AS(
SELECT 1 as n
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
UNION ALL
SELECT 1
),CTE2 AS(
SELECT ROW_NUMBER() OVER(ORDER BY c.n) as n
FROM CTE c
CROSS JOIN CTE c2
CROSS JOIN CTE c3
CROSS JOIN CTE c4
CROSS JOIN CTE c5
CROSS JOIN CTE c6
)
SELECT TOP(LEN(COALESCE(#string,'')))
splitdata = ltrim(rtrim(
SUBSTRING(#string, n, CHARINDEX(#delimiter, #string + #delimiter, n)-n)
)),
keyname= #keylist
FROM CTE2
WHERE n<= LEN(#string)
AND SUBSTRING(#Delimiter + #string , n, LEN(#Delimiter)) = #Delimiter
)
DECLARE #SplittedTable TABLE
(
splitted VARCHAR (50),
keys VARCHAR (50)
)
INSERT INTO #SplittedTable(splitted,keys)
SELECT ss.splitdata, ss.keyname
FROM #CHILD_TABLE ct
CROSS APPLY dbo.fnSplitString(ct.Patient_id,',',ct.Patient_id) ss
SELECT * FROM #CHILD_TABLE ct WHERE ct.Patient_id IN (
SELECT DISTINCT st.keys
FROM #SplittedTable AS st
WHERE st.splitted NOT IN (SELECT Patient_id FROM #Parent_table m ));
SELECT DISTINCT ct.*
FROM #CHILD_TABLE ct
CROSS APPLY dbo.fnSplitString(ct.Patient_id,',',ct.Patient_id) ss
WHERE ss.splitdata NOT IN (SELECT Patient_id FROM #Parent_table)
The below code took more than 15 hours to execute. table size: 20GB
CODE:
--selecting by keys and finding them into master
SELECT * FROM #child ct WHERE ct.patientid IN (
SELECT DISTINCT st.keys
FROM #SplittedTable AS st
WHERE st.splitted NOT IN (SELECT patientid FROM #parent m ));
--Use CROSS APPLY to get this done for all rows.
SELECT DISTINCT ct.*
FROM #child ct
CROSS APPLY dbo.fnSplitString(ct.patientid,',',ct.patientid) ss
WHERE ss.splitdata NOT IN (SELECT patientid FROM #parent)

Related

T-SQL query to check if given set of data is available are not

I have a SQL Server table and my java application is sending a list of descriptions.
Now I need to verify if all the description are available in the table or not, if all the description is not available in the table then an error has to be raised with the missing description.
For example: Java application is sending message as 'tree', 'flower', 'plant'.
In SQL Server, there is a column description - I need to check if 'tree', 'flower', 'plant' are available or not.
If anyone is unavailable like 'plant' is not there in the table then raise an error that 'plant' is unavailable.
Could you please help me with this?
You can try to left join the table to the descriptions. If there is no match in the table, the columns from that table remain NULL, so you can filter only for NULLs. Like that you have a list of descriptions, that don't exist.
SELECT i.description
FROM (SELECT 'tree'
UNION ALL
SELECT 'flower'
UNION ALL
SELECT 'plant') i
LEFT JOIN elbat t
ON t.description = i.description
WHERE t.description IS NULL;
u meant somethin like this?
if not exists (select 1 from table1 where description='plant')
begin
RAISERROR('your custom error',16,1)
end
ALTER FUNCTION [dbo].[SplitToItems] (
#pString NVARCHAR(3999), --!! DO NOT USE MAX DATA-TYPES
#pDelimiter CHAR(1)
)
RETURNS #Items TABLE (ItemNumber INT, Item NVARCHAR(100))
BEGIN
IF Replace(#pString, '''', '') = ''
SET #pString = '';
WITH E1 (N)
AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), E2 (N)
AS (
SELECT 1
FROM E1 a, E1 b
), E4 (N)
AS (
SELECT 1
FROM E2 a, E2 b
), cteTally (N)
AS (
SELECT TOP (ISNULL(DATALENGTH(#pString), 0)) ROW_NUMBER() OVER (
ORDER BY (
SELECT NULL
)
)
FROM E4
), cteStart (N1)
AS (
SELECT 1
UNION ALL
SELECT t.N + 1
FROM cteTally t
WHERE SUBSTRING(#pString, t.N, 1) = #pDelimiter
), cteLen (N1, L1)
AS (
SELECT s.N1, ISNULL(NULLIF(CHARINDEX(#pDelimiter, #pString, s.N1), 0) - s.N1, 8000)
FROM cteStart s
)
INSERT INTO #Items
SELECT ItemNumber = ROW_NUMBER() OVER (
ORDER BY l.N1
), Item = SUBSTRING(SUBSTRING(#pString, l.N1, l.L1), 1, 100)
FROM cteLen l
RETURN
END
SELECT *
FROM dbo.SplitToItems('''tree'',''flower'',''plant''', ',')

Missing Value Search in string seperated by comma

Please help me if it is possible in sql.
Hello, may anyone please share their expertise, i am not very sure whether it is possible in SQL
SIZE OF TABLE: 5GB
I am trying to see invalid VALIDATION_ID present in a child table for a given PRODUCT_LINE
SO in below scenario 114 is not present for PRODUCT_LINE Passive in master table but present in child table.
DECLARE #CHILD TABLE
(
PRODUCT_LINE VARCHAR (50),
COMPONENT VARCHAR (50),
MODEL VARCHAR (50),
YEARS VARCHAR (50),
VALIDATION_ID VARCHAR (50)
)
INSERT #CHILD
SELECT 'PASSIVE','RESISTOR','CARBON','2005','V114' UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005','V098, E009, V034' UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005','V201' UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005','V201,V098,V114' UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005',null UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005','null,V098,E009' UNION ALL
SELECT 'PASSIVE','RESISTOR','CARBON','2005','null,V114' UNION ALL
SELECT * FROM #CHILD
DECLARE #PARENT TABLE
(
PRODUCT_LINE VARCHAR (50),
VALIDATION_ID VARCHAR (50)
)
INSERT #PARENT
SELECT 'PASSIVE','V098' UNION ALL
SELECT 'PASSIVE','E009' UNION ALL
SELECT 'PASSIVE','V201' UNION ALL
SELECT 'PASSIVE','V034'
EXPECTED OUTPUT
PRODUCT_LINE COMPONENT MODEL YEARS VALIDATION_ID INVALID_VALIDATION_ID
PASSIVE RESISTOR CARBON 2005 V114 V114
PASSIVE RESISTOR CARBON 2005 V201,V098,V114 V114
'PASSIVE','RESISTOR','CARBON','2005','null,V114',V114
Thanks ....
you will definately required a CSV splitter. There are many, just search for it. Here I am using Jeff's DelimitedSplit8K()
SELECT c.*, INVALID_VALIDATION_ID = v.Item
FROM #CHILD c
CROSS APPLY dbo.DelimitedSplit8K(c.VALIDATION_ID, ',') v
WHERE NOT EXISTS
(
SELECT *
FROM #PARENT p
WHERE p.PRODUCT_LINE = c.PRODUCT_LINE
and p.VALIDATION_ID = LTRIM(v.Item)
)
if there are more than one invalid ID, it will appear as multiple line. If you want the multiple invalid ID to appear as CSV, you will need additional query to concatenate it. Let me know if you required that
EDIT : Updated query to show invalid ID in CSV
; WITH
CTE AS
(
SELECT c.*, INVALID_VALIDATION_ID = v.Item,
RN = DENSE_RANK() OVER (ORDER BY PRODUCT_LINE, COMPONENT, MODEL, YEARS, VALIDATION_ID)
FROM #CHILD c
CROSS APPLY dbo.DelimitedSplit8K(c.VALIDATION_ID, ',') v
WHERE c.VALIDATION_ID IS NOT NULL -- edit [2]
AND NOT EXISTS
(
SELECT *
FROM #PARENT p
WHERE p.PRODUCT_LINE = c.PRODUCT_LINE
and p.VALIDATION_ID = LTRIM(v.Item)
)
)
SELECT DISTINCT
PRODUCT_LINE, COMPONENT, MODEL, YEARS, VALIDATION_ID,
INVALID_VALIDATION_ID = STUFF(csv, 1, 1, '')
FROM CTE c
CROSS APPLY
(
SELECT ',' + x.INVALID_VALIDATION_ID
FROM CTE x
WHERE x.RN = c.RN
ORDER BY INVALID_VALIDATION_ID
FOR XML PATH('')
) i (csv)
You Need to Try This...
SELECT *
FROM
(
SELECT c.*, INVALID_VALIDATION_ID = x.Value
FROM #CHILD c
CROSS APPLY dbo.Split(c.VALIDATION_ID, ',') x
WHERE NOT EXISTS
(
SELECT *
FROM #PARENT p
WHERE p.PRODUCT_LINE = c.PRODUCT_LINE
and p.VALIDATION_ID = x.Value
)
) AS Q
WHERE INVALID_VALIDATION_ID IS NOT NULL AND INVALID_VALIDATION_ID != 'null'
Here dbo.split function is ..
CREATE FUNCTION [dbo].[Split]
(#List varchar(8000),#SplitOn varchar(5))
RETURNS #RtnValue table
(Id int identity(1,1),Value nvarchar(100))
AS
BEGIN
Set #List = Replace(#List,'''','')
While (Charindex(#SplitOn,#List)>0)
Begin
Insert Into #RtnValue (value)
Select
Value = ltrim(rtrim(Substring(#List,1,Charindex(#SplitOn,#List)-1)))
Set #List = Substring(#List,Charindex(#SplitOn,#List)+len(#SplitOn),len(#List))
End
Insert Into #RtnValue (Value)
Select Value = ltrim(rtrim(#List))
Return
END
try this......

Total sum wrong value in Dynamic Pivot

I have complicated query which works pretty good (MS SQL 2012). But it makes a mistake when sum up same price items. It count them correctly but only takes price once instead of taking them as a count number. It only appears when same item has same price and from same country. Here is my query ;
CREATE TABLE #ITEMS(ID INT,NAME VARCHAR(30))
INSERT INTO #ITEMS
SELECT 1, 'laptop'
UNION ALL
SELECT 2, 'phone'
UNION ALL
SELECT 3, 'playstation'
UNION ALL
SELECT 4, 'MacBook'
CREATE TABLE #Country(ID INT,NAME VARCHAR(30))
INSERT INTO #Country
SELECT 1, 'England'
UNION ALL
SELECT 2, 'Sweden'
UNION ALL
SELECT 3, 'Russia'
UNION ALL
SELECT 4, 'Italy'
CREATE TABLE [#Pre-Request](Id INT, countryId INT, ItemId INT)
INSERT INTO [#Pre-Request]
SELECT 1,1,3
UNION ALL
SELECT 2,2,1
UNION ALL
SELECT 3,2,2
UNION ALL
SELECT 4,3,3
UNION ALL
SELECT 5,3,3
UNION ALL
SELECT 6,2,3
CREATE TABLE #Offers(Id INT, PRICE VARCHAR(50))
INSERT INTO #Offers
SELECT 18,'257$'
UNION ALL
SELECT 19,'151$'
UNION ALL
SELECT 20,'424$'
UNION ALL
SELECT 21,'433$'
UNION ALL
SELECT 22,'151$'
CREATE TABLE #Request(Id INT, preReqId INT, requestStatus INT,winOfferId INT)
INSERT INTO #Request
SELECT 44, 1, 3, 18
UNION ALL
SELECT 11, 2, 4, 21
UNION ALL
SELECT 53, 3, 4, 20
UNION ALL
SELECT 87, 4, 3, 22
UNION ALL
SELECT 43, 5, 3, 19
UNION ALL
SELECT 43, 6, 2, Null
;WITH CTE AS
(
SELECT DISTINCT I.NAME ITEMNAME,C.NAME COUNTRYNAME
,CAST(REPLACE(TAB.PRICE,'$','')AS INT)PRICE
,COUNT(CASE WHEN TAB.PRICE IS NOT NULL THEN I.NAME END) OVER(PARTITION BY C.NAME,I.NAME) CNTITEM
FROM [#Pre-Request] PR
LEFT JOIN #Items I ON PR.ITEMID=I.ID
LEFT JOIN #COUNTRY C ON PR.COUNTRYID = C.ID
OUTER APPLY
(
SELECT R.preReqId,R.winOfferId,O.PRICE
FROM #Request R
JOIN #Offers O ON R.winOfferId=O.Id
WHERE PR.ID=R.preReqId
)TAB
UNION
-- Used to select Item name and country that are not in Pre-request table and other tables
SELECT I.NAME ,C.NAME ,NULL,0
FROM #Items I
CROSS JOIN #COUNTRY C
)
,CTE2 AS
(
-- Find the sum for number of items
SELECT DISTINCT ISNULL(ITEMNAME,'TOTAL')ITEMNAME,ISNULL(COUNTRYNAME,'TOTAL')COUNTRYNAME,
SUM(PRICE)PRICE
FROM CTE
GROUP BY ITEMNAME,COUNTRYNAME
WITH CUBE
)
,CTE3 AS
(
-- Find the sum of PRICE
SELECT DISTINCT ISNULL(ITEMNAME,'TOTAL')ITEMNAME,ISNULL(COUNTRYNAME,'TOTAL')COUNTRYNAME--,CNTITEM
,SUM(CNTITEM)CNTITEM
FROM
(
SELECT DISTINCT ITEMNAME,COUNTRYNAME,CNTITEM
FROM CTE
)TAB
GROUP BY ITEMNAME,COUNTRYNAME
WITH CUBE
)
SELECT C2.*,C3.CNTITEM,
CAST(C3.CNTITEM AS VARCHAR(20))+'x'+' ' + CAST(C2.PRICE AS VARCHAR(20))+'$' NEWCOL
INTO #NEWTABLE
FROM CTE2 C2
JOIN CTE3 C3 ON C2.COUNTRYNAME=C3.COUNTRYNAME AND C2.ITEMNAME=C3.ITEMNAME
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + ITEMNAME + ']', '[' + ITEMNAME + ']')
FROM (SELECT DISTINCT ITEMNAME FROM #NEWTABLE WHERE ITEMNAME<>'TOTAL') PV
ORDER BY ITEMNAME
-- Since we need Total in last column, we append it at last
SELECT #cols += ',[Total]'
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT COUNTRYNAME,' + #cols + ' FROM
(
SELECT DISTINCT ITEMNAME,COUNTRYNAME,ISNULL(NEWCOL,''0x 0$'')NEWCOL
FROM #NEWTABLE
) x
PIVOT
(
MIN(NEWCOL)
FOR ITEMNAME IN (' + #cols + ')
) p
ORDER BY CASE WHEN (COUNTRYNAME=''Total'') THEN 1 ELSE 0 END,COUNTRYNAME'
EXEC SP_EXECUTESQL #query
and here is result ;
As you can see there are 2 "playstation" from "Russia" it takes correct count (2x) but only take 1 price "151$" (normally it must be 302$). How can I fix this without making major changes from query? Thank you.
I think this does what you want. The problem is in your first CTE where you do the item count and get a distinct on the ItemName, CountryName, and Price.
Instead of getting a distinct, do a group by as shown below and SUM the price.
SELECT I.NAME ITEMNAME, C.NAME COUNTRYNAME
,SUM(CAST(REPLACE(TAB.PRICE,'$','')AS INT))PRICE
,COUNT(CASE WHEN TAB.PRICE IS NOT NULL THEN 1 ELSE NULL END) CNTITEM
FROM [#Pre-Request] PR
LEFT JOIN #Items I ON PR.ITEMID=I.ID
LEFT JOIN #COUNTRY C ON PR.COUNTRYID = C.ID
OUTER APPLY
(
SELECT R.preReqId,R.winOfferId,O.PRICE
FROM #Request R
JOIN #Offers O ON R.winOfferId=O.Id
WHERE PR.ID=R.preReqId
)TAB
GROUP BY
I.NAME
,C.NAME
EDIT:
Here are the results I get:
Here's all of your code starting from the CTEs:
;WITH CTE AS
(
SELECT I.NAME ITEMNAME, C.NAME COUNTRYNAME
,SUM(CAST(REPLACE(TAB.PRICE,'$','')AS INT))PRICE
,COUNT(CASE WHEN TAB.PRICE IS NOT NULL THEN 1 ELSE NULL END) CNTITEM
FROM [#Pre-Request] PR
LEFT JOIN #Items I ON PR.ITEMID=I.ID
LEFT JOIN #COUNTRY C ON PR.COUNTRYID = C.ID
OUTER APPLY
(
SELECT R.preReqId,R.winOfferId,O.PRICE
FROM #Request R
JOIN #Offers O ON R.winOfferId=O.Id
WHERE PR.ID=R.preReqId
)TAB
GROUP BY
I.NAME
,C.NAME
UNION
-- Used to select Item name and country that are not in Pre-request table and other tables
SELECT I.NAME ,C.NAME ,NULL,0
FROM #Items I
CROSS JOIN #COUNTRY C
)
,CTE2 AS
(
-- Find the sum for number of items
SELECT DISTINCT ISNULL(ITEMNAME,'TOTAL')ITEMNAME,ISNULL(COUNTRYNAME,'TOTAL')COUNTRYNAME,
SUM(PRICE)PRICE
FROM CTE
GROUP BY ITEMNAME,COUNTRYNAME
WITH CUBE
)
,CTE3 AS
(
-- Find the sum of PRICE
SELECT DISTINCT ISNULL(ITEMNAME,'TOTAL')ITEMNAME,ISNULL(COUNTRYNAME,'TOTAL')COUNTRYNAME--,CNTITEM
,SUM(CNTITEM)CNTITEM
FROM
(
SELECT DISTINCT ITEMNAME,COUNTRYNAME,CNTITEM
FROM CTE
)TAB
GROUP BY ITEMNAME,COUNTRYNAME
WITH CUBE
)
SELECT C2.*,C3.CNTITEM,
CAST(C3.CNTITEM AS VARCHAR(20))+'x'+' ' + CAST(C2.PRICE AS VARCHAR(20))+'$' NEWCOL
INTO #NEWTABLE
FROM CTE2 C2
JOIN CTE3 C3 ON C2.COUNTRYNAME=C3.COUNTRYNAME AND C2.ITEMNAME=C3.ITEMNAME
DECLARE #cols NVARCHAR (MAX)
SELECT #cols = COALESCE (#cols + ',[' + ITEMNAME + ']', '[' + ITEMNAME + ']')
FROM (SELECT DISTINCT ITEMNAME FROM #NEWTABLE WHERE ITEMNAME<>'TOTAL') PV
ORDER BY ITEMNAME
-- Since we need Total in last column, we append it at last
SELECT #cols += ',[Total]'
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT COUNTRYNAME,' + #cols + ' FROM
(
SELECT DISTINCT ITEMNAME,COUNTRYNAME,ISNULL(NEWCOL,''0x 0$'')NEWCOL
FROM #NEWTABLE
) x
PIVOT
(
MIN(NEWCOL)
FOR ITEMNAME IN (' + #cols + ')
) p
ORDER BY CASE WHEN (COUNTRYNAME=''Total'') THEN 1 ELSE 0 END,COUNTRYNAME'
EXEC SP_EXECUTESQL #query

SQL replace from list

I'm trying to figure our how I can replace a string using data from another table
I have a table that looks like this:
Id Translation
1 Peter
2 Sandra
3 Olga
Now I want to select all and replace the translations using a list that looks like this:
Original New
e #
r ?
lg *%
So that the select list looks like this:
Id Translation
1 P#t#?
2 Sand?a
3 O*%a
So, for each translation, I need to have a REPLACE(Translation,Original,New).
Or in other words: I need to go through every "Translation" in my first list and make another loop in my replacement table to see what to replace
Bare in mind that the first list has 25'000 rows and the second has 50'000, so I can't just type it by hand :)
EDIT
Just to clarify:
The Original and New from my look up table can be both letters and words so the table can looks like this:
Original New
one two
three fifty
sun moon
To do this in one query, you need to use a recursive CTE. Something like:
with trans as (
select t.original, t.new, row_number() over (order by t.original) as seqnum,
count(*) over () as cnt
from translations
),
t as (
select tt.id, tt.string, replace(tt.string, trans.original, trans.new) as replaced,
seqnum + 1 as seqnum, cnt
from totranslate tt join
trans
on trans.id = 1
union all
select t.id, t.string, replace(t.string, trans.original, trans.new),
seqnum + 1 as seqnum, cnt
from t join
trans
on t.seqnum = trans.id
where t.seqnum <= t.cnt
)
select t.id, t.string, t.replaced
from t
where seqnum = cnt;
You can use a UDF:
CREATE FUNCTION [dbo].[Translate]
(
-- Add the parameters for the function here
#Str nvarchar(max)
)
RETURNS nvarchar(max)
AS
BEGIN
DECLARE #Result nvarchar(max) = #Str;
SELECT #Result = replace(#Result,Original,New) from dbo.Mappings order BY Pos;
RETURN #Result;
END
Here I assumed the table containing translations is called dbo.Mappings and beside the Original and New columns you need another column Pos int which will be used to determine the order in which the translations are applied (to address the problems mentioned by #Thorsten Kettner in comments)
Also with recursive cte:
DECLARE #translations TABLE
(
Id INT ,
Translation NVARCHAR(20)
)
INSERT INTO #translations
VALUES ( 1, 'Peter' ),
( 2, 'Sandra' ),
( 3, 'Olga' )
DECLARE #replacements TABLE
(
Original VARCHAR(2) ,
New VARCHAR(2)
)
INSERT INTO #replacements
VALUES ( 'e', '#' ),
( 'r', '?' ),
( 'lg', '*%' );
WITH cte1 AS (SELECT *, ROW_NUMBER() OVER (PARTITION BY id ORDER BY (SELECT 1)) rn
FROM #translations CROSS JOIN #replacements),
cte2 AS (SELECT Id, rn, REPLACE(Translation, Original, New) AS NTranslation
FROM cte1
WHERE rn = 1
UNION ALL
SELECT c2.Id, c2.rn + 1, REPLACE(c2.NTranslation, c1.Original, c1.New)
FROM cte1 c1
JOIN cte2 c2 ON c2.Id = c1.Id AND c2.rn + 1 = c1.rn)
SELECT * FROM cte2
WHERE rn = (SELECT COUNT(*) FROM #replacements)
ORDER BY Id
EDIT:
WITH cte1 AS (SELECT t.*, p.Id AS Old, p.Code, ROW_NUMBER() OVER (PARTITION BY t.id ORDER BY (SELECT 1)) rn
FROM translations t CROSS JOIN Property p),
cte2 AS (SELECT Id, rn, REPLACE(Trans, Old, Code) AS NTranslation
FROM cte1
WHERE rn = 1
UNION ALL
SELECT c2.Id, c2.rn + 1, REPLACE(c2.NTranslation, c1.Old, c1.Code)
FROM cte1 c1
JOIN cte2 c2 ON c2.Id = c1.Id AND c2.rn + 1 = c1.rn)
SELECT * FROM cte2
WHERE rn = (SELECT COUNT(*) FROM Property)
ORDER BY Id
Here is something I worked out that will allow you to replace multiple characters with one specified string.
[Split2] is stolen from https://blogs.msdn.microsoft.com/amitjet/2009/12/11/convert-comma-separated-string-to-table-4-different-approaches/
USE <Your Database>
GO
CREATE FUNCTION [dbo].[Split2]
(
#strString varchar(4000)
)
RETURNS #Result TABLE
(
RID INT IDENTITY(0,1) Primary Key
,Value varchar(4000)
)
AS
BEGIN
WITH StrCTE(start, stop) AS
(
SELECT 1, CHARINDEX(',' , #strString )
UNION ALL
SELECT stop + 1, CHARINDEX(',' ,#strString , stop + 1)
FROM StrCTE
WHERE stop > 0
)
INSERT INTO #Result
SELECT SUBSTRING(#strString , start, CASE WHEN stop > 0 THEN stop - start ELSE 4000 END) AS stringValue
FROM StrCTE
RETURN
END
GO
USE <Your Database>
GO
CREATE FUNCTION [dbo].[MultiReplace]
(
#MyString varchar(MAX)
,#RepChars varchar(4000)
,#NewChars varchar(4000)
)
RETURNS varchar(MAX)
AS
BEGIN
DECLARE #CurRow int = 0
DECLARE #MaxRow int
SELECT #MaxRow = MAX(RID)
FROM dbo.split2 ( #RepChars )
WHILE #CurRow <= #MaxRow
BEGIN
SELECT #MyString = REPLACE(#MyString,VALUE,#NewChars)
FROM dbo.split2 ( #RepChars )
WHERE RID = #CurRow
SET #CurRow = #CurRow + 1
END
RETURN (#MyString);
END
GO
In this example I replace each character with no space
SELECT [dbo].[MultiReplace]('6th month 2016-06 (test / requested)',',1st,2nd,3rd,4th,5th,6th,0,1,2,3,4,5,6,7,8,9,(,),/,-,+, ','')
Result:
monthtestrequested
I hope this is useful for you.

SQL splitting a word in separate characters

I need to change an application and the first thing I need is to change a field in a database table.
In this table I now have 1 to 6 single characters, i.e. 'abcdef'
I need to change this to '[a][b][c][d][e][f]'
[edit] It is meant to stay in the same field. So before field = 'abcdef' and after field = '[a][b][c][d][e][f]'.
What would be a good way to do this?
rg.
Eric
You can split string to separate characters using following function:
create function ftStringCharacters
(
#str varchar(100)
)
returns table as
return
with v1(N) as (
select 1 union all select 1 union all select 1 union all select 1 union all select 1
union all
select 1 union all select 1 union all select 1 union all select 1 union all select 1
),
v2(N) as (select 1 from v1 a, v1 b),
v3(N) as (select top (isnull(datalength(#str), 0)) row_number() over (order by ##spid) from v2)
select N, substring(#str, N, 1) as C
from v3
GO
And then apply it as:
update t
set t.FieldName = p.FieldModified
from TableName t
cross apply (
select (select quotename(s.C)
from ftStringCharacters(t.FieldName) s
order by s.N
for xml path(''), type).value('text()[1]', 'varchar(20)')
) p(FieldModified)
SQLFiddle sample
DECLARE #text NVARCHAR(50)
SET #text = 'abcdef'
DECLARE #texttable TABLE (value NVARCHAR(1))
WHILE (len(#text) > 0)
BEGIN
INSERT INTO #texttable
SELECT substring(#text, 1, 1)
SET #text = stuff(#text, 1, 1, '')
END
select * from #texttable
Without using a function:
declare #t table(C varchar(18))
insert #t values('abc'), ('1234'), (' 1234a')
;with CTE as
(
select C, '[' + substring(c, a.n, 1) + ']' v, rn from
(select 1 n union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6) a
cross apply
(select c, row_number() over (order by C) rn from #t group by c) b
where a.n <= len(C)
)
update t3
set C = t4.[value]
FROM #t t3
JOIN
(
select C,
(
select v
from CTE t1
where t1.rn = t2.rn
for xml path(''), type
).value('.', 'varchar(18)') [value]
from CTE t2
group by t2.rn, C
) t4
ON t3.C = t4.C
SELECT * FROM #t