无法通过 EF ExecuteSqlCommand 重建索引

Can't rebuild indexes via EF ExecuteSqlCommand

我有以下重建索引的脚本:

DECLARE @TableName VARCHAR(255)
DECLARE @sql NVARCHAR(500)
DECLARE @fillfactor INT

SET @fillfactor = 80

DECLARE TableCursor CURSOR FOR
    SELECT OBJECT_SCHEMA_NAME([object_id])+'.['+name +']' AS TableName
    FROM sys.tables

OPEN TableCursor
FETCH NEXT FROM TableCursor INTO @TableName

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @sql = 'ALTER INDEX ALL ON ' + @TableName + ' REBUILD WITH (FILLFACTOR = ' + CONVERT(VARCHAR(3),@fillfactor) + ')'
    EXEC (@sql)

    FETCH NEXT FROM TableCursor INTO @TableName
END

CLOSE TableCursor
DEALLOCATE TableCursor

我还有其他脚本 运行 与此脚本的方式相同。

当我按以下方式执行时:

var sql = ResourceUtilities.ReadResourceContent("rebuild_indexes.sql");
db.Database.ExecuteSqlCommand(sql);

我收到以下错误:

Incorrect syntax near 'TableCursor'.

ReadResourceContent 的实施细节无关紧要 - 我正在 运行 与其他任意 SQL 相关联,它工作正常。

为什么会这样,需要改变什么?

您至少应该尝试用分号结束所有行。虽然很少需要(我知道只有两个实例是 THROW 语句之前的语句,这些语句是在 SQL Server 2012 中引入的,并且在 CTE 之前),但它被正式称为最佳实践SQL Server 2005 发布。

用分号终止语句/查询的好处之一是 SQL 服务器在出现行尾不一致等问题时更容易解析查询批处理,这可能是根本原因这里的问题。我猜根本原因是行尾不一致,因为您可以通过 SSMS 针对您的 Azure 数据库 运行 脚本。如果 Azure SQL 数据库需要分号,那么即使在通过 SSMS 运行ning 时也会产生错误。 SSMS 很可能在提交批处理之前使行尾保持一致,运行通过 .NET 代码不会自动为您完成的事情。

其他说明:

  • 最好不要混合使用 VARCHARNVARCHAR(尽管数据类型优先级最终会将其全部转换为 NVARCHAR)。由于您正在处理标识符(即 table 名称,它们在数据库中是 sysname 类型,是 NVARCHAR(128) 的别名),理想情况下所有都应该是 NVARCHAR 并且所有以 N.

  • 为前缀的字符串文字
  • 在大多数情况下,尤其是对于带有标识列的 table,FILLFACTOR 80 是很糟糕的,您应该使用 100。当使用 NEWID() 然后从 90 开始,仅在必要时降低。对于 NEWSEQUENTIALID() 使用 100.

  • 声明游标时,如果查询引用的是真实的tables而不是临时的tables,则使用STATIC关键字以免锁定基数 table(s)。 通常也使用以下关键字是个不错的主意:LOCAL READ_ONLY FORWARD_ONLY.

最终结果应如下所示:

DECLARE @TableName sysname, -- system alias for NVARCHAR(128)
        @SQL NVARCHAR(500),
        @FillFactor TINYINT; -- value cannot be < 0 or > 100 anyway

SET @FillFactor = 100; -- or 90 if using NEWID() for Clustered Index

DECLARE TableCursor CURSOR STATIC LOCAL READ_ONLY FORWARD_ONLY
FOR
    SELECT OBJECT_SCHEMA_NAME(st.[object_id]) + N'.' + QUOTENAME(st.[name]) AS [TableName]
    FROM   sys.tables st;

OPEN TableCursor;

FETCH NEXT
FROM  TableCursor
INTO  @TableName;

WHILE (@@FETCH_STATUS = 0)
BEGIN
    SET @SQL = N'ALTER INDEX ALL ON '
               + @TableName
               + N' REBUILD WITH (FILLFACTOR = '
               + CONVERT(NVARCHAR(3), @FillFactor)
               + N')';

    EXEC (@SQL);

    FETCH NEXT
    FROM  TableCursor
    INTO  @TableName;
END;

CLOSE TableCursor;
DEALLOCATE TableCursor;