比较两个表并更新Flag列中的值

时间:2017-10-10 08:34:51

标签: sql-server sql-server-2008

我有两个表和像这样的值

`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来获取输出并更新Flag列中的值,如屏幕截图所示,我尝试过类似这样的代码

`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

1 个答案:

答案 0 :(得分:0)

我认为您需要先了解为什么您认为XML方法的效果不能满足您的需求,it has actually been shown to perform very well for larger input strings

如果您只需要处理最多4000或8000个字符(分别为非max nvarcharvarchar类型的输入字符串),您可以使用包含在其中的计数表内联表值函数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