【问题标题】:IsNumeric failing with "A severe error occurred on the current command." SQL Server 2014 CTEIsNumeric 失败并显示“当前命令发生严重错误”。 SQL Server 2014 CTE
【发布时间】:2016-01-02 19:02:11
【问题描述】:

我正在运行一系列生成数据库的脚本。它们在 SQL Server 2012 (11.0.5058.0) 上运行完成。在 SQL Server 2014 (12.0.4213.0) 上出现以下脚本错误:

消息 0,级别 11,状态 0,行 0
当前命令发生严重错误。结果,如果有的话,应该丢弃。

消息 0,级别 20,状态 0,行 0
当前命令发生严重错误。结果(如果有)应丢弃。

似乎在 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 年的回归?

【问题讨论】:

  • 检查 SQL Server 错误日志,您会发现由于访问冲突而导致的堆栈转储。这表明您在 SQL Server 2014 中遇到了回归错误(我在 SQL Server 2012 或最新的 SQL Server 2016 CTP 中没有收到错误)。严重性 20 级致命错误取代了之前的严重性 11 错误。

标签: sql-server common-table-expression sql-server-2014 isnumeric


【解决方案1】:

这肯定是一个错误。

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()

【讨论】:

    【解决方案2】:

    我认为这是一个错误。不过,我想出了一个可能对您有用的解决方法。

    ;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 如果转换失败,则返回 NULL,所以如果它是 NOT NULL,那么你就知道它是一个有效的 int。

    这两个函数有一些细微的差别,但我还是更喜欢TRY_PARSE,因为根据MSDN

    ISNUMERIC 对某些不是数字的字符返回 1,例如 加号 (+)、减号 (-) 和有效的货币符号,例如美元 符号 ($)

    更新:

    我可能应该澄清其中的细微差别。 ISNUMERIC如果参数可以解析为任意数值类型,则返回1,它们是:

    • int
    • 数字
    • 大整数
    • 钱
    • smallint
    • 小钱
    • 小号
    • 浮动
    • 十进制
    • 真实

    它与TRY_PARSE 不同,后者尝试将输入解析为上述特定 数据类型之一。 (在我的例子中,它是INT)。因此,如果您真的想模仿ISNUMERIC,则需要为每种类型使用嵌套(或扁平)CASE。即便如此,行为仍然可能有些出乎意料,但那是另一回事了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-12
      • 2018-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多