【问题标题】:SQL Server - CHARINDEX on ASCII valuesSQL Server - ASCII 值上的 CHARINDEX
【发布时间】:2018-05-29 18:32:52
【问题描述】:

我在 SQL Server 表中有一个列,该列的值很长,带有换行符。我试图在每个换行符之前找到值。

示例:

--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 表示回车和换行。

首选结果:

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

这就是我想要的结果:

我的尝试:

这是我当前执行此操作的 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,我得到8" x 3/。对于Additions,我得到Brin(“Cover Brine”的子字符串)。

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

任何帮助将不胜感激。

谢谢。

【问题讨论】:

  • T-SQL 不适合字符串操作,更不用说解析了。如果您想存储复杂的数据,请将它们存储为 XML 或 Json。在加载/插入数据时,使用 C# 之类的语言解析数据会更容易很多
  • @PanagiotisKanavos 看,就是这样。这张表中的数据很简单,只用 T-SQL 就可以搞定。我在 XML 方面很糟糕,甚至不想为自己打开那罐蠕虫。但这些数据不会去任何地方。它只是去一个 SSRS 报告。所以我想我可以让它像这样工作。
  • 另外,如果你已经知道数据会有特定的字段,为什么不首先为它创建一个表呢?在 C# 中使用正则表达式解析字符串非常容易。提取键/值对并将它们作为字段值插入到表中
  • 问题是您的数据违反了 1NF,将多条数据存储在一个元组中。通过在单个元组中存储多行多个数据点,您的更进一步。您有一个 EAV 类型模式存储在单行中。这是处理sql的噩梦。
  • 一种可能的解决方法(不是解决方案,真正的解决方案是改变加载数据的方式并直接将其插入到 中)是将其转换为 JSON,然后使用 SQL Server 的 JSON 函数提取属性.例如,用',' 替换每个换行符,用' : ' 替换每个: 并用'{''} 包装所有内容,这会使它看起来像JSON。然后您可以使用JSON_QUERY 来读取属性。不过,这将是 slow。修复数据

标签: sql-server select substring ascii charindex


【解决方案1】:

在解决这个问题大约一周后,我尝试将我的执行想法转移到 cmets 中提到的建议上,最后我找到了解决这个问题的方法。

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

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

这样可以确保! 前面始终有一个空格,从而更容易处理。

第 2 步:
我在 SO 上找到了一个答案,它有一个表值函数来拆分字符串。函数名称为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 问题中发现表和函数的连接可以这样完成:

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

做到了。

我希望这对将来的某人有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-13
    • 2014-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多