SQL 服务器:如何即时更改我的 select 所有列的排序规则

SQL Server: How to change collation of all columns of my select on the fly

我需要从我的数据库中的 table 中查询一些内容,但某些列的排序规则与我需要的不同。

我发现了如何像这样即时更改一列的排序规则:

SELECT 
  u.name  COLLATE Latin1_General_CI_AS AS 'User'(...)

但问题是超过 150 列需要即时更改排序规则(不要问我为什么,因为我们不能更改整个 table 的排序规则,尽管我认为这将是最好的选择。

所以主要问题是: Is there a way to set the COLLATION to all columns of my query?

我不确定是否有办法以这种方式对输出的所有列强制使用默认排序规则。

但是,如果您的服务器允许您使用动态 SQL,您可以构建一个动态查询并执行它以获得您要查找的结果。

此工作的示例如下所示。请将 YOUR_TABLE_NAME 更改为您的实际 table 名称以使其正常工作:

DECLARE @col    VARCHAR(200)
DECLARE @sql    VARCHAR(MAX)
DECLARE @table  VARCHAR(200)
SET @table = 'YOUR_TABLE_NAME';

-- Use a cursor to read all varchar columns from table
DECLARE curColumns CURSOR FORWARD_ONLY STATIC READ_ONLY
  FOR
    SELECT  c.name
      FROM  sys.columns c
        INNER JOIN sys.types t ON c.user_type_id = t.user_type_id
      WHERE c.object_id = OBJECT_ID(@table) AND
            t.name = 'varchar'

SET @sql = 'SELECT'

-- Go through column cursor
OPEN curColumns
FETCH NEXT FROM curColumns INTO @col
WHILE (@@FETCH_STATUS = 0)
BEGIN
    -- Add column to SQL with collation
    SET @sql = @sql + ' ' + @col + ' COLLATE Latin1_General_CI_AS,'

    FETCH NEXT FROM curColumns INTO @col
END
CLOSE curColumns
DEALLOCATE curColumns

-- Add the from clause
SET @sql = SUBSTRING(@sql, 0, LEN(@sql) - 1) + ' FROM ' + @table

-- Uncomment this line to see the generated SQL
--PRINT @sql
EXEC (@sql)

请注意,上面的例子有几个限制:

  1. 您的服务器必须允许 运行 动态 SQL。
  2. 在此示例中,仅输出 VARCHAR 列(您可以通过更改游标查询以包含更多类型来更改此设置)。
  3. 无法使用此方法指定列别名。

无法即时更改所有文本列的排序规则——但可以通过操纵查询来获得相同的结果。 展示了一种方法,这是使用查询元数据的另一种方法。作为一个好处,它是无游标的,并且如果排序规则已经是我们想要的,它可以避免无偿地引入 "new" 列。它至少需要 SQL Server 2012 具有 sys.dm_exec_describe_first_result_set,并且 SQL Server 2017 具有 STRING_AGG(在以前的版本中,可以使用经典的字符串连接技巧,例如FOR XML PATH 把戏)。

DECLARE @query NVARCHAR(MAX) = N'SELECT * FROM MyTable';
DECLARE @tweakedQuery NVARCHAR(MAX);
DECLARE @tweakedCollation SYSNAME = N'Latin1_General_CI_AS';

SELECT @tweakedQuery = CONCAT(
    'SELECT ', 
    STRING_AGG(
        CONCAT(
            CONVERT(NVARCHAR(MAX), QUOTENAME([name])), 
            CASE 
                WHEN collation_name IS NOT NULL AND collation_name <> @tweakedCollation
                THEN ' COLLATE ' + @tweakedCollation + ' AS ' + QUOTENAME([name]) 
            END
        ), 
        ', ' + CHAR(13) + CHAR(10)
    ),
    ' FROM (' + CHAR(13) + CHAR(10),
    @query,
    CHAR(13) + CHAR(10) + ') _'
)
FROM sys.dm_exec_describe_first_result_set(@query, NULL, NULL);
PRINT @tweakedQuery;

如果自定义别名是系统生成的(基于 table 或列名),则合并自定义别名会很简单。否则你将不得不手动修补。

对于一次性查询(不一定需要即时执行),熟悉在您喜欢的编辑器中使用正则表达式仍然是一个更好的主意。如果您最喜欢的编辑器没有正则表达式,那就找一个有正则表达式的吧。