LEFT 用于子查询

LEFT used in subquery

我的问题很简单。下面查询returns错误:

Mensagem 537, Level 16, state 2, Line 1
Invalid length parameter passed to the LEFT or SUBSTRING function..

代码:

Select  
  c.name "Nome Conta",
  c.code "Código Conta",
  sq1.[Nome Conta] Tipo

From
  (chart_tag ct inner join chart c on ct.id_chart = c.id_chart) inner join
        (Select  
            c1.name "Nome Conta",
            LEFT(c1.code, (charindex('00', c1.code)-1)) "Código Conta"

         from chart_tag ct1 inner join chart c1 on ct1.id_chart = c1.id_chart

         where ct1.id_tag = 18159 and
               c1.id_type_chart = 1942 and
               c1.only_accrual = 1 and 
               c1.code not in ('99', '98')) sq1 on LEFT(c.code, 1) = sq1.[Código Conta]

Where
  ct.id_tag = 18159 and
  c.id_type_chart = 1942 and
  c.only_accrual = 0

order by
  c.code

第一个内连接returns以下table:

Name Code
Caixa Geral 1111001
Caixa Departamentos/Operador 1111005
Valores Recebidos a Depositar 1111025
Bancos Conta Movimento 1112001
Bancos Conta Movimento - Vincul 1112005
Bancos Conta Subvenções 1112021
Bancos Conta Doações 1112022
Bancos Conta Contribuições 1112023
Aplicações Financeiras Imediata 1113001
Aplicações Financeiras Vinc. 1113005

单独执行From子查询没有返回错误。生成如下table:

Name code
ATIVO 1
ATIVO CIRCULANTE 11
CAIXA E EQUIVALENTES 111
CAIXA 1111
BANCOS 1112
APLICAÇÕES FINANCEIRAS 1113

来自

Name code
ATIVO 1000000
ATIVO CIRCULANTE 1100000
CAIXA E EQUIVALENTES 1110000
CAIXA 1111000
BANCOS 1112000
APLICAÇÕES FINANCEIRAS 1113000

子查询:

Select  
  c1.name "Nome Conta",
  LEFT(c1.code, (charindex('00', c1.code)-1)) "Código Conta"
    
from
  chart_tag ct1 inner join chart c1 on ct1.id_chart = c1.id_chart
    
where
  ct1.id_tag = 18159 and
  c1.id_type_chart = 1942 and
  c1.only_accrual = 1 and 
  c1.code not in ('99', '98')

更改主要逻辑,即去掉“code”列右边的'0',使用replace 查询正常执行,返回预期结果,除了一些偏离的行模式,在字符串的中间包含'0',而不是恰到好处。因此,我想我会使用返回错误的当前逻辑。知道这一点,错误所指的LEFT是子查询的LEFT,但是,如前所述,由于它是一个孤立的子查询,不依赖于外部因素,我不明白这个错误的原因。

那么,调试方法如下:

  • 不使用 LEFT 时,不会出现错误
  • 报错提示传给LEFT的长度无效
  • LEFT 函数有问题
  • 长度作为第二个参数传递

c1.code 替换为某个值:

Select  
  LEFT('100', (charindex('00', '100')-1)) "Código Conta"

有效,给你 1

但是.. 如果在值中找不到您要查找的字符串怎么办?

Select  
  LEFT('199', (charindex('00', '199')-1)) "Código Conta"

“无效长度...”错误

那么我们要经过多长时间?仅提取长度计算:

Select  
  charindex('00', '199')-1)
  --LEFT('199', (charindex('00', '199')-1)) "Código Conta"

它给出 -1,而 -1 不是要从某物左侧删除的有效字符数...

大海捞针找不到,你想怎么办?也许在没有子字符串的情况下获取整个字符串?或者调整 WHERE 子句以仅允许以 00 结尾的代码,以便查找始终有效?

你决定.. 如果找不到“00”,这将为你提供全部信息:

Select  
  LEFT(
    c1.code, 
    COALESCE(
      NULLIF(
        charindex('00', c1.code)-1,
        -1
      ),
      LEN(c1.Code)
    )
  ) "Código Conta"

如果搜索 returns -1 表示“未找到”,NULLIF 将 -1 转换为 null,然后 COALESCE 转换null 到字符串的 LEN,即 LEFT returns 整个

如果找不到字符串,这将给出空字符串;唯一的区别是在合并中使用 0 - 它要求 LEFT 给出最左边的 0 个字符:

Select  
  LEFT(
    c1.code, 
    COALESCE(
      NULLIF(
        charindex('00', c1.code)-1,
        -1
      ),
      0
    )
  ) "Código Conta"

或者,如前所述,使用 WHERE c1.code LIKE '%00%' 确保您只查看确实包含 00 的代码

如果没有子字符串'00'

LEFT(c1.code, (charindex('00', c1.code)-1))

会报错,因为 return 0 和 -1 无效

这会起作用:

CASE WHEN charindex('00', c1.code) > 0 
     THEN LEFT(c1.code, (charindex('00', c1.code)-1))
     ELSE c1.code
END

这也适用:

LEFT(c1.code+'00', (charindex('00', c1.code+'00')-1))

我相信第二个会更快,但它可能不是基于您的数据配置文件,所以如果性能真的很重要,请测试两个。

顺便说一下,如果您只是想从数字中删除尾随的 0,您是否考虑过:

REPLACE((RTRIM(REPLACE(c1.code, '0', ' ')),' ', '0')

将所有的 0 改成空格,TRIM,并将剩余的空格改回来..

给猫剥皮的方法会更多;

REVERSE(CAST(REVERSE(c1.code) as INT))

(不过一般认为reverse有点nasty/slow)等..