Проверка наличия столбца во временной таблице всегда возвращает false в SQL Server

#sql #sql-server #tsql #exec #temp-tables

#sql #sql-сервер #tsql #exec #временные таблицы

Вопрос:

У меня есть следующий оператор выполнения, который создает таблицу (используя данные из другой процедуры), вставляет значения во временную таблицу, добавляет столбец image (поскольку они не могут быть включены в группировку), а затем обновляет его на основе критериев из другой временной таблицы (результирующие поля таблицы используются в отчете SSRS, поэтому мне нужно сохранить тип данных IMAGE):

 EXEC ('SELECT '   @COL_TO_GROUP_BY   @COL_ADDITONAL   @sColVals   ' INTO
#RESULTS_TABLE from ('   @SQL_STATEMENT   ') A '   @WHERE_CLAUSE   ' GROUP BY '  
@COL_TO_GROUP_BY  

' ALTER TABLE #RESULTS_TABLE
ADD IMAGE_DATA IMAGE

IF EXISTS(SELECT * FROM tempdb.INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME = 
''COLUMN_A'' and TABLE_NAME LIKE ''#RESULTS_TABLE%'')
BEGIN
    UPDATE #RESULTS_TABLE
    SET IMAGE_DATA = FILE_DATA
    FROM #RESULTS_TABLE A, #IMAGE_TABLE B 
    WHERE A.COLUMN_A = B.COLUMN_A
END

SELECT * FROM #RESULTS_TABLE')
  

Проблема в том, что независимо от того, существует COLUMN_A или нет, столбец для IMAGE_DATA всегда равен NULL. Есть ли какой-либо другой способ получить данные в столбец IMAGE_DATA? Пожалуйста, помогите!

Примечание: Я не буду принимать никаких ответов, из которых следует, что проблема связана с содержимым других таблиц, более конкретно, из предложения WHERE. Я выполнил несколько проверок, чтобы убедиться, что условия могут быть как истинными, так и ложными (совпадающие строки, нет совпадающих строк и т.д.). Таким образом, это исключает условный оператор. Спасибо.

Редактировать:

Я все еще не совсем уверен, какова была точная причина этого, но в итоге я создал глобальную временную таблицу и выполнил две отдельные процедуры, теперь, кажется, все работает нормально. Мне пришлось принять ответ, который наиболее точно соответствовал моему собственному решению. Все ответы и комментарии были очень жизнеспособными. Спасибо!

Комментарии:

1. Прежде всего, я бы попытался опустить условие WHERE A.COLUMN_A = B.IMAGE_VALUE мне кажется, этого достаточно, чтобы найти нужную строку с помощью OBJ_ID

2. Спасибо, что указали на это. В моем случае мне нужны оба условия, потому что есть несколько строк с повторяющимися OBJ_IDs. Я упростил свой исходный код и должен был убрать и это, поскольку это выглядит вводящим в заблуждение. Я отредактирую вопрос.

3. Не уверен, поможет ли это, но я думаю, вы можете добавить столбец image в свой оператор select следующим образом cast(null as Image) as IMAGE_DATA , не добавляя его в предложение group by, поскольку оно является константой. Кстати, зачем вам нужно проверять, существует ли столбец?

4. @Mikael Eriksson — Добавление приведения просто позволит мне удалить инструкцию ALTER TABLE. Хотя это более эффективно, это не решает мою проблему. Однако спасибо за совет! Я должен проверить, существует ли столбец, потому что я извлекаю динамические столбцы из другой процедуры, и IMAGE_DATA заполняется в зависимости от того, существует столбец или нет. Я отредактировал вопрос, чтобы сделать его более зависимым от проверяемого столбца.

Ответ №1:

У вас есть несколько проблем в вашем скрипте:

  • TempDB это неправильное название tempdb . Ваш код будет прерван на сервере, установленном с учетом сортировки с учетом регистра. Всегда используйте правильный регистр для всех имен баз данных. Ваш код, вероятно, будет аналогичным образом прерываться в именах результирующих столбцов, например COLUMN_A , если выполняется при развертывании с учетом регистра.
  • Логика некорректна при параллелизме: сеанс A увидит таблицу #temp сеанса B и выполнит неправильные действия.
  • Простой тест показывает, что столбцы являются видимыми:

.

 EXEC ('SELECT * INTO #RESULTS_TABLE from 
     (select *, newid() as COLUMN_A from master..spt_values) A;
ALTER TABLE #RESULTS_TABLE
ADD IMAGE_DATA varbinary(max);
IF EXISTS(SELECT * FROM TempDB.INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME = 
''COLUMN_A'' and TABLE_NAME LIKE ''#RESULTS_TABLE%'')
BEGIN
    update #RESULTS_TABLE 
       set IMAGE_DATA = 0xBAADF00D;
END
SELECT * FROM #RESULTS_TABLE')
  

Этот тест показывает, что столбец был обновлен, поэтому проверка на СУЩЕСТВОВАНИЕ прошла успешно. Очевидный вывод заключается в том, что в вашем случае OBJ_ID соединение между #RESULTS_TABLE и #IMAGE_TABLE не находит соответствия, что является проблемой, которая полностью зависит от содержимого ваших таблиц.

Редактировать

Вы можете сделать COLUMN_A динамическим, он по-прежнему отлично работает при тестировании:

 declare @cn sysname = case 
      when RAND() * 100 >= 50 then 'COLUMN_A' 
      else 'COLUMN_B' end;

EXEC (N'SELECT * INTO #RESULTS_TABLE from (
     select *, newid() as '   @cn   N'  from master..spt_values) A;
...
  

Комментарии:

1. Спасибо за ваш ответ. Пожалуйста, ознакомьтесь с изменениями в вопросе. Ответьте мне, поскольку это мой первый вопрос на этом форуме, поэтому я должен был сделать вопрос более понятным с первого раза. Я попробую использовать процедуру, которую вы использовали для своего теста.

2. Без обид, но всякий раз, когда я сталкиваюсь с «вашим словом против проверенного поведения», я выбираю «проверенное поведение». Мяч на вашей стороне, вам нужно доказать, что ветвь IF не занята.

3. Я посмотрел на ваш скрипт и понял, что есть разница в том, как вы его выполнили, по сравнению с тем, как я это сделал. Мой скрипт явно не создает COLUMN_A. COLUMN_A отображается в зависимости от содержимого, извлеченного из другой процедуры (другой таблицы), и поэтому является динамическим. Вы правы, если я сделаю это таким образом, это будет работать нормально (и мне, вероятно, вообще не нужно было бы выполнять проверку в этом случае). Я думаю, что проблема заключается в динамически создаваемых столбцах.

4. Тест по-прежнему работает нормально, если COLUMN_A генерируется динамически, см. Мое обновление.

5. В итоге я создал глобальную временную таблицу и две отдельные процедуры. Кажется, сейчас это работает, поэтому я не буду больше вмешиваться в это. 🙂 Мне пришлось принять ответ от gbn, потому что его рассуждения наиболее точно соответствовали моему решению. Тем не менее, большое спасибо за ваши усилия.

Ответ №2:

Я знаю, что это очень старая тема, но хотел поделиться своим решением.

Проблема в том, что ИМЯ_ТАБЛИЦЫ, возвращаемое представлением INFORMATION_SCHEMA.COLUMNS, является именем таблицы, поскольку оно хранится в таблице tempdb.sys.objects, которая включает индикатор сеанса.

Вы не можете использовать «ИМЯ_ТАБЛИЦЫ как ‘RESULTS_TABLE%'», потому что это небезопасно для сеанса.

Решение состоит в том, чтобы запрашивать схему напрямую с идентификатором объекта таблицы вместо использования представлений INFORMATION_SCHEMA следующим образом:

 if exists (Select 1 
           from tempdb.sys.columns 
           where [object_id] = object_id('tempdb..#RESULTS_TABLE') 
             and name ='COLUMN_A')
  

Ответ №3:

Правильно.

На момент компиляции столбец не существует. То есть SQL Server просматривает весь набор команд и анализирует / компилирует его. Последствия ИЗМЕНЕНИЯ ТАБЛИЦЫ в коде не будут видны последующим командам.

Вы должны выполнить изменение ТАБЛИЦЫ отдельно от ОБНОВЛЕНИЯ

Примечание: Для SQL Server 2005 у вас будет varbinary (max), который является гораздо более гибким и устраняет некоторые сложности

Комментарии:

1. Спасибо за ваш ответ. Я использую SQL Server 2008. Я попытался преобразовать столбец image в varbinary (max) и даже в varbinary из двоичного файла (согласно другим предложениям), но это дает мне тарабарщину, так что это был не вариант. Не могли бы вы, пожалуйста, пояснить, как я должен изменять ТАБЛИЦУ отдельно от ОБНОВЛЕНИЯ? Выполнение двух инструкций EXEC выходит за рамки временной таблицы. Спасибо.

Ответ №4:

Вы можете проверить, существует ли столбец во временной таблице, используя..

 IF EXISTS (SELECT * FROM TempDB.INFORMATION_SCHEMA.COLUMNS WHERE COLUMN_NAME = 'Column' AND TABLE_NAME LIKE '#TempTable%')
    PRINT 'Column exists'
ELSE
    PRINT 'Column doesn''t exists'
  

Комментарии:

1. Это не нормально, если 2 базы данных на сервере используют одинаковые имена таблиц tmp. Этот скрипт увидит таблицу tmp в другой базе данных и будет думать, что она существует в текущей базе данных.