Sql 如何从另一个值中删除类似文本

Sql 如何从另一个值中删除类似文本,sql,sql-server,Sql,Sql Server,我有一个巨大的数据集,数百万行,包含各种各样的人和他们相关的项目。在许多情况下,这些人的名字也出现在物品名称中。我想找到项目名称的最短子字符串,其中所有者名称或其名称的一部分不再存在 数据样本如下: CREATE TABLE test ([ID] nvarchar(255), [OWNER] nvarchar(255), [ITEM] nvarchar(255)) INSERT INTO test SELECT '1','A B C','A B X X X' UNION ALL SELECT '

我有一个巨大的数据集,数百万行,包含各种各样的人和他们相关的项目。在许多情况下,这些人的名字也出现在物品名称中。我想找到项目名称的最短子字符串,其中所有者名称或其名称的一部分不再存在

数据样本如下:

CREATE TABLE test ([ID] nvarchar(255), [OWNER] nvarchar(255), [ITEM] nvarchar(255))
INSERT INTO test
SELECT '1','A B C','A B X X X'
UNION ALL
SELECT '2','ABC DEF','XABCD XX X'
UNION ALL
SELECT '2','ABC DEF','YABCD X X'
UNION ALL
SELECT '3','X X X X','YPD X X'
UNION ALL
SELECT '4','XYZ','X X X'
UNION ALL
SELECT '5','A B C','OOO PPP QQQ'
理想输出为:

ID | OWNER     | ITEM       | SHORT ITEM
1  | A B C     | A B X X X  | X X X
2  | ABC DEF   | XABCD XX X | XX X
2  | ABC DEF   | YABCD X X  | X X
3  | X X X X   | YPD X X    | X X
4  | XYZ       | X X X      | X X X
5  | A B C     | OOO PPP DDD| PPP QQQ
这个输出包括两种情况,在这些情况下,我希望从项目名称中删除一些不是所有者名称的内容,因此我将其硬编码到查询中。我编写了以下查询:

;WITH p1 as(    --Retrieving first word of ITEM and ITEM minus first word
    SELECT SUBSTRING([ITEM],1,
            case when CHARINDEX(' ',[ITEM])=0 then LEN([ITEM])     --When no space in ITEM, return ITEM
            else CHARINDEX(' ', [ITEM]) -1 end) as w1p                     --Return the first word separated by space
        ,SUBSTRING([ITEM],CHARINDEX(' ',[ITEM])+1,100) as m1p      --Return everything minus the first word
        ,[ITEM]
        ,[ID]
        ,[OWNER]
    FROM test
),p2 as(    --Retrieving second word of ITEM and ITEM minus second word
    SELECT SUBSTRING(m1p,1,
            case when CHARINDEX(' ',m1p)=0 then LEN(m1p) 
            else CHARINDEX(' ',m1p) -1 end) as w2p
        ,SUBSTRING(m1p,CHARINDEX(' ',m1p)+1,100) as m2p
        ,[ITEM]
        ,[ID]
        ,[w1p]
        ,[m1p]
    FROM p1
),p3 as(    --Retrieving third word of ITEM and ITEM minus third word
    SELECT SUBSTRING(m2p,1,
            case when CHARINDEX(' ',m2p)=0 then LEN(m2p) 
            else CHARINDEX(' ',m2p) -1 end) as w3p
        ,SUBSTRING(m2p,CHARINDEX(' ',m2p)+1,100) as m3p
        ,*
    FROM p2
),p4 as(    --Retrieving fourth word of ITEM and ITEM minus fourth word
    SELECT SUBSTRING(m3p,1,
        case when CHARINDEX(' ',m3p)=0 then LEN(m3p) 
        else CHARINDEX(' ',m3p) -1 end) as w4p
    ,SUBSTRING(m3p,CHARINDEX(' ',m3p)+1,100) as m4p
    ,*
    FROM p3
),m1 as(    --Retrieving first word of OWNER and OWNER minus first word
    SELECT SUBSTRING([OWNER],1,
            CASE WHEN CHARINDEX(' ',[OWNER])=0 THEN LEN([OWNER])
                ELSE CHARINDEX(' ',[OWNER])-1 end) as w1m
        ,SUBSTRING([OWNER],CHARINDEX(' ',[OWNER])+1,100) as m1m
        ,[OWNER]
        ,[ID]
    FROM p1
    GROUP BY [OWNER], [ID]
),m2 as(    --Retrieving second word of OWNER and OWNER minus second word
    SELECT SUBSTRING(m1m,1,
            case when CHARINDEX(' ', m1m) = 0 then LEN(m1m) 
            else CHARINDEX(' ', m1m) -1 end) as w2m
        ,SUBSTRING(m1m,CHARINDEX(' ',m1m)+1,100) as m2m
        ,*
        FROM m1
),m3 as(    --Retrieving third word of OWNER and OWNER minus third word
    SELECT SUBSTRING(m2m,1,
            case when CHARINDEX(' ', m2m) = 0 then LEN(m2m) 
            else CHARINDEX(' ', m2m) -1 end) as w3m
        ,SUBSTRING(m2m,CHARINDEX(' ',m2m)+1,100) as m3m
        ,*
        FROM m2
),m4 as(    --Retrieving fourth word of OWNER
    SELECT SUBSTRING(m3m,1,
            case when CHARINDEX(' ', m3m) = 0 then LEN(m3m) 
            else CHARINDEX(' ', m3m) -1 end) as w4m
        ,*
        FROM m3
),ms as(    --Adding special cases not caught by regular query
    SELECT CASE WHEN [ID] IN ('3','5') THEN
            CASE WHEN [ID] = '3' THEN 'YPD' 
                WHEN [ID] = '5' THEN 'OOO' 
                ELSE NULL END 
            ELSE NULL END as SPEC
        ,*
        FROM m4
)
SELECT m.[ID]   --Finding closest shortname
,m.[OWNER]
,p.[ITEM]
,CASE WHEN SUBSTRING(p.[ITEM],1,LEN(m.SPEC)) = SPEC AND SPEC IS NOT NULL THEN LTRIM(SUBSTRING(p.[ITEM],LEN(m.SPEC)+1,100))       --If hardcoded phrase in ITEM, return ITEM minus that phrase
WHEN p.w1p LIKE '%'+m.w1m+'%' AND p.w2p NOT LIKE '%'+m.w2m+'%' AND p.w3p NOT LIKE '%'+m.w3m+'%' AND p.w4p NOT LIKE '%'+m.w4m+'%' THEN p.m1p      --If first word of ITEM match first of OWNER, return ITEM minus first
WHEN p.w1p LIKE '%'+m.w1m+'%' AND p.w2p LIKE '%'+m.w2m+'%' AND p.w3p NOT LIKE '%'+m.w3m+'%' AND p.w4p NOT LIKE '%'+m.w4m+'%' THEN p.m2p          --If first two words of ITEM match first of OWNER, return ITEM minus two words
WHEN p.w1p LIKE '%'+m.w1m+'%' AND p.w2p LIKE '%'+m.w2m+'%' AND p.w3p LIKE '%'+m.w3m+'%' AND p.w4p NOT LIKE '%'+m.w4m+'%' THEN p.m3p              --If first three words of ITEM match first of OWNER, return ITEM minus three words
WHEN p.w1p LIKE '%'+m.w1m+'%' AND p.w2p LIKE '%'+m.w2m+'%' AND p.w3p LIKE '%'+m.w3m+'%' AND p.w4p LIKE '%'+m.w4m+'%' THEN p.m4p                  --If first four words of ITEM match first of OWNER, return ITEM minus four words                                                     
ELSE p.[ITEM]
END AS [SHORT ITEM]
FROM p4 p
LEFT JOIN ms m ON p.[ID] = m.[ID]
虽然这达到了我的目标,但它看起来不太好,感觉可以优化。它要求where语句在执行时具有任何速度。尽管我可能不会在完整的数据集上运行这个,但我正在寻找改进的方法。我没有查看执行计划的权限,因此无法共享


感谢您提供的任何帮助或建议。

好的,我仅使用表中的前三行开发了此功能。用表名替换test。让我知道这是否适合你

select *, row_number() over(order by id) rowid 
into #a 
from #test 

select *, item short_item, row_number() over(order by id) rowid 
into #b 
from #test

declare @iterator int=1
declare @owner varchar(max)
declare @owneroriginal varchar(max)
declare @item varchar(max)
declare @itemoriginal varchar(max)
declare @itemactualoriginal varchar(max)

while @iterator<=(select max(rowid) from #a)
begin
select @owner=[owner] from #a where rowid=@iterator
select @owneroriginal=[owner] from #a where rowid=@iterator
select @item=[item] from #a where rowid=@iterator
select @itemoriginal=[item] from #a where rowid=@iterator
select @itemactualoriginal=[item] from #a where rowid=@iterator

while @owner<>''
begin
select @owner=left(@owneroriginal, charindex(' ',@owneroriginal))  
select @owneroriginal= ltrim(replace(@owneroriginal,@owner,''))
select @item=left(@itemoriginal, charindex(' ',@itemoriginal))  
select @itemoriginal= ltrim(replace(@itemoriginal,@item,''))
--select @owner, @owneroriginal, @item, @itemoriginal

if @itemactualoriginal
like '%'+rtrim(@owner)+'%' and @owner<>''
begin
--select 1
update #b 
set short_item=replace(short_item, rtrim(@item),'') 
where rowid=@iterator
end
else if @@rowcount=0
update #b  
set short_item=
    case 
        when @owner = '' 
        then ltrim(replace(short_item, @owneroriginal,'')) 
        else ltrim(replace(short_item, @owner,'')) 
    end 
where rowid=@iterator
end
set @iterator=@iterator+1
end 

select id, owner, item, short_item from #b

为什么不是ID5 OOO PPP DDD的短项目?@DanielMarcus这是我在ms cte中专门定义和删除的一个特例。”“OOO”成为id 5的SPEC值,然后匹配并删除。那么,您试图为id 1和id 2创建算法的一般规则是什么?@DanielMarcus。从项目名称中捕获并删除所有者名称。这样,两个文件中的空格分隔的单词都会从项目中删除,而不会删除任何不属于所有者名称的单词。好的,我想我现在明白了-我会为你做这件事不会太糟糕