#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 в другой базе данных и будет думать, что она существует в текущей базе данных.