SQL Server 2019. I am looking for a solution to change the collation of sql_variant values in a select statement, if there is one. The proplem is that the COLLATE operator cannot be applied to sql_variant data types. The select queries are cross-database and may be invoked using OPENQUERY. It is not allowed to inject extended stored procedures or CLR functions.
Currently I am forced to post-process results with a cursor.
SET NOCOUNT ON;
DECLARE @var sql_variant;
DECLARE @sql nvarchar(2000);
DECLARE varupdcur CURSOR GLOBAL FORWARD_ONLY FOR
SELECT [_sqlvariant] FROM [dbo].[test] WHERE SQL_VARIANT_PROPERTY([_sqlvariant], 'Collation') = N'Latin1_General_CI_AS'
FOR UPDATE OF [_sqlvariant]; OPEN varupdcur;
FETCH NEXT FROM varupdcur INTO @var;
WHILE @@FETCH_STATUS = 0 BEGIN
SET @sql = N'UPDATE [dbo].[test] SET [_sqlvariant] = CAST(CAST([_sqlvariant] AS '
+ CAST(SQL_VARIANT_PROPERTY(@var, 'Basetype') AS nvarchar(16))
+ N'('
+ CAST(CAST(SQL_VARIANT_PROPERTY(@var, 'MaxLength') AS int) / IIF(SQL_VARIANT_PROPERTY(@var, 'Basetype') in (N'nchar', N'nvarchar'), 2, 1) AS nvarchar(16))
+ N')) COLLATE DATABASE_DEFAULT AS sql_variant) WHERE CURRENT OF varupdcur';
EXECUTE sp_executesql @sql;
FETCH NEXT FROM varupdcur INTO @var;
END
CLOSE varupdcur; DEALLOCATE varupdcur;
SET NOCOUNT OFF;
All my research led to this
WITH test(v, va) AS
(
SELECT
v,
(CASE WHEN SQL_VARIANT_PROPERTY(v, 'Collation') = 'Cyrillic_General_CI_AS'
-- Expression type sql_variant is invalid for COLLATE clause.
--THEN v COLLATE DATABASE_DEFAULT
-- Obviously makes MaxLength = 8000
THEN CAST(CAST(v AS varchar(8000)) COLLATE DATABASE_DEFAULT AS sql_variant)
-- Argument data type sql_variant is invalid for argument 1 of left function
--THEN LEFT(v, SQL_VARIANT_PROPERTY(v, 'MaxLength')) COLLATE DATABASE_DEFAULT
-- The constant length value works, but ???
--THEN LEFT(CAST(v AS varchar(8000)) COLLATE DATABASE_DEFAULT, 777)
-- Operand type clash: nvarchar(max) is incompatible with sql_variant
--THEN sys.fn_sqlvarbasetostr(v) COLLATE DATABASE_DEFAULT
-- The argument 2 of the XML data type method "value" must be a string literal
--THEN (SELECT CAST(v AS varchar(8000)) COLLATE DATABASE_DEFAULT FOR XML PATH, TYPE).value('.[1]', N'varchar(' + CAST(SQL_VARIANT_PROPERTY(v, 'MaxLength') AS varchar(100)) + ')')
ELSE v
END) AS va
FROM
( VALUES
(CAST('some text' COLLATE Cyrillic_General_100_CI_AS AS sql_variant)),
(34.56),
('some text some text some text some text some text some text' COLLATE Cyrillic_General_CI_AS),
(0x0030),
('some text some text' COLLATE Cyrillic_General_CI_AS)
) AS test(v)
)
SELECT
v,
va,
SQL_VARIANT_PROPERTY(v, 'MaxLength') maxlen,
SQL_VARIANT_PROPERTY(va, 'MaxLength') va_maxlen,
SQL_VARIANT_PROPERTY(v, 'Collation') collation,
SQL_VARIANT_PROPERTY(va, 'Collation') va_collation
FROM
test