0

I've few strings in Table1 which i want to update based on values available in Table2.

Sample code

http://rextester.com/HQFOQ18215

IF OBJECT_ID('Test1','U') iS NOT NULL
DROP TABLE Test1;

IF OBJECT_ID('Test2','U') iS NOT NULL
DROP TABLE Test2;


Create table Test1 
(
 Id         INT 
,Lid        INT
,MyString   VARCHAR(MAX)
,Did        INT
,Secid      INT
);

INSERT INTO Test1 values (1,100,'you,shall,not,pass,gandlaf,the,grey', 401, 501);
INSERT INTO Test1 values (2,100,'ok,fine,bye', 401, 501);
INSERT INTO Test1 values (3,100,'test,dev,uat,prod', 403, 501);
INSERT INTO Test1 values (4,100,'1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16', 404, 501);


Create table Test2 
(
Id              INT IDENTITY(1,1)
,SecId          INT 
,CommaPosition  INT
,Value          VARCHAR(50)
,Did            INT
,RowId          INT
);

INSERT INTO Test2 Values (501, 4, '[Quantity]', 401, 1);
INSERT INTO Test2 Values (501, 14, '[Price]', 401, 1);
INSERT INTO Test2 Values (501, 4, '[Quantity]', 401, 2);
INSERT INTO Test2 Values (501, 14, '[Price]', 401, 2);
INSERT INTO Test2 Values (501, 14, '[Quantity|Price]', 403, 3);
INSERT INTO Test2 Values (501, 4, '[Interest]', 404, 4);
INSERT INTO Test2 Values (501, 14, '[Expired]', 404, 4);

SELECT * FROM Test1;
SELECT * FROM Test2;

Expected Output

/*

Expected OUTPUT 

Id  Lid     MyString                                                    Did     Secid
1   100     you,shall,not,[quantity],gandlaf,the,grey,,,,,,,[Price],    401     501
2   100     ok,fine,bye,[quantity],,,,,,,,,,[Price],                    402     501
3   100     test,dev,uat,prod,,,,,,,,,,[Quantity|Price],                403     501
4   100     1,2,3,[Quantity],5,6,7,8,9,10,11,12,13,[Price],15,16        404     501
*/
  1. First string you,shall,not,pass,gandlaf,the,grey where "pass" is before 4th comma position which is replaced by [quantity] from table2 but this doesn't have 14th comma so comma is replicated till it reaches the 14th comma and before 14th comma [Price] has been replaced. Final output is "you,shall,not,[quantity],gandlaf,the,grey,,,,,,,[Price],"

  2. Second string ok,fine,bye doesn't have 4th comma either 14th comma, so 4th comma is added and before that [quantity] is substituted and then comma added till 14th position basd on Comma position column in Table2 and final string becomes ok,fine,bye,[quantity],,,,,,,,,,[Price],

  3. Third-string test,dev,uat,prod only 14th comma position is available in Table2, so comma is replicated till 14th commas and before 14th comma [Quantity|Price] string is added final string becomes test,dev,uat,prod,,,,,,,,,,[Quantity|Price],

  4. In 4th string 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 if you see 4th and 14th become commas are available so string before 14th comma is replaced by [Interest] and string become 14th comma is replaced by [Expired] keeping the remaining string untouched.

I want to achieve above output, I tried below update statement is not giving me desired results.

UPDATE T1
SET   T1.MyString = T1.MyString + REPLICATE(',',T2.CommaPosition - (len(T1.MyString) - LEN(REPLACE(T1.MyString,',','')))) 
FROM  Test1 as T1 INNER JOIN Test2 as T2 ON T1.Secid = T2.SecId AND T1.Did = T2.Did AND T1.Id = T2.RowId;

SELECT  DISTINCT T1.Lid, T1.MyString, T1.Did, T1.Secid, T2.RowId
FROM  Test1 as T1 INNER JOIN Test2 as T2 ON T1.Secid = T2.SecId AND T1.Did = T2.Did AND T1.Id = T2.RowId;
1
  • 1
    This doesn't make any sense at all. Why do you have these bizarre delimited string in the first place? That violates 1NF and defeats the whole point of relational data. Commented Apr 25, 2017 at 19:23

2 Answers 2

2

I'm assuming you performing some sort of macro substition.

A couple of note:

  1. The Trailing Comma seems to be inconsistent, so I left it out
  2. You have Price in the final row, while I have Expired

Example

Select A.ID
      ,A.Lid
      ,B.MyString
      ,A.Did
      ,A.Secid
 From  Test1 A
 Cross Apply (
                Select MyString = Stuff((Select ',' +RetVal 
                  From (
                           Select RetSeq
                                 ,RetVal = max(IsNull(B2.Value,B1.RetVal))
                            From (
                                    Select Top (Select max(CommaPosition) From Test2 Where RowID=A.ID )
                                           RetSeq=Row_Number() Over (Order By (Select null))
                                          ,RetVal='' 
                                     From master..spt_values 
                                    Union All
                                    Select RetSeq = Row_Number() over (Order By (Select null))
                                          ,RetVal = LTrim(RTrim(N.i.value('(./text())[1]', 'varchar(max)')))
                                    From  (Select x = Cast('<x>' + replace((Select replace(A.MyString,',','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as X 
                                    Cross Apply x.nodes('x') AS N(i)
                                 ) B1
                            Left Join Test2 B2 on B2.RowID=A.ID and B2.CommaPosition=B1.RetSeq
                            Group By B1.RetSeq
                        ) B3
                  Order by RetSeq
                  For XML Path ('')),1,1,'') 
             ) B

Returns

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

2

The correct answer is don't store multiple values in a single column. This violates first normal form!!!

It's best if you decompose your concatenated strings into a parent and a child table. Your example Table2 which appears to be a set of modification commands already has the required structure--you just need to put the full set of values into a permanent table just like it.

Once you have that, processing your change data is super easy: join on key values and update! Boom, done. Your database should be normalized and should not store data that is prone to modification in a format that requires unpacking it, modifying it, and packing it again.

If you need some way to concatenates values together to produce output like MyString in Table1, make a view for that, or do it in front-end code. There's a wonderful SQL-based solution with FOR XML PATH that can do this easily. Beware of random XML PATH solutions that you might run across as most of the ones out there can't accommodate strings that contain xml-like elements such as > and <. Use the one from this example below (complete with the .value() expression) which can accommodate such characters.

This is a generic example that shows how to combine a parent and child table into single rows, one for each parent, with all the child values concatenated together. You can put an ORDER BY clause into the query using FOR XML to get a particular order.

CREATE TABLE dbo.Parent (
    ParentId int identity(1,1) NOT NULL CONSTRAINT PK_Parent PRIMARY KEY CLUSTERED,
    Name varchar(100) NOT NULL CONSTRAINT UQ_Parent_Name UNIQUE
);

CREATE TABLE dbo.ParentChild (
   ParentId int NOT NULL,
   ChildName varchar(100)
);

INSERT dbo.Parent (Name) VALUES
   ('Parent 1'), ('Parent X'), ('Parent B');

INSERT dbo.ParentChild (ParentId, ChildName) VALUES
   (1, 'ABC'), (1, 'WHOAMAN'), (1, 'QRT'),
   (2, 'XYZ'), (3, 'LMN'), (3, 'MAN'), (3, 'WHOADOG');

SELECT
   p.*,
   Children = Substring((
      SELECT ', ' + ChildName
      FROM ParentChild pc
      WHERE p.ParentId = pc.ParentId
      FOR XML PATH(''), TYPE
   ).value('.[1]', 'varchar(max)'), 3, 8000)
FROM
   dbo.Parent p
WHERE -- an example of filtering by a child value without substrings on the combined string
   EXISTS (
      SELECT *
      FROM dbo.ParentChild pc
      WHERE
         p.ParentId = pc.ParentId
         AND pc.ChildName = 'WHOADOG'
   );

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.