简体   繁体   English

比较字符串并突出显示在T-SQL中发现的不匹配

[英]Compare strings and Highlight mismatch wherever found in T-SQL

I have below Material table which contains data like this: 我在下面的Material表中包含如下数据:

 [PO Number] [Actual Material]   [Ideal Material]
 ----------------------------------------------------
 1000000     Milk-Sugar-tea       Milk-Sugar-Coffee
 1000001     Milk-Water           Milk-Water-Ice-tea

I have the requirement where I need to compare two columns Actual Material and Ideal material and highlight the mismatch materials in SQL. 我有需要比较两列“ Actual Material和“ Ideal material并在SQL中突出显示不匹配材料的要求。

Mismatch would be 不匹配将是

[PO Number] [Actual Material]   [Ideal Material]     [Mismatch]
----------------------------------------------------------------  
1000000     Milk-Sugar-tea       Milk-Sugar-Coffee    tea-coffee
1000001     Milk-Water           Milk-Water-Ice-tea   Ice-tea

How to achieve this in a SQL query? 如何在SQL查询中实现呢?

I use a table value functions for split actual-material an ideal-material values. 我使用表值函数将实际物料值拆分为理想物料值。

Split Function detail is ALTER FUNCTION [dbo].[Split] ( @String NVARCHAR(4000), @Delimiter NCHAR(1) ) RETURNS TABLE AS RETURN ( WITH Split(stpos,endpos) AS( SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos UNION ALL SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1) FROM Split WHERE endpos > 0 ) SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)), 'Value' = LTRIM(SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)-stpos)) FROM Split ) 拆分函数的详细信息为ALTER FUNCTION [dbo].[Split] ( @String NVARCHAR(4000), @Delimiter NCHAR(1) ) RETURNS TABLE AS RETURN ( WITH Split(stpos,endpos) AS( SELECT 0 AS stpos, CHARINDEX(@Delimiter,@String) AS endpos UNION ALL SELECT endpos+1, CHARINDEX(@Delimiter,@String,endpos+1) FROM Split WHERE endpos > 0 ) SELECT 'Id' = ROW_NUMBER() OVER (ORDER BY (SELECT 1)), 'Value' = LTRIM(SUBSTRING(@String,stpos,COALESCE(NULLIF(endpos,0),LEN(@String)+1)-stpos)) FROM Split )

For result table is 对于结果表是

declare @result table (
[PO Number] int , [Actual Material] varchar(100),[Ideal Material] varchar(100),Mismatch varchar(200)
)

And query for result table insert is : 查询结果表插入为:

;with CTE AS (
select distinct s.* ,x1.Value x1value,x2.Value x2value
from dbo.material s 
 outer apply (select *from Split(s.[Actual Material],'-')) x1 
 outer apply (select *from Split(s.[Ideal Material],'-')) x2 
),
CTE2 AS (
SELECT  distinct c.[PO Number],c.[Actual Material],c.[Ideal Material]
  ,case when not exists (select *from CTE c2 where c2.[PO Number] = c.[PO Number] and c2.x2value = c.x1value ) then c.x1value else '' end [ActualMismatch]
,case when not exists (select *from CTE c2 where c2.[PO Number] = c.[PO Number]         and c2.x1value = c.x2value ) then c.x2value else '' end [IdealMismatch]
 FROM CTE c 
 )

 insert into @result
 SELECt  c.[PO Number],c.[Actual Material],c.[Ideal Material],c.ActualMismatch Mismatch from CTE2 c
 union 
 SELECt c.[PO Number],c.[Actual Material],c.[Ideal Material] ,c.IdealMismatch Mismatch  from CTE2  c
  where 
  (c.ActualMismatch !='' or 
 c.[IdealMismatch] !='')
 order by 1 

 select [PO Number],[Actual Material],[Ideal Material],
        STUFF((
    SELECT '-' + mismatch 
    FROM @result 
    WHERE ([PO Number] = c.[PO Number]) 
    FOR XML PATH(''),TYPE).value('(./text())[1]','VARCHAR(MAX)')
  ,1,2,'') AS mismatch

  from @result c 
 where Mismatch !=''
Group by [PO Number],[Actual Material],[Ideal Material]

how to coalesce mismatch values ! 如何合并不匹配的值! => with xml stuff =>与xml的东西

As many others have very sensibly suggested, your first port of call should be to restructure your database so you are actually storing normalised data against your PO Numbers . 正如许多其他人非常明智地建议的那样,您的第一个调用端口应该是重组数据库,以便您实际上是根据PO Numbers存储规范化的数据。


That said, something we are dealt a rubbish hand and have to play the cards we get. 就是说,有些东西给了我们垃圾,必须打出我们得到的牌。 To answer your question exactly as it is asked, you can do the following: 要完全按照要求回答您的问题,您可以执行以下操作:

If you are not on SQL Server 2016 and therefore cannot use the built in string_split function, start by creating your own: 如果您不在SQL Server 2016上,因此无法使用内置的string_split函数, string_split创建自己的函数:

create function [dbo].[StringSplit]
(
    @str nvarchar(4000) = ' '               -- String to split.
    ,@delimiter as nvarchar(1) = ','        -- Delimiting value to split on.
    ,@num as int = null                     -- Which value to return.
)
returns @results table(ItemNumber int, Item nvarchar(4000))
as
begin
    declare @return nvarchar(4000);

    -- Handle null @str values
    select @str = case when len(isnull(@str,'')) = 0 then '' else @str end;

                    -- Start tally table with 10 rows.
    with n(n)   as (select n from (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n(n))

                    -- 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(@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(@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,@str,s),0)-s,4000) from s)

    insert into @results
    select rn as ItemNumber
            ,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;

    return;

end

Using this function you can then create a set each for your Actual Material and Ideal Material columns, combine them to find the differences using a full join and then concatenate the results using stuff and for xml into one string value: 然后,您可以使用此功能为“ Actual Material和“ Ideal Material列分别创建一个集合,使用full join将它们组合以查找差异,然后使用stufffor xml将结果连接为一个字符串值:

declare @t table(PONumber int, ActualMaterial nvarchar(50), IdealMaterial nvarchar(50));
insert into @t values (1000000,'Milk-Sugar-tea','Milk-Sugar-Coffee'),(1000001,'Milk-Water','Milk-Water-Ice-tea');

with a as
(
    select t.PONumber
        ,a.Item
    from @t t
        outer apply dbo.StringSplit(t.ActualMaterial,'-',null) a
), i as
(
    select t.PONumber
            ,i.Item
    from @t t
        outer apply dbo.StringSplit(t.IdealMaterial,'-',null) i
), m as
(
    select isnull(a.PONumber,i.PONumber) as PONumber
            ,isnull(a.Item,i.Item) as Item
    from a
        full join i
            on(a.PONumber = i.PONumber
                and a.Item = i.Item
                )
    where a.Item is null
        or i.Item is null
)
select t.PONumber
        ,t.ActualMaterial
        ,t.IdealMaterial
        ,stuff((select '-' + m.Item
                    from m
                    where t.PONumber = m.PONumber
                    order by m.Item
                    for xml path('')
                )
                ,1,1,'') as Mismatch
from @t t
order by PONumber;

Output: 输出:

+----------+----------------+--------------------+------------+
| PONumber | ActualMaterial |   IdealMaterial    | Mismatch   |
+----------+----------------+--------------------+------------+
|  1000000 | Milk-Sugar-tea | Milk-Sugar-Coffee  | Coffee-tea |
|  1000001 | Milk-Water     | Milk-Water-Ice-tea | Ice-tea    |
+----------+----------------+--------------------+------------+

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM