IsNumeric 因 "A severe error occurred on the current command." SQL Server 2014 CTE 而失败
IsNumeric failing with "A severe error occurred on the current command." SQL Server 2014 CTE
我是运行一系列生成数据库的脚本。他们 运行 在 SQL Server 2012 (11.0.5058.0) 上完成。在 SQL Server 2014 (12.0.4213.0) 上出现脚本错误:
Msg 0, Level 11, State 0, Line 0
A severe error occurred on the current command. The results, if any, should be discarded.
Msg 0, Level 20, State 0, Line 0
A severe error occurred on the current command. The results, if any, should be discarded.
似乎在 CTE 查询中使用 IsNumeric
语句的结果会破坏查询构建,因为不需要任何行来导致错误。我 运行 案例的简化版本是:
CREATE TABLE #Temp1 ( CTECol VARCHAR );
CREATE TABLE #Temp2 ( NumCol Int null);
;
WITH cte AS
(
SELECT
CASE WHEN ISNUMERIC(t.CTECol) = 1
THEN 1
ELSE null
END as IsNCol1
FROM
#Temp1 t
)
SELECT *
FROM #Temp2
JOIN cte ON #Temp2.NumCol = cte.IsNCol1
我能找到的最简单的情况是:
CREATE TABLE #Temp3 ( CTECol Int );
CREATE TABLE #Temp4 ( NumCol Int );
;
WITH cte AS
(
SELECT ISNUMERIC(t.CTECol) as IsNCol1
FROM #Temp3 t
)
SELECT *
FROM #Temp4
JOIN cte ON #Temp4.NumCol = cte.IsNCol1
我检查了 Microsoft 的错误级别,看起来 11 是可纠正的用户错误,20 是致命错误,所以我觉得我收到的信息不一。
是否有正确的方法来做到这一点,或者它是 2014 年的倒退?
我认为这是一个错误。不过,我想出了一个可能适合您的解决方法。
;WITH cte AS
(
--SELECT
-- CASE WHEN ISNUMERIC(t.CTECol) = 1
-- THEN 1
-- ELSE null
--END as IsNCol1
SELECT CASE WHEN TRY_PARSE(t.CTECol AS INT) IS NOT NULL
THEN 1
ELSE NULL
END AS IsNCol1
FROM #Temp1 t
)
SELECT *
FROM #Temp2
JOIN cte
ON #Temp2.NumCol = cte.IsNCol1
TRY_PARSE
returns NULL
如果转换失败,那么如果它是 NOT NULL
那么你知道它是一个有效的整数。
这两个函数之间有一些细微的差别,但我更喜欢 TRY_PARSE
,因为根据 MSDN
ISNUMERIC returns 1 for some characters that are not numbers, such as
plus (+), minus (-), and valid currency symbols such as the dollar
sign ($)
更新:
我可能应该澄清其中一个细微差别。 ISNUMERIC
returns 1 如果参数可以解析为任何数值类型,它们是:
- 整数
- 数字
- bigint
- 金钱
- smallint
- 小钱
- tinyint
- 浮动
- 十进制
- 真实
它与 TRY_PARSE
不同,后者试图将输入解析为上述 特定 数据类型之一。 (在我的示例中,它 INT
)。因此,如果您真的想模仿 ISNUMERIC
,则需要为每种类型使用嵌套(或扁平化)CASE
。即使那样,行为仍然可能有些出乎意料,但这完全是另外一回事了。
这肯定是一个错误。
CTE 也不需要产生这种行为。下面直接用表达式效果一样。
SELECT *
FROM #Temp4
JOIN #Temp3
ON #Temp4.NumCol = ISNUMERIC(#Temp3.CTECol)
我可以在 12.0.2269.0 和 12.0.4213.0 上重现,但不能在 12.0.4449.0 上重现,所以看起来它现在已经修复了。
包含详细信息的相关知识库文章是 (FIX: Access violation when a query uses ISDATE or ISNUMERIC functions in Join conditions in SQL Server 2014 SP1)。
抛出异常时的堆栈跟踪如下(为了便于搜索)
KernelBase.dll!RaiseException()
msvcr100.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 157
sqldk.dll!ExceptionBackout::GetCurrentException(void)
sqldk.dll!ex_raise2(int,int,int,int,void *,char *)
sqldk.dll!ex_raise_va_list(int,int,int,int,char *)
sqllang.dll!alg_ex_raise(int,int,int,int,int,...)
sqllang.dll!CAlgTableMetadata::RaiseBadTableException(int,int)
sqllang.dll!CAlgTableMetadata::Bind(class CRelOp_Query *,class COptExpr *)
sqllang.dll!CRelOp_Get::BindTree(class COptExpr *,class CBindEnv *,int)
sqllang.dll!COptExpr::BindTree(class CBindEnv *,int)
sqllang.dll!CRelOp_FromList::BindTree(class COptExpr *,class CBindEnv *,int)
sqllang.dll!COptExpr::BindTree(class CBindEnv *,int)
sqllang.dll!CRelOp_QuerySpec::BindTree(class COptExpr *,class CBindEnv *,int)
sqllang.dll!COptExpr::BindTree(class CBindEnv *,int)
sqllang.dll!CRelOp_DerivedTable::BindTree(class COptExpr *,class CBindEnv *,int)
sqllang.dll!COptExpr::BindTree(class CBindEnv *,int)
sqllang.dll!CRelOp_Query::BindCTEList(class CBindEnv *,class COptExpr *)
sqllang.dll!CRelOp_SelectQuery::BindTree(class COptExpr *,class CBindEnv *,int)
sqllang.dll!COptExpr::BindTree(class CBindEnv *,int)
sqllang.dll!CRelOp_Query::FAlgebrizeQuery(class COptExpr *,class CCompExecCtxtStmt const &,enum EObjType,class CSequenceProjectContext *)
sqllang.dll!CProchdr::FNormQuery(class CCompExecCtxtStmt const &,class CAlgStmt *,enum EObjType)
sqllang.dll!CProchdr::FNormalizeStep(class CCompExecCtxtStmt const &,class CAlgStmt *,class CCompPlan *,bool,class CParamExchange *,unsigned long *)
sqllang.dll!CSQLSource::FCompile(class CCompExecCtxt const &,class CParamExchange *)
sqllang.dll!CSQLSource::FCompWrapper(class CCompExecCtxt const &,class CParamExchange *,enum CSQLSource::ESqlFunction)
sqllang.dll!CSQLSource::Transform(class CCompExecCtxt const &,class CParamExchange *,enum CSQLSource::ESqlState)
sqllang.dll!CSQLSource::Execute(class CCompExecCtxtBasic const &,class CParamExchange *,unsigned long)
sqllang.dll!process_request(class IBatch *,class SNI_Conn *,enum RequestType)
sqllang.dll!process_commands(void *)
sqldk.dll!SOS_Task::Param::Execute(class SOS_Task *,void * * const)
sqldk.dll!SOS_Scheduler::RunTask(class Worker *)
sqldk.dll!SOS_Scheduler::ProcessTasks(class SOS_Scheduler *,class Worker *)
sqldk.dll!SchedulerManager::WorkerEntryPoint(class Worker *)
sqldk.dll!SystemThread::RunWorker(class Worker *)
sqldk.dll!SystemThreadDispatcher::ProcessWorker(class SystemThread *)
sqldk.dll!SchedulerManager::ThreadEntryPoint(void *)
kernel32.dll!BaseThreadInitThunk()
ntdll.dll!RtlUserThreadStart()
我是运行一系列生成数据库的脚本。他们 运行 在 SQL Server 2012 (11.0.5058.0) 上完成。在 SQL Server 2014 (12.0.4213.0) 上出现脚本错误:
Msg 0, Level 11, State 0, Line 0
A severe error occurred on the current command. The results, if any, should be discarded.Msg 0, Level 20, State 0, Line 0
A severe error occurred on the current command. The results, if any, should be discarded.
似乎在 CTE 查询中使用 IsNumeric
语句的结果会破坏查询构建,因为不需要任何行来导致错误。我 运行 案例的简化版本是:
CREATE TABLE #Temp1 ( CTECol VARCHAR );
CREATE TABLE #Temp2 ( NumCol Int null);
;
WITH cte AS
(
SELECT
CASE WHEN ISNUMERIC(t.CTECol) = 1
THEN 1
ELSE null
END as IsNCol1
FROM
#Temp1 t
)
SELECT *
FROM #Temp2
JOIN cte ON #Temp2.NumCol = cte.IsNCol1
我能找到的最简单的情况是:
CREATE TABLE #Temp3 ( CTECol Int );
CREATE TABLE #Temp4 ( NumCol Int );
;
WITH cte AS
(
SELECT ISNUMERIC(t.CTECol) as IsNCol1
FROM #Temp3 t
)
SELECT *
FROM #Temp4
JOIN cte ON #Temp4.NumCol = cte.IsNCol1
我检查了 Microsoft 的错误级别,看起来 11 是可纠正的用户错误,20 是致命错误,所以我觉得我收到的信息不一。
是否有正确的方法来做到这一点,或者它是 2014 年的倒退?
我认为这是一个错误。不过,我想出了一个可能适合您的解决方法。
;WITH cte AS
(
--SELECT
-- CASE WHEN ISNUMERIC(t.CTECol) = 1
-- THEN 1
-- ELSE null
--END as IsNCol1
SELECT CASE WHEN TRY_PARSE(t.CTECol AS INT) IS NOT NULL
THEN 1
ELSE NULL
END AS IsNCol1
FROM #Temp1 t
)
SELECT *
FROM #Temp2
JOIN cte
ON #Temp2.NumCol = cte.IsNCol1
TRY_PARSE
returns NULL
如果转换失败,那么如果它是 NOT NULL
那么你知道它是一个有效的整数。
这两个函数之间有一些细微的差别,但我更喜欢 TRY_PARSE
,因为根据 MSDN
ISNUMERIC returns 1 for some characters that are not numbers, such as plus (+), minus (-), and valid currency symbols such as the dollar sign ($)
更新:
我可能应该澄清其中一个细微差别。 ISNUMERIC
returns 1 如果参数可以解析为任何数值类型,它们是:
- 整数
- 数字
- bigint
- 金钱
- smallint
- 小钱
- tinyint
- 浮动
- 十进制
- 真实
它与 TRY_PARSE
不同,后者试图将输入解析为上述 特定 数据类型之一。 (在我的示例中,它 INT
)。因此,如果您真的想模仿 ISNUMERIC
,则需要为每种类型使用嵌套(或扁平化)CASE
。即使那样,行为仍然可能有些出乎意料,但这完全是另外一回事了。
这肯定是一个错误。
CTE 也不需要产生这种行为。下面直接用表达式效果一样。
SELECT *
FROM #Temp4
JOIN #Temp3
ON #Temp4.NumCol = ISNUMERIC(#Temp3.CTECol)
我可以在 12.0.2269.0 和 12.0.4213.0 上重现,但不能在 12.0.4449.0 上重现,所以看起来它现在已经修复了。
包含详细信息的相关知识库文章是 (FIX: Access violation when a query uses ISDATE or ISNUMERIC functions in Join conditions in SQL Server 2014 SP1)。
抛出异常时的堆栈跟踪如下(为了便于搜索)
KernelBase.dll!RaiseException()
msvcr100.dll!_CxxThrowException(void * pExceptionObject, const _s__ThrowInfo * pThrowInfo) Line 157
sqldk.dll!ExceptionBackout::GetCurrentException(void)
sqldk.dll!ex_raise2(int,int,int,int,void *,char *)
sqldk.dll!ex_raise_va_list(int,int,int,int,char *)
sqllang.dll!alg_ex_raise(int,int,int,int,int,...)
sqllang.dll!CAlgTableMetadata::RaiseBadTableException(int,int)
sqllang.dll!CAlgTableMetadata::Bind(class CRelOp_Query *,class COptExpr *)
sqllang.dll!CRelOp_Get::BindTree(class COptExpr *,class CBindEnv *,int)
sqllang.dll!COptExpr::BindTree(class CBindEnv *,int)
sqllang.dll!CRelOp_FromList::BindTree(class COptExpr *,class CBindEnv *,int)
sqllang.dll!COptExpr::BindTree(class CBindEnv *,int)
sqllang.dll!CRelOp_QuerySpec::BindTree(class COptExpr *,class CBindEnv *,int)
sqllang.dll!COptExpr::BindTree(class CBindEnv *,int)
sqllang.dll!CRelOp_DerivedTable::BindTree(class COptExpr *,class CBindEnv *,int)
sqllang.dll!COptExpr::BindTree(class CBindEnv *,int)
sqllang.dll!CRelOp_Query::BindCTEList(class CBindEnv *,class COptExpr *)
sqllang.dll!CRelOp_SelectQuery::BindTree(class COptExpr *,class CBindEnv *,int)
sqllang.dll!COptExpr::BindTree(class CBindEnv *,int)
sqllang.dll!CRelOp_Query::FAlgebrizeQuery(class COptExpr *,class CCompExecCtxtStmt const &,enum EObjType,class CSequenceProjectContext *)
sqllang.dll!CProchdr::FNormQuery(class CCompExecCtxtStmt const &,class CAlgStmt *,enum EObjType)
sqllang.dll!CProchdr::FNormalizeStep(class CCompExecCtxtStmt const &,class CAlgStmt *,class CCompPlan *,bool,class CParamExchange *,unsigned long *)
sqllang.dll!CSQLSource::FCompile(class CCompExecCtxt const &,class CParamExchange *)
sqllang.dll!CSQLSource::FCompWrapper(class CCompExecCtxt const &,class CParamExchange *,enum CSQLSource::ESqlFunction)
sqllang.dll!CSQLSource::Transform(class CCompExecCtxt const &,class CParamExchange *,enum CSQLSource::ESqlState)
sqllang.dll!CSQLSource::Execute(class CCompExecCtxtBasic const &,class CParamExchange *,unsigned long)
sqllang.dll!process_request(class IBatch *,class SNI_Conn *,enum RequestType)
sqllang.dll!process_commands(void *)
sqldk.dll!SOS_Task::Param::Execute(class SOS_Task *,void * * const)
sqldk.dll!SOS_Scheduler::RunTask(class Worker *)
sqldk.dll!SOS_Scheduler::ProcessTasks(class SOS_Scheduler *,class Worker *)
sqldk.dll!SchedulerManager::WorkerEntryPoint(class Worker *)
sqldk.dll!SystemThread::RunWorker(class Worker *)
sqldk.dll!SystemThreadDispatcher::ProcessWorker(class SystemThread *)
sqldk.dll!SchedulerManager::ThreadEntryPoint(void *)
kernel32.dll!BaseThreadInitThunk()
ntdll.dll!RtlUserThreadStart()