Sql 提取嵌入在复合代码字符串中的特定列值

Sql 提取嵌入在复合代码字符串中的特定列值,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,我正在尝试在SQLServer2008中创建一段代码,它将从我的dbo表中的每个不同字符串中获取特定值。最终目标是在Visual Studio中创建一个下拉框,以便可以从数据库中选择包含特定产品代码的所有行(请参见下面的产品代码定义)。示例字符串: in_0314_95pf_500_w_0315 in_0314_500_95pf_0315_w 我希望识别的这些字符串的一部分是在每个字符串中出现一次的3位数字代码(在本例中,我们称之为产品代码)。大约有300种不同的产品代码 问题是这些产品代码

我正在尝试在
SQLServer2008
中创建一段代码,它将从我的
dbo
表中的每个不同字符串中获取特定值。最终目标是在Visual Studio中创建一个下拉框,以便可以从数据库中选择包含特定产品代码的所有行(请参见下面的产品代码定义)。示例字符串:

in_0314_95pf_500_w_0315

in_0314_500_95pf_0315_w
我希望识别的这些字符串的一部分是在每个字符串中出现一次的3位数字代码(在本例中,我们称之为产品代码)。大约有300种不同的产品代码

问题是这些产品代码值在每个唯一字符串中的位置不相同。因此,我很难确定产品代码,因为我不能使用substring、charindex等


有什么想法吗?非常感谢您的帮助。

一种方法是使用字符串拆分表,将字符串拆分为其组件。然后,您可以根据您的条件筛选组件:

SELECT Name
FROM dbo.splitstring('in_0314_95pf_500_w_0315', '_')
WHERE ISNUMERIC(Name) = 1 AND LEN(Name) = 3;
我稍微修改了函数,以接受分隔符作为参数

CREATE FUNCTION dbo.splitstring ( @stringToSplit VARCHAR(MAX), @delimiter VARCHAR(50))
RETURNS
 @returnList TABLE ([Name] [nvarchar] (500))
AS
BEGIN

 DECLARE @name NVARCHAR(255)
 DECLARE @pos INT

 WHILE CHARINDEX(@delimiter, @stringToSplit) > 0
 BEGIN
  SELECT @pos  = CHARINDEX(@delimiter, @stringToSplit)  
  SELECT @name = SUBSTRING(@stringToSplit, len(@delimiter), @pos-len(@delimiter))

  INSERT INTO @returnList 
  SELECT @name

  SELECT @stringToSplit = SUBSTRING(@stringToSplit, @pos+LEN(@delimiter), 
         LEN(@stringToSplit)-@pos)
 END
INSERT INTO @returnList
 SELECT @stringToSplit

 RETURN
END
要将其应用于表,请使用
交叉应用
(单个分隔符):

更新,多个分隔符

我想真正的根本问题是,最终产品代码需要从复合键中规范化(例如,向同一个表中添加一个不同的
ProductId
ProductCode
列),使用类似这样的查询派生,然后通过
更新
存储回表中。从字符串中提取产品代码的逆向工程似乎是一个反复试验的过程

尽管如此,在应用最终区分过滤器之前,您可以继续通过进一步的拆分函数(每种类型的分隔符一个)传递拆分字符串:

SELECT *
FROM MyTable mt
CROSS APPLY dbo.splitstring(mt.Name, 'test') y -- First alias
      CROSS APPLY dbo.splitstring(y.Name, '_') x -- Reference the preceding alias
WHERE ISNUMERIC(x.Name) = 1 AND LEN(x.Name) = 3; -- Must reference the last alias (x)
请注意,stringsplit函数已再次更改,以适应多字符分隔符。

如果您有产品代码表(或可以在内联视图中生成),则可以使用like子句将长字符串列表与产品代码连接起来

Create Table longcodes (
    longcode varchar(20)
    )

Create Table products (
    prodCode char(3)
    )

insert products values('100')
insert products values('111')
insert products values('123')

insert longcodes values ('abc_a_100_test')
insert longcodes values ('asdf_111_bob')
insert longcodes values ('in_0314_123_95pf')
insert longcodes values ('f_100_u')
insert longcodes values ('hihi_111_bye')
insert longcodes values ('in_123_0314_95pf')
insert longcodes values ('a_b__c_d_100_efg')

select * 
from products p 
     join longcodes l on l.longcode like '%_' + p.prodCode + '_%'
它们与以下产品代码保持一致:

prodCode    longcode
100         abc_a_100_test
100         f_100_u
100         a_b__c_d_100_efg
111         asdf_111_bob
111         hihi_111_bye
123         in_0314_123_95pf
123         in_123_0314_95pf
编辑:看到另一个答案中的发展,您可以将like子句简化为

like p.prodCode

只需处理一个复合字符串产生多个匹配项的可能性更大的事实。

这可以通过
PATINDEX
实现:

DECLARE @s NVARCHAR(100) = 'in_0314_95pf_500_w_0315'
SELECT SUBSTRING(@s, PATINDEX('%[_][0-9][0-9][0-9][_]%', @s) + 1, 3)
输出:

500
如果没有下划线,则:

SELECT SUBSTRING(@s, PATINDEX('%[^0-9][0-9][0-9][0-9][^0-9]%', @s) + 1, 3)
这意味着非数字符号之间有3位数字

编辑:

适用于以下表格:

SELECT SUBSTRING(ColumnName, PATINDEX('%[^0-9][0-9][0-9][0-9][^0-9]%', ColumnName) + 1, 3)
FROM TableName

如果它总是在2 _之间的3位数字,并且可以安全地判断没有其他具有此模式的产品代码,那么您可以使用patindex来执行此操作您是否有一个包含所有有效产品代码的表?在您的示例中,如果有人搜索“500”,您会希望返回这些字符串,但如果有人搜索“314”,则不会返回这些字符串?在我的示例中,“0314”是MMYY。我的组织内的人都知道产品代码是3个数字字符。日期总是四个数字字符,所以我不认为这会是个问题……对于一个字符串来说这很好,但我认为对于很多字符串来说,它的性能会很慢。@Zoharpeld当然可以,但OP可以运行上述操作,因为一次过的练习可以在现有产品表上创建一个新列,或者在一个由复合字符串产品键控的新表上,这将为将来的查询提供服务。当然,真正的问题是使用复合键,而组件应该被规范化。我同意,如果OP可以控制表的结构。最好只接受一次字符串拆分,并以规范化的方式存储数据。@StuartLC感谢您的帮助!一旦我在我的表中输入信息和列名为:select mt.DSIN,x.name作为ProductCode from dbo.CM_Summary mt CROSS APPLY dbo.splitstring(mt.DSIN,'.'''''x'),其中ISNUMERIC(x.name)=1和LEN(x.name)=3,我得到以下错误:“Msg 8144,级别16,状态3,第3行过程或函数dbo.splitstring指定的参数太多。“你能帮忙吗?谢谢非常感谢你的帮助!感谢Giorgi的回复,但是您如何将其应用于需要识别数据库每行中产品代码的更大数据集?谢谢@AdamMeyer,我为表添加了语句
SELECT SUBSTRING(ColumnName, PATINDEX('%[^0-9][0-9][0-9][0-9][^0-9]%', ColumnName) + 1, 3)
FROM TableName