Написав сценарий для экспериментов с sql_variant
типом данных и SQL_VARIANT_PROPERTY
функцией (для восстановления информации о данных из sql_variant
столбца) на SQL Server, я случайно обнаружил то, что я считаю неожиданным поведением оператора с несколькими значениями INSERT INTO
.
Следующий сценарий иллюстрирует поведение:
BEGIN
DECLARE @TblVariant AS TABLE (
rowid int identity
,VariantValue sql_variant
,AsStringColumn varchar(max)
,AsStringTypeInfo varchar(max)
,AsStringValue varchar(max)
,Result varchar(max)
)
--Inserting multiple values with one INSERT INTO statement
INSERT INTO @TblVariant (VariantValue)
VALUES (convert(float ,10.25))
,(convert(int ,11.00))
,(convert(numeric(10,0),12.15))
,(convert(numeric(10,2),13.50))
,(convert(bigint ,14.75))
,(null)
UPDATE @TblVariant
SET AsStringColumn = convert(varchar(max),VariantValue)
,AsStringTypeInfo =
'BaseType=' +isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'BaseType')),'NULL')
+';Precision=' +isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'Precision')),'NULL')
+';Scale=' +isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'Scale')),'NULL')
+';TotalBytes='+isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'TotalBytes')),'NULL')
+';Collation=' +isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'Collation')),'NULL')
+';MaxLength=' +isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'MaxLength')),'NULL')
SELECT *
FROM @TblVariant
DELETE @TblVariant
-- Multiple insert statements
INSERT INTO @TblVariant (VariantValue) VALUES (convert(float, 10.25))
INSERT INTO @TblVariant (VariantValue) VALUES (convert(int, 11.00))
INSERT INTO @TblVariant (VariantValue) VALUES (convert(numeric(10, 0), 12.15))
INSERT INTO @TblVariant (VariantValue) VALUES (convert(numeric(10, 2), 13.50))
INSERT INTO @TblVariant (VariantValue) VALUES (convert(bigint, 14.75))
INSERT INTO @TblVariant (VariantValue) VALUES (NULL)
UPDATE @TblVariant
SET AsStringColumn = convert(varchar(max),VariantValue)
--Ojo a este comportamiento. Al
,AsStringTypeInfo =
'BaseType=' +isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'BaseType')),'NULL')
+';Precision=' +isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'Precision')),'NULL')
+';Scale=' +isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'Scale')),'NULL')
+';TotalBytes='+isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'TotalBytes')),'NULL')
+';Collation=' +isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'Collation')),'NULL')
+';MaxLength=' +isnull(try_convert(varchar(max),SQL_VARIANT_PROPERTY(VariantValue,'MaxLength')),'NULL')
SELECT *
FROM @TblVariant
END
Сценарий выше использует табличную переменную @TblVariant
со столбцом sql_variant
с именем VariantValue
для хранения некоторых данных, вставляет некоторые значения в @TblVariant
, а затем использует SQL_VARIANT_PROPERTY
для восстановления информации о типе данных из столбца VariantValue
.
Первый сценарий вставляет несколько значений с помощью одного оператора INSERT INTO
, после удаления данных из @TblVariant
второй сценарий повторяет процесс с использованием нескольких операторов INSERT INTO
для повторного заполнения @TblVariant
теми же данными.
Результат выглядит следующим образом
Как мы видим, в обоих сценариях значения в VariantValue
и AsStringColumn
выглядят одинаковыми для всех строк, но вычисляемый столбец AsStringTypeInfo
выглядит по-разному в каждом сценарии.
Для сценария с одним оператором INSERT INTO
результаты почти для всех строк (за исключением строки с нулевым значением в столбце VariantValue
) одинаковы; в то время как, с другой стороны, для сценария с несколькими операторами INSERT INTO
значение, рассчитанное для AsStringTypeInfo
, выглядит по-разному для каждой строки, предоставляя более точную информацию для значения, вставленного / сохраненного в столбце VariantValue
, для каждой строки.
Мой вопрос: в чем разница между выполнением одного оператора INSERT INTO
для вставки нескольких значений строки и выполнением нескольких операторов INSERT INTO
для вставки одних и тех же значений, и почему эта разница влияет на поведение функции SQL_VARIANT_PROPERTY
?
Вы можете привести/преобразовать одно из значений в sql_variant, тогда вариант имеет наивысший приоритет. ), (преобразовать (числовой (10,0), 12,15))….
Спасибо за ваш комментарий @lptr. Насколько я понимаю ваше объяснение и я прочитал из документации, которую вы публикуете, при вставке через оператор Table Value Constructor Values () значения за пределами первой строки «приводятся» к типу с наивысшим приоритетом в этом случае ´ float», в результате чего столбец VariantValue
для всех строк в @TblVariant
сохраняется как sql_variant(float)
. Я думаю, что это ответ, вы можете обновить свой комментарий как ответ.
Как ответил @lptr в комментариях:
Values()
— это конструктор табличных значений. Каждый столбец values()
(поскольку это конструктор таблицы) должен иметь один и тот же тип данных.
Первый оператор множественных значений имеет несколько типов данных, поэтому тот, у которого наивысший приоритет, — float
. values
выводит float
, который преобразуется в sql_variant
при вставке в таблицу. Короче говоря, values()
выполняет union all
каждой строки (из плана выполнения, constant scan
, <OutputList><ColumnReference Column = "Union1009">
Вы можете привести/преобразовать одно из значений в sql_variant
, тогда sql_variant
имеет наивысший приоритет
INSERT INTO @TblVariant (VariantValue) VALUES
(convert(sql_variant, convert(float ,10.25))),
(convert(int,11.00)),
(convert(numeric(10,0),12.15))
Values() — это конструктор табличных значений: learn.microsoft.com/en-us/sql/t-sql/queries/… . Каждый столбец values() (поскольку это конструктор таблицы) должен иметь один и тот же тип данных. Первый, множественный, оператор значений имеет несколько типов данных, тот, у которого наивысший приоритет, - это число с плавающей запятой, значения выводят число с плавающей запятой, которое преобразуется в вариант при вставке в таблицу. Короче говоря, values() выполняет «объединение всех» каждой строки (из плана выполнения, постоянного сканирования, <OutputList><ColumnReference Column = "Union1009">..