SQL Server:提取特定字符串,然后加入两个表

Posted

技术标签:

【中文标题】SQL Server:提取特定字符串,然后加入两个表【英文标题】:SQL Server : extracting specific strings and then joining two tables 【发布时间】:2021-03-07 12:43:38 【问题描述】:

我对 SQL Server 还很陌生。

为了获得两个表之间的匹配,我已获得两个表加入:PRODUCTCUSTOMERS

第一个表 (PRODUCT) 有 14 列,而第二个表 (CUSTOMERS) 有 5 列。快速查看数据,它似乎是一个列,它是两个表之间的关系:PRODUCT 中的NAMES1CUSTOMERS 中的NAMES2

但是,为了能够加入表,我需要首先从表 CUSTOMERS 的不同列中提取一些字符串,因为当其他人将数据导入 SQL 时出现了问题。我在表 CUSTOMERS 的 NAMES2 列中需要的名称是该字符串的最后一个子字符串(在下面的示例中为 James),然后名称的其余部分继续到右侧的列 (ID),直到出现“别名”一词。

NAMES2 ID
2247 50% James McCarthy Petersson Alias: James ","200874741",

因此,我需要提取 NAMES2 列的最后一个字符串以及 ID 列 (James McCarthy Petersson) 中“别名”之前的所有内容,然后在 PRODUCT 表中内连接 NAMES1 列。

关于如何做到这一点的任何建议?

【问题讨论】:

这是一个非常糟糕的桌子设计。你能解决它还是你必须使用你所拥有的? 一个示例不足以分析您的数据并决定采用一种方法来隔离相关信息的识别片段。在这个特定的示例中,您似乎想加入“James” - 那么这些特定值所代表的模式是否一致? 现在我看一下,这是不是因为导入变坏了,有人没有正确划分“字段”,不小心把两个单独的列之间的名称切掉了?就此而言,为什么会在 PRODUCT 表中找到一个人的姓名?这里似乎有严重的问题。 可能是这样。是的,这里有些事情严重搞砸了,我需要解决这个问题。我想应该可以吧? 【参考方案1】:

您可以使用的一种方法是string_split() 来查找每个字符串中的最后一个“单词”:

with p as
      select p.*, s.value as p_name
      from product p cross apply
           (select top (1) s.value
            from string_split(p.names1, ' ') s
            where p.names1 like '% ' + s.value
           ) s
     ),
     c as (
      select c.*, s.value as c_name
      from customer c cross apply
           (select top (1) s.value
            from string_split(c.names2, ' ') s
            where c.names2 like '% ' + s.value
           ) s
     )
select . . .
from p join
     c
     on p.p_name = c.c_name;

还有其他方法——但 SQL Server 的字符串处理能力很差。你真的应该重新考虑你的数据模型,因为数据的呈现方式似乎有问题。

例如,一个没有子查询的相对简单的方法使用名称的反向来匹配:

select . . .
from products p join
     customers c
     on right(p.name1, charindex(' ', reverse(p.name1) + ' ')) = 
        right(c.name2, chardindex(' ', reverse(c.name1) + ' '))

【讨论】:

【参考方案2】:

查询下方的用户(它将忽略该数字之前的任何前导 (")。在 'ad"dfk "01645"' 中,这将首先忽略 ("),因为它后面没有数字):

select * from product p inner join entities c
on p.numbers=cast(trim(substring(substring(c.id,patindex('%"[0-9]%',c.id)+1,len(c.id)),1,charindex('"',substring(c.id,patindex('%"[0-9]%',c.id)+1,len(c.id)))-1))  as float)

将实体 ID 字段中的数字与产品的数字字段匹配:

select * from product p inner join entities c
on p.numbers=trim(substring(substring(c.id,charindex('"',c.id)+1,len(c.id)),1,charindex('"',substring(c.id,charindex('"',c.id)+1,len(c.id)))-1)) 

请确保所有记录都满足此条件。并特别注意字符串中的 ' '。请试试这个,让我知道您的反馈。

      select * from product p inner join entities c on 
p.[NAMES1] = concat(trim(left(trim(c.[ID]),
(case when charindex(' Alias',trim(c.[ID]))>0 then charindex(' Alias',trim(c.[ID])) when charindex(' Also known as',trim(c.[ID]))>0 then charindex(' Also known as',trim(c.[ID])) end)))
 ,' ',trim(right(trim(c.NAMES2),charindex(' ',reverse(trim(c.NAMES2))))))

在 case 语句中添加任何替代“别名”:

    (case when charindex(' Alias',trim(c.[ID]))>0 then charindex(' Alias',trim(c.[ID])) when charindex(' Also known as',trim(c.[ID]))>0 then charindex(' Also known as',trim(c.[ID])) 
when charindex(' Good Alias',trim(c.[ID]))>0 then charindex(' Good Alias',trim(c.[ID]))end)

从您的单行示例看来,您只需在 id 字段中将 'Alias:' 替换为 '' 就可以做同样的事情

select * from PRODUCT p inner join CUSTOMER c on
p.NAMES1=replace(c.ID,'Alias: ','')

【讨论】:

非常抱歉。错字。我写的不是“在”,而是“在哪里”。我已经改变了答案。 ID 中有没有子字符串“别名”的值。因此 charindex('Alias',trim(c.[ID]))-1 返回 -1 ,这是 left() 的无效参数。我已经改变了我的答案来解决这个问题。请告诉我这两个表中有多少行数据? 需要解决每一个异常。费时,但如果没有其他选择,我们可以到达那里。 你考虑过 replace() 选项吗?满足条件会更快。 您可以将表格中的这三列提供给我,如果您觉得可以,我可以试一试。但肯定不会正确匹配所有行。【参考方案3】:

这就是我的做法。

--==== Easily Consumable sample data (take note)
DECLARE @t TABLE (NAMES2 VARCHAR(100), ID VARCHAR(100));
INSERT @t VALUES 
  ('9999 10% Walter', 'White Alias: Heisenburg ","2021991010",'),
  ('2247 50% James', 'McCarthy Petersson Alias: James ","200874741",');

--==== Solution
SELECT tName = f2.tName
FROM        @t AS t
CROSS APPLY (VALUES(PATINDEX('%[a-zA-z]%', t.NAMES2),
                    CHARINDEX(' Alias:', t.ID))) AS f(Pos1, Pos2)
CROSS APPLY (VALUES(CONCAT(SUBSTRING(t.Names2, f.Pos1, 100),' ',
                           SUBSTRING(t.ID, 1, f.Pos2)))) AS f2(tName)
--JOIN        dbo.thatOtherTable AS ot
--  ON        ot.tName = f2.tName

请注意,加入已被注释掉,但应该会有所帮助。这原样返回:

tName
----------------------------
Walter White 
James McCarthy Petersson 

【讨论】:

以上是关于SQL Server:提取特定字符串,然后加入两个表的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server实现将特定字符串拆分并进行插入操作的方法

SQL - 加入 t1.value 与 t2.column 名称匹配的表?

SQL Server中如何将特定形式字符串转换为时间格式。并将该时间进行加减

SQL Server 从字符串中提取中文英文数字

从包含十进制 SQL Server 18 的字符串字段中提取数字

SQL Server:在某个字符之前提取所有内容