2017-10-10 118 views
0

我有两个表,这样比较两个表并更新值在标志列

`create table InputLocationTable(SKUID int,InputLocations varchar(100),Flag varchar(100)) 
create table Location(SKUID int,Locations varchar(100)) 

insert into InputLocationTable(SKUID,InputLocations) values(11,'Loc1, Loc2, Loc3, Loc4, Loc5, Loc6') 
insert into InputLocationTable(SKUID,InputLocations) values(12,'Loc1, Loc2') 
insert into InputLocationTable(SKUID,InputLocations) values(13,'Loc4,Loc5') 

insert into Location(SKUID,Locations) values(11,'Loc3') 
insert into Location(SKUID,Locations) values(11,'Loc4') 
insert into Location(SKUID,Locations) values(11,'Loc5') 
insert into Location(SKUID,Locations) values(11,'Loc7') 
insert into Location(SKUID,Locations) values(12,'Loc10') 
insert into Location(SKUID,Locations) values(12,'Loc1') 
insert into Location(SKUID,Locations) values(12,'Loc5') 
insert into Location(SKUID,Locations) values(13,'Loc4') 
insert into Location(SKUID,Locations) values(13,'Loc2') 
insert into Location(SKUID,Locations) values(13,'Loc2')` 

值我需要通过匹配每个表SKUID对得到的输出,并更新标志列中的值如截图所示,我已经试过这样的事情代码

`SELECT STUFF((select ','+ Data.C1 
FROM 
(select 
    n.r.value('.', 'varchar(50)') AS C1 
from InputLocation as T 
    cross apply (select cast('<r>'+replace(replace(Location,'&','&'), ',', '</r><r>')+'</r>' as xml)) as S(XMLCol) 
    cross apply S.XMLCol.nodes('r') as n(r)) DATA 
WHERE data.C1 NOT IN (SELECT Location 
       FROM Location) for xml path('')),1,1,'') As Output` 

但不相信有输出,并且还我试图避免XML路径代码,因为性能并不第一名这段代码,我需要的输出像下面的截图一样。任何帮助将不胜感激。

enter image description here

+0

你的意思是说,更新的标志的列,其中INputLocation不是位置表的位置scolumn –

+0

我不知道如果我正确地理解你的问题......你说,你设法找到与XML解决方案但宁愿避免使用它。那么,“FOR XML PATH”afaik是直到SQL Server 2016执行字符串连接的最佳和推荐的解决方案。2016年,MSFT添加了STRING_AGG函数。在SQL Server 2008上,您可以使用自定义程序集作为替代方案。但这有其不利之处。看看https://www.sqlshack.com/string-concatenation-done-right-part-2-an-effective-technique/。 –

+0

嗨,感谢您的重播,我只是避免使用XML路径,这并不意味着我不应该使用和所有,我使用的SQL服务器2014年,所以请建议我更好的代码或答案为 – Karthik

回答

0

我认为你需要先了解一下,为什么你认为XML方法不执行不够好,您的需求,为it has actually been shown to perform very well for larger input strings

如果只需要处理的输入串高达要么4000或8000个字符(分别非maxnvarcharvarchar类型),则可以利用包含内嵌表值函数which will also perform very well内的帐簿表。我使用的版本可以在这篇文章的末尾找到。

利用这个功能,我们可以在你的InputLocations柱拆分出来的价值观,但我们仍然需要使用for xml将它们串联到一起为你想要的格式:

-- Define data 
declare @InputLocationTable table (SKUID int,InputLocations varchar(100),Flag varchar(100)); 
declare @Location table (SKUID int,Locations varchar(100)); 

insert into @InputLocationTable(SKUID,InputLocations) values (11,'Loc1, Loc2, Loc3, Loc4, Loc5, Loc6'),(12,'Loc1, Loc2'),(13,'Loc4,Loc5'),(14,'Loc1'); 
insert into @Location(SKUID,Locations) values (11,'Loc3'),(11,'Loc4'),(11,'Loc5'),(11,'Loc7'),(12,'Loc10'),(12,'Loc1'),(12,'Loc5'),(13,'Loc4'),(13,'Loc2'),(13,'Loc2'),(14,'Loc1'); 

--Query 

-- Derived table splits out the values held within the InputLocations column 
with i as 
(
    select i.SKUID 
      ,i.InputLocations 
      ,s.item as Loc 
    from @InputLocationTable as i 
     cross apply dbo.fn_StringSplit4k(replace(i.InputLocations,' ',''),',',null) as s 
) 
select il.SKUID 
     ,il.InputLocations 
     ,isnull('Add ' -- The split Locations are then matched to those already in @Location and those not present are concatenated together. 
       + stuff((select ', ' + i.Loc 
         from i 
          left join @Location as l 
           on i.SKUID = l.SKUID 
            and i.Loc = l.Locations 
         where il.SKUID = i.SKUID 
          and l.SKUID is null 
         for xml path('') 
        ) 
        ,1,2,'' 
        ) 
       ,'No Flag') as Flag 
from @InputLocationTable as il 
order by il.SKUID; 

输出:

+-------+------------------------------------+----------------------+ 
| SKUID |   InputLocations   |   Flag   | 
+-------+------------------------------------+----------------------+ 
| 11 | Loc1, Loc2, Loc3, Loc4, Loc5, Loc6 | Add Loc1, Loc2, Loc6 | 
| 12 | Loc1, Loc2       | Add Loc2    | 
| 13 | Loc4,Loc5       | Add Loc5    | 
| 14 | Loc1        | No Flag    | 
+-------+------------------------------------+----------------------+ 

对于nvarchar输入(我有不同的功能varcharmax类型输入)这是我的版本的字符串分裂功能链接上面:

create function [dbo].[fn_StringSplit4k] 
(
    @str nvarchar(4000) = ' '  -- String to split. 
,@delimiter as nvarchar(1) = ',' -- Delimiting value to split on. 
,@num as int = null    -- Which value in the list to return. NULL returns all. 
) 
returns table 
as 
return 
    -- Start tally table with 10 rows. 
with n(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) 

    -- Select the same number of rows as characters in @str as incremental row numbers. 
    -- Cross joins increase exponentially to a max possible 10,000 rows to cover largest @str length. 
    ,t(t) as (select top (select len(isnull(@str,'')) a) row_number() over (order by (select null)) from n n1,n n2,n n3,n n4) 

    -- Return the position of every value that follows the specified delimiter. 
    ,s(s) as (select 1 union all select t+1 from t where substring(isnull(@str,''),t,1) = @delimiter) 

    -- Return the start and length of every value, to use in the SUBSTRING function. 
    -- ISNULL/NULLIF combo handles the last value where there is no delimiter at the end of the string. 
    ,l(s,l) as (select s,isnull(nullif(charindex(@delimiter,isnull(@str,''),s),0)-s,4000) from s) 

select rn 
     ,item 
from(select row_number() over(order by s) as rn 
      ,substring(@str,s,l) as item 
     from l 
    ) a 
where rn = @num 
    or @num is null; 
go