«Отключение» приведения столбцов при выполнении UNION ALL (MS Sql Server 2012) - PullRequest
0 голосов
/ 04 мая 2018

Согласно диаграмме преобразования типов SQL, найденной в (https://docs.microsoft.com/en-us/sql/t-sql/data-types/data-type-conversion-database-engine?view=sql-server-2017)), все используемые мной типы данных могут быть неявно преобразованы в тип nvarchar. Это идеально подходит для моих целей, поэтому я ожидал, что это сработает, полагая, что #CSV_Column_Titles определит тип всех столбцов:

INSERT INTO #CSV_Output          
SELECT * from #CSV_Column_Titles   --  n" columns, all columns are nvarchar( MAX ) )
UNION ALL
SELECT * FROM #QueryResults       --  "n" columns, all must be implicitly converted to nvarchar( MAX )

однако это возвращает такие сообщения, как:

Msg 8114, Level 16, State 5, Line 120
Error converting data type nvarchar to float.

Я знаю, почему это происходит; для того, что я пытаюсь сделать правила приоритета преобразования (см. https://docs.microsoft.com/en-us/sql/t-sql/data-types/data-type-precedence-transact-sql?view=sql-server-2017) "назад"). Я ожидал, что неявное преобразование будет "понижено" от самой специфической / ограничительной формы до самой общей формы - которая будет соответствовать мои потребности.

Приведенный выше фрагмент кода взят из процедуры, которая создает таблицу в стиле CSV. Неявное преобразование (в nvarchar) было бы идеальным, потому что #QueryResults передается в качестве параметра и может иметь любое количество столбцов любого типа. (Для любопытных #CSV_Column_Titles создается с использованием динамического SQL для извлечения заголовков столбцов из #QueryResults.)

Я попытался обойти проблему приоритета, создав использованный определенный тип (псевдоним nvarchar), но мне было отказано в разрешении «создать» (и я знаю, что оно никогда не будет предоставлено).

Прежде чем я углублюсь в использование динамического SQL, есть ли какой-нибудь простой способ (в идеале неявно) "понижать" каждый столбец до nvarchar без использования явных приведений / преобразований?


Кстати, если мне нужен динамический sql, я вижу, что он делает что-то вроде (это концептуальный, а не реальный код SQL):

INSERT INTO #CSV_Output          
SELECT * from #CSV_Column_Titles   
UNION ALL
EXECUTE( @Command )          -- instead of SELECT * FROM #QueryResults

где @Command строится следующим образом:

Set @Command  =  'SELECT (CAST(' +  @ColumnName + ' ) as nvarchar (MAX)) as '' +  @ColumnName  +'' '

затем добавляем одну из этих строк для каждого из оставшихся столбцов #QueryResults:

Set @Command =  @Command + ' , (CAST(' + @ColumnName + ' ) as nvarchar (MAX)) as '' + @ColumnName  + '' '

и заканчивается

Set @Command =   @Command + ' FROM #QueryResults' )

где @ColumnName извлекается из временной таблицы, которая использовалась для создания # CSV_Column_Titles.


Обновлено 20180404

(Вздох) Часто в пылу битвы вы не видите леса за деревьями ... Я сделал две ошибки: предполагая, что первый выбор * в объединении (неявно) определит типы столбцов финала набор результатов и пытается сделать многое за один шаг.

Ключевым моментом в принятом ответе является преобразование перед выполнением объединения.

В моем случае объединение не требуется, все, что мне нужно было сделать, это вставить (преобразованные) значения в #CSV_Output и передать их обратно вызывающему сценарию.

1 Ответ

0 голосов
/ 04 мая 2018

Если вы не возражаете против дополнительных операций ввода-вывода, вы можете сделать это без динамического SQL.

Сначала создайте временную таблицу со схемой выходной таблицы (используя SELECT / INTO с отрицательным предикатом). Вставьте Query # 2 в эту временную таблицу, чтобы она неявно конвертировалась в эти целевые типы. В вашем случае это все NVARCHAR (MAX).

--This will create a table like #CSV_Output, to store intermediate results
SELECT * INTO #QueryResultsCast FROM #CSV_Output WHERE 0 = 1

--cast all via INSERT
INSERT INTO #QueryResultsCast
SELECT * FROM #QueryResults

--Original desired output (retrieving from #QueryResultsCast)
INSERT INTO #CSV_Output          
SELECT * from #CSV_Column_Titles   --  n" columns, all columns are nvarchar( MAX ) )
UNION ALL
SELECT * FROM #QueryResultsCast       --  "n" columns, all must be implicitly converted to nvarchar( MAX )
...