8

Я запускаю ряд сценариев, которые генерируют базу данных. Они заканчиваются на SQL Server 2012 (11.0.5058.0). В SQL Server 2014 (12.0.4213.0) а ошибки сценария с:Ошибка IsNumeric с «Сильная ошибка при текущей команде». SQL Server 2014 CTE

Msg 0, Level 11, состояние 0, строка 0
серьезная ошибка произошла на текущей команды. Результаты, если таковые имеются, должны быть отброшены.

Msg 0, Level 20, State 0, Line 0
Серьезная ошибка при текущей команде. Результаты, если таковые имеются, должны быть отброшены.

Похоже, что с использованием результатов инструкции внутри запроса CTE разбивается на запрос построения запроса, поскольку для возникновения ошибки не требуются строки. Вареная версия случае я столкнулся это:

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 году?

+0

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/7c4dd91f-4ca6-4cb9- ad8b-c8f32e9d25e7/a-serious-error -used-on-the-current-command-the-results-if-any-should-be-discarded? forum = transactsql –

+0

Проверьте журнал ошибок SQL Server, и вы найдете стек сброс из-за нарушения доступа. Это означает, что вы столкнулись с ошибкой регрессии в SQL Server 2014 (я не получаю ошибку в SQL Server 2012 или последнем CTP SQL Server 2016). Фатальная ошибка уровня серьезности 20 заменяет предыдущую ошибку уровня серьезности 11. –

ответ

2

Это, безусловно, ошибка.

Нет необходимости в том, чтобы 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() 
1

Я думаю, что это ошибка. Однако я придумал обходное решение, которое может сработать для вас.

;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 для некоторых символов, которые не являются цифрами, такими как плюса (+), минус (-), а также действующие валютные символы, такие как знак доллара ($)

UPDATE:

Я, вероятно, должен прояснить одну из тонких отличий.ISNUMERIC возвращает 1, если параметр может быть разобран для любого числового типа, и они:

  • INT
  • числовая
  • BIGINT
  • деньги
  • SMALLINT
  • smallmoney
  • tinyint
  • float
  • десятичного
  • реального

Это не то же самое, как TRY_PARSE, который пытается разобрать вход в один из указанных выше специфических типов данных. (В моем примере это INT). Поэтому, если вы действительно хотите имитировать ISNUMERIC, вам нужно будет использовать вложенный (или сплющенный) CASE для каждого типа. Даже тогда поведение может быть несколько неожиданным, но это совсем другая история.

Смежные вопросы