【问题标题】:Substring in nested query嵌套查询中的子字符串
【发布时间】:2021-10-08 15:10:53
【问题描述】:

我在表中有一个 nvarchar 列,将数据存储为“{CRLF}”分隔值。这些值可以是“namespace#name$value”格式,也可以是其他格式。

我需要提取分离的命名空间、名称和值,并跳过其他格式的数据。我可以通过查询来做到这一点:

select 
  SUBSTRING([value], 1, CHARINDEX('#',[value]) - 1) as paramNamespace,
  SUBSTRING([value], CHARINDEX('#',[value]) + 1, CHARINDEX('$',[value]) - CHARINDEX('#',[value]) - 1) as paramName,
  SUBSTRING([value], CHARINDEX('$',[value]) + 1, LEN([value])) as paramValue
FROM STRING_SPLIT(REPLACE((select data from test where Id = 1), '{CRLF}', CHAR(7)), CHAR(7))
WHERE [value] LIKE '%#%$%'

但是,当我尝试使用此查询按 paramName 过滤时:

select * from (select 
  SUBSTRING([value], 1, CHARINDEX('#',[value]) - 1) as paramNamespace,
  SUBSTRING([value], CHARINDEX('#',[value]) + 1, CHARINDEX('$',[value]) - CHARINDEX('#',[value]) - 1) as paramName,
  SUBSTRING([value], CHARINDEX('$',[value]) + 1, LEN([value])) as paramValue
FROM STRING_SPLIT(REPLACE((select data from test where Id = 1), '{CRLF}', CHAR(7)), CHAR(7))
WHERE [value] LIKE '%#%$%') a
where paramName IN ('name', 'name3')

返回

传递给 LEFT 或 SUBSTRING 函数的长度参数无效。

看起来 "WHERE [value] LIKE '%#%$%'" 没有首先执行,它尝试以其他格式对值进行子串化。

我应该如何编写这个查询来按 paramName 过滤它?

架构:

CREATE TABLE test (Id INT, data nvarchar(max));
INSERT INTO test VALUES (1, N'namespace#name$value{CRLF}somedatainotherformat{CRLF}namespace2#name2$value2{CRLF}namespace3#name3$value3{CRLF}');

小提琴:http://sqlfiddle.com/#!18/e5d71/1

【问题讨论】:

  • “我在表中有一个 nvarchar 列,将数据存储为 '{CRLF}' 分隔值” 这是 真正的 问题。修复您的设计,您就不会遇到此问题。永远不要在数据库中存储分隔数据。
  • 这就是我使用此查询的目的,它是存储过程的一部分,它从分隔数据中提取值并将其存储在正确的列中。
  • 那么,从您的样本数据来看,您在这里的预期结果是什么?
  • 在我的简单数据中,有 3 个格式正确的值,因此它们被分成 3 行,参数名称为“name”、“name2”和“name3”。我想过滤它,所以它只返回 2 行带有 'name' 和 'name3',但是当我添加 'where paramName IN ('name', 'name3')' 它返回错误。
  • case when charindex('#',value)

标签: sql sql-server tsql sql-server-2017


【解决方案1】:

也许PARSENAME 将数据拆分为列就是您所追求的:

CREATE TABLE test (Id int,
                   data nvarchar(MAX));
INSERT INTO test
VALUES (1, N'namespace#name$value{CRLF}somedatainotherformat{CRLF}namespace2#name2$value2{CRLF}namespace3#name3$value3{CRLF}');
GO
SELECT *
FROM dbo.test t
     CROSS APPLY STRING_SPLIT(REPLACE(t.data,'{CRLF}','|'),'|') SS
     CROSS APPLY (VALUES(PARSENAME(TRANSLATE(SS.[value],'$#','..'),3),PARSENAME(TRANSLATE(SS.[value],'$#','..'),2),PARSENAME(TRANSLATE(SS.[value],'$#','..'),1)))V(paramNamespace, paramName, paramValue)
WHERE V.paramName IN ('name', 'name3')
GO

DROP TABLE test;

【讨论】:

  • 不幸的是,我不能保证数据中不会有任何点,尤其是在命名空间中。此外,可能有一些点分隔数据会产生误报结果。这就是为什么我不能使用 PARSENAME,但我将您的方法与我的查询中的子字符串结合起来,它返回了预期的结果。
  • 然后用其他东西替换这些点,然后再将它们替换回来,@grzegorzorwat。我只能根据您提供的样本解决方案。
【解决方案2】:

这是一种选择:

Select paramNamespace = substring(v.data, 1, p1.pos - 2)
     , paramName = substring(v.data, p1.pos, p2.pos - p1.pos - 1)
     , paramValue = substring(v.data, p2.pos, p3.pos - p2.pos - 1)
 From test As t
Cross Apply string_split(replace(t.data, '{CRLF}', '|'), '|') As s
Cross Apply (Values (concat(s.value, '#$'))) As v(data)
Cross Apply (Values (charindex('#', v.data, 1) + 1)) As p1(pos)
Cross Apply (Values (charindex('$', v.data, p1.pos) + 1)) As p2(pos)
Cross Apply (Values (charindex('#', v.data, p2.pos) + 1)) As p3(pos)  --end of set
Where p2.pos - p1.pos > 1;

我们将分隔符连接到末尾 - 然后过滤掉分隔符之间的差异大于 1 的任何行。因此,任何只有 '#$' 的行或以 '#$' 开头的行或有一些不包括任何一个分隔符的其他值。

如果您的数据可以有任何一个分隔符,但不能有另一个分隔符,则需要以其他方式将其过滤掉。

【讨论】:

    【解决方案3】:

    我将 Larnu 的查询与我的合并,并创建了一个查询,该查询仅以预期格式分隔数据,然后对其进行过滤:

    SELECT paramName, paramValue
    FROM STRING_SPLIT(REPLACE((select data from test where Id = 1),'{CRLF}',CHAR(7)),CHAR(7)) SS
         CROSS APPLY (VALUES(SUBSTRING([value], 1, CHARINDEX('#',[value]) - 1),
            SUBSTRING([value], CHARINDEX('#',[value]) + 1, CHARINDEX('$',[value]) - CHARINDEX('#',[value]) - 1),
            SUBSTRING([value], CHARINDEX('$',[value]) + 1, LEN([value]))))V(paramNamespace, paramName, paramValue)
    WHERE ss.value LIKE '%#%$%' and paramName IN ('name', 'name3')
    

    但我仍然不明白为什么我的问题中的子查询会产生错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-08-25
      • 2017-11-16
      • 1970-01-01
      • 1970-01-01
      • 2013-10-23
      • 2016-11-27
      • 2015-10-21
      相关资源
      最近更新 更多