SQL Server Puzzle - 在逗号之前复制逗号并替换字符串?

时间:2017-04-25 19:13:50

标签: sql sql-server sql-server-2008 tsql sql-server-2008-r2

我在Table1中有几个字符串,我想根据表2中的可用值进行更新。

示例代码

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 

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. 第一个字符串you,shall,not,pass,gandlaf,the,grey其中"pass"位于第4个逗号位置之前,该位置由table2中的[quantity]替换但是没有第14个逗号 所以逗号被复制,直到达到第14个逗号并且在第14个逗号[Price]被替换之前。最终输出为"you,shall,not,[quantity],gandlaf,the,grey,,,,,,,[Price],"

  2. 第二个字符串ok,fine,bye没有第四个逗号或者第14个逗号,所以添加第4个逗号,然后替换[quantity],然后添加逗号直到第14个位置 基于表2中的逗号位置列,最终字符串变为ok,fine,bye,[quantity],,,,,,,,,,[Price],

  3. 表2中只有第三个字符串test,dev,uat,prod可用的第14个逗号位置,因此逗号被复制到第14个逗号和第14个逗号[Quantity|Price]字符串之前 添加最终字符串变为test,dev,uat,prod,,,,,,,,,,[Quantity|Price],

  4. 在第4个字符串1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16中,如果您看到第4个和第14个变为逗号,则第14个逗号之前的字符串被[Interest]替换,字符串变为第14个逗号被替换为 [Expired]保持剩下的字符串不受影响。

  5. 我想实现上面的输出,我在下面尝试更新语句并没有给我想要的结果。

    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;
    

2 个答案:

答案 0 :(得分:2)

我假设您正在执行某种宏观替代。

几点说明:

  1. 尾随逗号似乎不一致,所以我把它留了出来
  2. 您在最后一行中有价格,而我已过期
  3. 示例

    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
    

    <强>返回

    enter image description here

答案 1 :(得分:2)

正确答案是不要在一个列中存储多个值。这违反了第一范式 !!!

如果将连接的字符串分解为父表和子表,则最好。您的示例Table2似乎是一组修改命令,已经具有所需的结构 - 您只需将完整的值集合放入永久表中即可。

完成后,处理您的更改数据非常简单:加入关键值并更新!热潮,完成了。您的数据库应该规范化,不应该存储易于修改的数据,格式需要解压缩,修改并重新打包。

如果您需要某种方法将值连接在一起以生成MyString中的Table1输出,请为此创建视图,或者在前端代码中执行此操作。有FOR XML PATH的基于SQL的精彩解决方案可以轻松完成。请注意您可能遇到的随机XML PATH解决方案,因为那里的大多数解决方案都无法容纳包含xml类元素的字符串,例如><。使用以下示例中的一个(带有.value()表达式) 可以容纳这些字符。

这是一个通用示例,演示如何将父表和子表组合成单行,每个父表一行,所有子值连接在一起。您可以使用ORDER BYFOR XML子句放入查询中以获取特定订单。

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'
   );