为什么这个游标以错误的顺序产生结果?

Han*_*non 7 sql-server-2005 sql-server dynamic-sql

我正在编写一些动态 SQL 来识别,也许如果我觉得够疯狂,可以自动将我的NONCLUSTERED索引转换为CLUSTERED索引。

ORDER BY 1,2,3 DESC;下面的 SQL 中的这一行旨在在DROP INDEX...语句之前输出语句ALTER TABLE...,以便先删除 NONCLUSTERED 索引,然后添加一个 CLUSTERED 索引。我必须在第DESC3 列之后添加DROP,然后是 ALTER。这是倒退,除非我失去它!

DECLARE @Server nvarchar(max);
DECLARE @Database nvarchar(max);
DECLARE @cmd nvarchar(max);
DECLARE @IndexType int;


SET @IndexType = 2; /*  1 is CLUSTERED, 2 is NONCLUSTERED */
SET @Server = 'MyServer';
SET @Database = 'MyDatabase';

SET @cmd = '
    DECLARE @cmd nvarchar(max);
    SET @cmd = ''
    SET NOCOUNT ON;
    DECLARE @IndexInfo TABLE (TableName nvarchar(255), IndexName nvarchar(255), IndexColumnName nvarchar(255));
    INSERT INTO @IndexInfo (TableName, IndexName, IndexColumnName)
    SELECT t.name AS TableName, i.name AS IndexName, c.name AS IndexColumnName /*, t.create_date, ic.*, c.* */
    FROM sys.tables t
        LEFT JOIN sys.indexes i ON t.object_id = i.object_id
        LEFT JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
        LEFT JOIN sys.columns c ON i.object_id = c.object_id and ic.column_id = c.column_id
    WHERE i.is_primary_key = 1  
        AND i.type = ' + CAST(@IndexType as nvarchar(max)) + '
    ORDER BY t.create_date desc;
    DECLARE @t1 nvarchar(max);
    DECLARE @t2 nvarchar(max);
    DECLARE @t3 nvarchar(max);
    DECLARE @cmd nvarchar(max);
    DECLARE cur CURSOR FOR
    SELECT TableName, IndexName, 1 AS ExecOrder, ''''DROP INDEX '''' + IndexName + '''' ON '''' + TableName + '''';'''' FROM @IndexInfo I
    UNION ALL 
    SELECT TableName, IndexName, 2 AS ExecOrder, ''''ALTER TABLE '''' + TableName + '''' ADD CONSTRAINT PK_'''' + TableName + ''''_'''' + IndexColumnName + '''' PRIMARY KEY CLUSTERED ('''' + IndexColumnName + '''')'''' + '''';'''' FROM @IndexInfo I
    ORDER BY 1,2,3 DESC;
    OPEN cur;
    FETCH NEXT FROM cur INTO @t1, @t2, @t3, @cmd;
    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT @cmd;
        FETCH NEXT FROM cur INTO @t1, @t2, @t3, @cmd;
    END
    CLOSE cur;
    DEALLOCATE cur;
    '';
    EXEC ' + @Server + '.' + @Database + '.sys.sp_executesql @cmd;
';
PRINT @cmd;
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 11

PRINT内部声明WHILE循环不执行你所期望的顺序,但在此之前的输出缓冲sys.sp_executesql的回报。实现细节意味着缓冲输出被反转。

使用RAISERROR (@cmd, 0, 1) WITH NOWAIT;而不是PRINT强制缓冲区在每次调用后刷新,按照您期望的顺序为您提供结果。IIRC 该NOWAIT技巧仅适用于前 500 行的每行。在任何情况下,所有这些都是无证的东西,随时可能改变,所以请不要依赖它——我提到它只是为了解释你所看到的。

如果您将sp_executesql调用替换为EXEC (@cmd) AT ' + @Server + '虽然确实需要USE database前缀为的命令@cmd并且链接服务器需要为 RPC 启用,则不会发生逆转。这也不是建议,只是显示输出反转是sp_executesql.


Aar*_*and 5

鉴于评论中列出的新信息,至少有三种解决方案不需要动态 SQL 和手动操作,事后反应:

使用尺子

说真的,他是一名 DBA,负责贵公司的工作。当然,您可以实施一项策略,即表具有聚集索引,除非在某些情况下(在这种情况下,它们必须被注销)。当作为政策表述时,这将成为衡量工作绩效的潜在可量化衡量标准。

实现 DDL 触发器

CREATE_TABLE在 DDL 触发器中捕获事件非常容易,然后检查 sys.indexes 或 sys.partitions 目录视图是否存在聚集索引。没有聚集索引?回滚。这将消除 的可能性SELECT INTO,因为您不能提前定义聚集索引,但也许没关系。

让他在 Azure 中发展

如果没有聚集索引,就无法在云中创建表。如果您让他在那里发展,则不需要标尺或 DDL 触发器。


你为什么使用序数?代替:

ORDER BY 1,2,3 DESC;
Run Code Online (Sandbox Code Playgroud)

尝试直接引用别名:

ORDER BY TableName, IndexName, ExecOrder;
Run Code Online (Sandbox Code Playgroud)

你也使用游标来建立@cmd,我希望你能在某个地方附加它。目前看来,您正在遍历游标,但只@cmd从循环中的最后一次迭代开始执行。那么游标的目的是什么?如果确实需要光标,为什么不使用更好的光标选项?(如果不是,您可以将其更改为单个 TOP 查询)。