【问题标题】:SQL Server Puzzle - Replicate comma and replace string before comma?SQL Server Puzzle - 复制逗号并在逗号之前替换字符串?
【发布时间】:2017-04-25 19:13:49
【问题描述】:

表 1 中有几个字符串,我想根据表 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 没有第 4 个逗号或第 14 个逗号,因此添加第 4 个逗号,在此之前替换 [quantity],然后添加逗号直到第 14 个位置 基于 Table2 中的逗号位置列,最终字符串变为 ok,fine,bye,[quantity],,,,,,,,,,[Price],

  3. 第三字符串test,dev,uat,prod在表2中只有第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] 保持剩余字符串不变。

我想达到上面的输出,我试过下面的更新语句没有给我想要的结果。

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;

【问题讨论】:

  • 这根本没有任何意义。为什么你首先有这些奇怪的分隔字符串?这违反了 1NF 并破坏了关系数据的全部意义。

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


【解决方案1】:

我假设您执行某种宏替换。

注意几点:

  1. 尾随逗号似乎不一致,所以我省略了它
  2. 你在最后一行有Price,而我有Expired

示例

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

退货

【讨论】:

    【解决方案2】:

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

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

    一旦你有了这些,处理你的变更数据就超级简单了:加入键值并更新!砰,完成。您的数据库应该被规范化,并且不应该以需要对其进行解包、修改和再次打包的格式存储易于修改的数据。

    如果您需要某种方法将值连接在一起以产生像MyString 中的MyString 这样的输出,请为此创建一个视图,或者在前端代码中执行此操作。有一个很棒的基于 SQL 的解决方案,FOR XML PATH 可以轻松做到这一点。请注意您可能遇到的随机XML PATH 解决方案,因为大多数解决方案无法容纳包含类似xml 元素的字符串,例如&gt;&lt;。使用下面这个例子中的那个(完整的 .value() 表达式)可以容纳这样的字符。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-02
      • 2017-02-25
      • 1970-01-01
      • 2017-12-28
      • 2017-11-27
      相关资源
      最近更新 更多