SQL 服务器 - 基于 ASCII 值的 CHARINDEX

SQL Server - CHARINDEX on ASCII values

我在 SQL 服务器 table 上有一个列,该列的值很长且带有换行符。我试图在每个换行符之前找到值。

示例:

--Column name: ItemDescription

Case Qty: 12
Weight: 8 oz.
Flavor code: PB
Size: STOCK
Cut: 1/8" x 1/8" x 3/16"
Additions: Bells
Cover Brine #: P1
Kosher Cert: OU
Organic Cert: 

这正是我复制结果中的单元格并粘贴时得到的结果。所以我将这个字段转换为 VARBINARY 并看到这里有什么 ASCII 代码。这是一个值的 ASCII 解释的一部分:

43 61 73 65 20 51 74 79 3A 20 31 32 0D0A 57 65 69 67 68 74 3A 20 38 20 6F 7A 2E 0D0A 46

0D0A 表示回车 return 和换行。

首选结果:

现在数据已经清楚了,我正在尝试找到冒号之后和换行符之前的值并将其放入新列中。

所以这是我希望的结果应该是这样的:

我尝试了什么:

这是我当前的 SQL 查询:

DECLARE @firstLine int
DECLARE @secondLine int
DECLARE @thirdLine int
DECLARE @fourthLine int
DECLARE @fifthLine int
DECLARE @sixthLine int
DECLARE @seventhLine int
DECLARE @eighthLine int


DECLARE @firstColon int
DECLARE @secondColon int
DECLARE @thirdColon int
DECLARE @fourthColon int
DECLARE @fifthColon int
DECLARE @sixthColon int
DECLARE @seventhColon int
DECLARE @eighthColon int
DECLARE @ninethColon int
DECLARE @itemDesc varchar(MAX)

SELECT 
    @itemDesc = ItemDescription
    ,@firstLine = CHARINDEX(CHAR(13), ItemDescription, 1)
    ,@secondLine = CHARINDEX(CHAR(13), ItemDescription, @firstLine + 1)
    ,@thirdLine =  CHARINDEX(CHAR(13), ItemDescription, @secondLine + 1)
    ,@fourthLine =  CHARINDEX(CHAR(13), ItemDescription, @thirdLine + 1)
    ,@fifthLine =  CHARINDEX(CHAR(13), ItemDescription, @fourthLine+ 1)
    ,@sixthLine =  CHARINDEX(CHAR(13), ItemDescription, @fifthLine + 1)
    ,@seventhLine =  CHARINDEX(CHAR(13), ItemDescription, @sixthLine + 1)
    ,@eighthLine = CHARINDEX(CHAR(13), ItemDescription, @seventhLine + 1)

    ,@firstColon = CHARINDEX(CHAR(58), ItemDescription, 1)--aaa
    ,@secondColon = CHARINDEX(CHAR(58), ItemDescription, @firstLine + 1)
    ,@thirdColon = CHARINDEX(CHAR(58), ItemDescription, @secondLine + 1)
    ,@fourthColon = CHARINDEX(CHAR(58), ItemDescription, @thirdLine + 1)
    ,@fifthColon = CHARINDEX(CHAR(58), ItemDescription, @fourthLine + 1)
    ,@sixthColon = CHARINDEX(CHAR(58), ItemDescription, @fifthLine + 1)
    ,@seventhColon = CHARINDEX(CHAR(58), ItemDescription, @sixthLine + 1)
    ,@eighthColon = CHARINDEX(CHAR(58), ItemDescription, @seventhLine + 1)
    ,@ninethColon = CHARINDEX(CHAR(58), ItemDescription, @eighthLine + 1)
FROM TableName

SELECT 
    ItemDescription
    ,CONVERT(VarBInary, itemDescription)
    ,LTRIM(SUBSTRING(ItemDescription, @firstColon + 2, @firstLine - (@firstColon - 1))) as caseQty          --1
    ,LTRIM(SUBSTRING(ItemDescription, @secondColon + 2, @secondLine - (@secondColon - 1))) as caseQty           --2
    ,LTRIM(SUBSTRING(ItemDescription, @thirdColon + 2, @thirdLine - (@thirdColon - 1))) as FlavorCode
    ,LTRIM(SUBSTRING(ItemDescription, @fourthColon + 2, @fourthLine - (@fourthColon - 1))) as Size
    ,LTRIM(SUBSTRING(ItemDescription, @fifthColon + 2, @fifthLine - (@fifthColon - 1))) as Cut
    ,LTRIM(SUBSTRING(ItemDescription, @sixthColon + 2, @sixthLine - (@sixthColon - 1))) as Additions
    ,LTRIM(SUBSTRING(ItemDescription, @eighthColon + 2, @eighthLine- (@eighthColon - 1))) as Brine
FROM
    TableName

问题:

出于某种原因,SUBSTRING 未获得正确的子字符串!我得到 QtyWeight 正确。但是如果大小是 RELISHSTOCK,我得到 RELISHSTOC。对于 FlavorCode,我得到 ut:("Cut:" 的子字符串)。对于 Cut,我得到 8" x 3/。对于 Additions,我得到 Brin("Cover Brine" 的子字符串)。

我做错了什么!!我尝试了多种不同的子串长度组合。我知道这是长度。但是现在我意识到风味代码搞砸后的所有起始位置,我不太确定错误在哪里。

如有任何帮助,我们将不胜感激。

谢谢。

经过大约一周的时间来解决这个问题,并尝试将我的执行想法转移到评论中提到的建议中,我最终找到了解决这个问题的方法。

第 1 步:
我发现操作像 ! 这样的特殊字符比换行和换行(Char(10)Char(13))更容易。
所以我做的第一件事就是将所有换行符替换为!。像这样:

REPLACE(REPLACE(columnName, char(10), ''), char(13), '!')

这确保 ! 前面总是有一个 space,使其更容易处理。

第 2 步:
我在 SO 上找到了一个答案,它有一个 table-value 函数来拆分字符串。函数名称是 fnSplitString,它有两个参数:@string nvarchar(MAX)@delimited char(1).

ALTER FUNCTION [dbo].[fnSplitString] 
( 
    @string NVARCHAR(MAX), 
    @delimiter CHAR(1) 
) 
RETURNS @output TABLE(columnName NVARCHAR(MAX), outValue NVARCHAR(MAX)) 
BEGIN 
    DECLARE 
        @start INT
        ,@end INT
        ,@colon INT

    SELECT 
        @start = 1
        ,@colon = CHARINDEX(@delimiter, @string)
        ,@end = CHARINDEX(CHAR(33), @string)
    WHILE @start < LEN(@string) + 1 BEGIN 
        IF(SUBSTRING(@string, @colon + 1, 1) = ' ')
        BEGIN
            INSERT INTO 
                @output (columnName, outValue)  
            VALUES
                ((SUBSTRING(@string, @start, @colon - @start)) ,(SUBSTRING(@string, @colon + 2,  @end - @colon - 2)))
            SET @start = @end + 1 
            SET @colon = CHARINDEX(@delimiter, @string, @start)
            SET @end = CHARINDEX(CHAR(33), @string, @start)
        END
        ELSE
        BEGIN
            INSERT INTO 
                @output (columnName, outValue)  
            VALUES
                ((SUBSTRING(@string, @start, @colon - @start)) ,(SUBSTRING(@string, @colon + 1,  @end - @colon - 1)))
            SET @start = @end + 1 
            SET @colon = CHARINDEX(@delimiter, @string, @start)
            SET @end = CHARINDEX(CHAR(33), @string, @start)
        END
    END 
    RETURN 
END

我不得不对该函数进行大量更改,因为该函数最初用于获取分隔符之后直至字符串末尾的值,这非常简单。但是我的必须从分隔符转到 !,即 char(33)

第 3 步:
我编写了一个存储过程来处理所有这些操作。我已经为我的要求准备了一个相当冗长的存储过程,我必须将此函数的结果与 select 语句的结果一起添加到新的 CTE 中,并在最终结果集中使用它。

对于任何好奇的人,在另一个 SO 问题中发现 table 和函数的连接可以像这样完成:

SELECT
    table1.columnName1
    ,function1.columnName2
    ,function1.columnName3
FROM
    tableName1 table1
CROSS APPLY
    dbo.functionName1(table1.columnName2, ':') function1

做到了。

我希望这对以后的人有所帮助。