我需要检查ACC_1的ACCT_NUMS值。如果ACCT_NUM以“GF0”为前缀,那么我需要忽略“GF0”前缀并取出剩余字符串的最右边7个字符。如果在account_x_master或CW_CLIENT_STAGE中找不到此结果值,则该记录将被标记为错误。
以下似乎可以解决问题,但我有一个问题......
UPDATE
table_1
SET
Error_Ind = 'GW001'
WHERE
LEFT(ACCT_NUM, 3) = 'GF0'
AND RIGHT(SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3), 7) NOT IN
(
SELECT
acct_num
FROM
account_x_master
)
AND RIGHT(SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3), 7) NOT IN
(
SELECT
CW_CLIENT_STAGE.AGS_NUM
FROM
dbo.CW_CLIENT_STAGE
)
我担心的是SQL Server可能会尝试执行SUBSTRING操作
SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3)
导致计算出的负值并导致SQL失败。当然,这不会失败是SUBSTRING操作只应用于那些我们至少3个字符长的记录,如果
那将永远如此LEFT(ACCT_NUM, 3) = 'GF0'
首先应用。如果可能的话,我想避免在表格中添加新列。简化和减少开销的奖励点: - )
如何重写此UPDATE SQL以防止此问题?
答案 0 :(得分:2)
您有一个非常有效的顾虑,因为SQL Server将重新排列WHERE
中表达式的评估顺序。
保证SQL语句中操作顺序的唯一方法是使用case
。我认为没有办法捕捉substring()
失败的电话。 。 。没有try_substring()
类似于try_convert()
。
所以:
WHERE
LEFT(ACCT_NUM, 3) = 'GF0' AND
(CASE WHEN LEN(ACCT_NUM) > 3 THEN RIGHT(SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3), 7) END) NOT IN (SELECT acct_num
FROM account_x_master
) AND
(CASE WHEN LEN(ACCT_NUM) > 3 THEN RIGHT(SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3), 7) END) NOT IN (SELECT CW_CLIENT_STAGE.AGS_NUM
FROM dbo.CW_CLIENT_STAGE
)
这是更加丑陋的。并且,可能存在各种方法,例如使用带有通配符的LIKE
而不是字符串操作。但是,case
将保证SUBSTRING()
仅在字符串上运行足够长的时间,因此不会产生错误。
答案 1 :(得分:2)
正如其他人所说,你的担忧是有效的。
我会对您的查询进行两处更改。
1)为了避免在SUBSTRING
参数中出现负值,我们可以使用STUFF
重写它:
SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3)
相当于:
STUFF(ACCT_NUM, 1, 3, '')
我们不是提取字符串的尾部,而是用空字符串替换前三个字符。如果字符串短于3个字符,则结果为空字符串。
顺便说一句,如果您的ACCT_NUM
可能以空格结尾,则SUBSTRING
版本会对其进行裁剪,因为LEN
不会计算尾随空格。
2)而不是
LEFT(ACCT_NUM, 3) = 'GF0'
使用:
ACCT_NUM LIKE 'GF0%'
如果您在ACCT_NUM
上有索引且只有相对较少的行以GF0
开头,则将使用索引。如果使用函数,例如LEFT
,则无法使用索引。
因此,最终的查询变为:
UPDATE
table_1
SET
Error_Ind = 'GW001'
WHERE
ACCT_NUM LIKE 'GF0%'
AND RIGHT(STUFF(ACCT_NUM, 1, 3, ''), 7) NOT IN
(
SELECT
acct_num
FROM
account_x_master
)
AND RIGHT(STUFF(ACCT_NUM, 1, 3, ''), 7) NOT IN
(
SELECT
CW_CLIENT_STAGE.AGS_NUM
FROM
dbo.CW_CLIENT_STAGE
)
答案 2 :(得分:1)
请尝试以下查询。
由于SQL and or
子句中没有短路WHERE
,因此只能通过CASE
语法实现。
我注意到您在NOT IN
的不同部分进行了两次WHERE
次比较,我将其合并为一次。
请注意,CASE
条件为>=3
而不是>3
,因为RIGHT('',x)
是允许的。
另请注意CASE
与NOT IN
UPDATE table_1
SET
Error_Ind = 'GW001'
select * from table_1
WHERE
LEFT(ACCT_NUM, 3) = 'GF0'
AND CASE
WHEN LEN(ACCT_NUM)>=3
THEN RIGHT(SUBSTRING(ACCT_NUM, 4, LEN(ACCT_NUM) - 3), 7)
ELSE NULL END NOT IN
(
SELECT acct_num as num
FROM account_x_master
UNION
SELECT CW_CLIENT_STAGE.AGS_NUM as num
FROM dbo.CW_CLIENT_STAGE
)