无法通过EF ExecuteSqlCommand重建索引

时间:2017-01-05 04:53:37

标签: sql-server entity-framework tsql azure-sql-database ssms

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

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);

我收到以下错误:

  

'TableCursor'附近的语法不正确。

ReadResourceContent的实现细节是无关紧要的 - 我正在运行其他任意SQL,它运行正常。

为什么会发生这种情况以及需要改变什么?

1 个答案:

答案 0 :(得分:2)

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

使用分号终止语句/查询的一个好处是,当存在诸如不一致的行结尾之类的问题时,SQL Server将更容易解析查询批处理,这可能是此处问题的根本原因。我猜测根本原因是不一致的行结束,因为您可以通过SSMS对Azure数据库运行脚本。如果Azure SQL数据库上需要分号,那么即使通过SSMS运行也会产生错误。很可能SSMS在提交批处理之前使得行结束一致,通过.NET代码运行的东西不会自动为你完成。

其他说明:

  • 最好不要混用VARCHARNVARCHAR(即使数据类型优先顺序最终会将其全部转换为NVARCHAR)。由于您正在处理标识符(即数据库中sysname类型的表名称,这是NVARCHAR(128)的别名),理想情况下所有标识符都应为NVARCHAR,所有字符串文字都以前缀为N

  • 在大多数情况下,特别是对于具有标识列的表,FILLFACTOR为80是可怕的,您应该使用100.使用NEWID()然后从90开始并仅降低如有必要。 NEWSEQUENTIALID()使用100。

  • 在声明游标时,如果查询引用实际表而不是临时表,则使用STATIC关键字以便不锁定基表。并且通常也可以使用以下关键字: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;