如何将单独列中冒号前后的单词拆分为sql中的行

Posted

技术标签:

【中文标题】如何将单独列中冒号前后的单词拆分为sql中的行【英文标题】:how to split words before and after colon in separate columns to rows in sql 【发布时间】:2019-05-30 15:39:12 【问题描述】:

我在表中有一个列 [LongText],它的行值是所有属性及其值的合并。下面是示例。 我们可以将单词前后的冒号分成两列,如预期结果所示?在 sql 2014 中需要它

Longtext

TYPE: SOLID WEDGE 1,SOLID WEDGE 2,  VALVE SIZE: 1 IN,  PRESSURE RATING: 800 LB,  CONNECTION TYPE: SOCKET WELD,  BONNET STYLE: BOLTED

预期输出为 2 列属性和值:

Attribute        | Value
----------------------------------------------
TYPE             | SOLID WEDGE 1,SOLID WEDGE 2
VALVE SIZE       | 1 IN
PRESSURE RATING  | 800 LB
CONNECTION TYPE  | SOCKET WELD
BONNET STYLE     | BOLTED

【问题讨论】:

处理这个问题的一个好方法是通过正则表达式。不幸的是,SQL Server 对正则表达式的支持很弱,所以它不是进行这种清理的最佳场所。在将数据导入 SQL Server 之前,您是否有机会使用其他工具清理数据? 你好,蒂姆,你能举一些正则表达式的例子吗 如果这是一次性数据加载,您可以编写一个 while 循环,从字符串中删除第一个键/值对,将它们写入新表并继续直到字符串空的。或者,使用其他一些具有丰富正则表达式功能的编程语言来完成这项工作。 (如果这不是一次性数据加载,我不建议使用循环) STRING_SPLIT() 可能有用.. (docs.microsoft.com/en-us/sql/t-sql/functions/…) @Sai 你在回答部分检查了我的代码吗? 【参考方案1】:

看看这是否有帮助。另外,如果您希望我解释代码,请告诉我。

IF EXISTS(SELECT 1 FROM SYS.OBJECTS WHERE NAME = 'fn_GetAttributeAndValueFromLongText' AND TYPE = 'TF')
BEGIN
    DROP FUNCTION dbo.fn_GetAttributeAndValueFromLongText
END
GO

CREATE FUNCTION dbo.fn_GetAttributeAndValueFromLongText ( @String_LongText VARCHAR(MAX) )
RETURNS @TBL_Attribute_Value TABLE 
(
    Attribute VARCHAR(MAX)
  , Value VARCHAR(MAX)
)
AS
BEGIN

    DECLARE @ATTR_VALUE_DELIMITER AS VARCHAR(MAX) = ':'

    WHILE (RTRIM(LTRIM(LEN(@String_LongText))) != 0)
    BEGIN
        DECLARE @String_ATTR VARCHAR(MAX)='', @String_VALUE VARCHAR(MAX)= ''

        SELECT @String_LongText = RTRIM(LTRIM(@String_LongText))

        SELECT @String_ATTR = SUBSTRING(@String_LongText, 1,CHARINDEX(':',@String_LongText)-1)
        SELECT @String_LongText = RIGHT(@String_LongText, LEN(@String_LongText)-(LEN(@String_ATTR)+1))

        IF @String_LongText LIKE '%'+@ATTR_VALUE_DELIMITER+'%'
        BEGIN
            SELECT @String_VALUE = LEFT(SUBSTRING(@String_LongText, 1, CHARINDEX(':', @String_LongText)-1), LEN(SUBSTRING(@String_LongText, 1, CHARINDEX(':', @String_LongText)-1)) - CHARINDEX(',', REVERSE(SUBSTRING(@String_LongText, 1, CHARINDEX(':', @String_LongText)-1))))
            SELECT @String_LongText = RIGHT(@String_LongText, LEN(@String_LongText)-(LEN(@String_VALUE)+1))
        END
        ELSE
        BEGIN
            SELECT @String_VALUE = @String_LongText
            SELECT @String_LongText = REPLACE(@String_LongText, @String_VALUE, '')
        END

        INSERT INTO @TBL_Attribute_Value ([Attribute], [Value])
        VALUES(RTRIM(LTRIM(@String_ATTR)), RTRIM(LTRIM(@String_VALUE))) 
    END

    RETURN
END

GO 

SELECT * FROM dbo.fn_GetAttributeAndValueFromLongText('TYPE: SOLID WEDGE 1,SOLID WEDGE 2,  VALVE SIZE: 1 IN,  PRESSURE RATING: 800 LB,  CONNECTION TYPE: SOCKET WELD,  BONNET STYLE: BOLTED')

【讨论】:

【参考方案2】:

开始使用 STRING_SPLIT(),类似于:

DECLARE @string varchar(max) = 'TYPE: SOLID WEDGE 1,SOLID WEDGE 2,  VALVE SIZE: 1 IN,  PRESSURE RATING: 800 LB .....';
DECLARE @output varchar(max) = '';
DECLARE @v varchar(max) = (SELECT TOP(1) value from string_split(@string,' '));
WHILE @v <> ''
BEGIN
    select @v;
    SET @string = (SELECT ltrim(substring(@string,LEN(@v)+1,1024)));
    select @string;
    SET @v = (SELECT TOP(1) value from string_split(@string,' '));
END

给出:

正如@Tim 所说,不能保证上述方法的顺序是正确的。

所以,第二次尝试?:

DECLARE @string varchar(max) = 'TYPE: SOLID WEDGE 1,SOLID WEDGE 2,  VALVE SIZE: 1 IN,  PRESSURE RATING: 800 LB,  CONNECTION TYPE: SOCKET WELD,  BONNET STYLE: BOLTED';
SELECT * FROM string_split(REPLACE(@string,'  ','#'),'#');

这希望(='does-not-check')原始字符串中没有“#”字符。

【讨论】:

这个答案可能无法正常工作,因为STRING_SPLIT不保证会保持单词的顺序。

以上是关于如何将单独列中冒号前后的单词拆分为sql中的行的主要内容,如果未能解决你的问题,请参考以下文章

将列中逗号分隔的字符串拆分为单独的行

如何将存储在列中的 JSON 数组中的每个元素的行拆分为一行?

如何在 BigQuery SQL 中将字符串列拆分为多行单个单词和单词对?

(Oracle) SQL 中的正则表达式将日期/时间拆分为单独的日期和时间列

SQL 将列值拆分为 Netezza 中的行

将一行拆分为三个单独的行