SQL:在Cursor中创建内联表函数

时间:2014-02-05 10:41:57

标签: sql sql-server cursor

我正在尝试运行一个SQL脚本,它将在我的每个数据库上创建一个函数,然后使用该函数填充临时表,然后将其用作另一个用于重建索引的游标的源。但是,我遇到的问题是该函数只能在我当前连接的数据库上创建,即使我在游标中使用“使用数据库”。我已经复制了下面的脚本,该脚本是为了隔离问题而编写的(所以它还没有效率)。

IF OBJECT_ID (N'sp_index_maintenance', N'P') IS NOT NULL
DROP Proc sp_index_maintenance;
GO
Create proc sp_index_maintenance
AS
BEGIN
CREATE TABLE #T1
(
Database_Name NVARCHAR(MAX),
[Object_Name] NVARCHAR(MAX),
Index_Name NVARCHAR(MAX),
Index_ID INT,
Index_Type_Desc NVARCHAR(MAX),
AVG_Fragmentation_in_percent INT,
Fragment_Count INT,
Page_Count INT
)


DECLARE @DB as nvarchar(100);
DECLARE @Command as NVARCHAR(MAX);
DECLARE @FetchFragStatus AS NVARCHAR(max);
DECLARE @Create_Function NVARCHAR(Max);
DECLARE @Drop_Function NVARCHAR(MAX);

DECLARE DB_USE cursor for 
SELECT [Name] FROM sys.databases



OPEN DB_USE
FETCH NEXT FROM DB_USE
INTO @DB
WHILE @@FETCH_STATUS = 0
BEGIN


    SET @Command = 'USE ' + @DB 
    SET @Drop_Function = 'USE ' + @DB + ' IF OBJECT_ID (N''dbo.Index_fragmentation'', N''IF'') IS NOT NULL
    DROP Function dbo.index_fragmentation'
    SET @Create_Function = 
    'Create function dbo.index_fragmentation()
    RETURNS TABLE AS
    RETURN
    (
    SELECT 
    DB_NAME(database_ID) AS Database_Name,
    OBJECT_NAME(ps.object_id) as [Object_Name],
    i.Name AS Index_Name,
    ps.index_id, 
    index_type_desc,
    avg_fragmentation_in_percent, 
    fragment_count, 
    page_count
    FROM
    sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, ''Limited'') AS ps
    INNER JOIN
    sys.indexes AS i WITH (NOLOCK)
    ON ps.[object_id] = i.[object_id]
    AND ps.index_id = i.index_id
    WHERE database_id = DB_ID()
    --AND page_count > 500
    AND avg_fragmentation_in_percent >= 20)'
    PRINT (@command)
    EXEC sp_executesql @Command
    --PRINT (@Drop_Function)
    --EXEC (@Drop_Function)
    PRINT (@Create_function)
    EXEC sp_executesql @Create_function




FETCH NEXT FROM DB_USE
INTO @DB

END
CLOSE DB_USE
DEALLOCATE DB_USE

DECLARE DB_USE cursor for 
SELECT [Name] FROM sys.databases


OPEN DB_USE
FETCH NEXT FROM DB_USE
INTO @DB
WHILE @@FETCH_STATUS = 0
BEGIN 

SET @FetchFragStatus = 'USE ' + @DB + 
' INSERT INTO #T1 (Database_Name, Object_Name, Index_Name, index_Id, index_type_desc, avg_fragmentation_in_percent, fragment_count, page_count)
      SELECT * FROM dbo.Index_Fragmentation()'
    PRINT (@FetchFragStatus);
    EXEC (@FetchFragStatus);

    FETCH NEXT FROM DB_USE
INTO @DB

END
CLOSE DB_USE
DEALLOCATE DB_USE

--DECLARE @DB_Name NVARCHAR(100);
--DECLARE @Index_Name NVARCHAR(100);
--DECLARE @Alter_index NVARCHAR(MAX);
--DECLARE @Obj_Name NVARCHAR(MAX);
--
--DECLARE Fragmented_index_Cur Cursor For
--SELECT Database_Name, Index_Name, [Object_Name]
--FROM #T1
--
--OPEN Fragmented_index_cur
--FETCH NEXT FROM Fragmented_index_cur
--INTO
--@DB_Name, @Index_Name, @Obj_Name
--WHILE @@FETCH_STATUS = 0
--BEGIN
--SET @Command = 'USE ' + @DB_NAME
--SET @Alter_index = 'ALTER Index ' + @Index_Name + ' ON ' + @Obj_Name + ' REBUILD;'
--PRINT (@command)
--EXEC (@command)
--PRINT (@Alter_Index)
--EXEC (@Alter_index)
--
--FETCH NEXT FROM Fragmented_index_cur
--INTO @DB_Name, @Index_Name, @Obj_Name
--
--END
--CLOSE Fragmented_index_cur
--DEALLOCATE Fragmented_index_cur

SELECT * FROM #T1
RETURN;
END


EXEC sp_index_maintenance

2 个答案:

答案 0 :(得分:0)

针对所有数据库执行脚本的最佳方法是使用Microsoft的EXEC sys.sp_MSforeachdb未记录的功能,这非常方便,如果你谷歌你可以找到很多使用它的例子。通过使用此功能,您可以摆脱社区中的许多人将惩罚您的坏游标。我只需要知道何时使用它们就可以使用游标。

这是您的查询的简化版本。这将在所有数据库中获取错误索引并存储到临时表中。

 CREATE PROCEDURE myBadIndexFromAllDBs
 AS
    BEGIN
        CREATE TABLE #tempTable
            (
             Database_Name VARCHAR(250)
            ,OBJECT_NAME VARCHAR(250)
            ,Index_Name VARCHAR(250)
            ,Index_id INT
            ,index_type_desc VARCHAR(60)
            ,avg_fragmentation_in_percent FLOAT
            ,fragment_count BIGINT
            ,page_count BIGINT
            )

        EXEC sys.sp_MSforeachdb
            '
    use [?]

    INSERT INTO #tempTable
    SELECT DB_NAME(database_ID) AS Database_Name
           ,OBJECT_NAME(ps.object_id) AS [Object_Name]
           ,i.Name AS Index_Name
           ,ps.index_id
           ,index_type_desc
           ,avg_fragmentation_in_percent
           ,fragment_count
           ,page_count
        FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, ''Limited'') AS ps
        INNER JOIN sys.indexes AS i WITH ( NOLOCK )
            ON ps.[object_id] = i.[object_id]
               AND ps.index_id = i.index_id
        WHERE database_id = DB_ID()
            AND avg_fragmentation_in_percent >= 50
    '

        SELECT *
            FROM #tempTable
    END

答案 1 :(得分:0)

每个sp_executesql都在它自己的实例下运行,因此sp_executesql @Command只将其调用视为使用其他数据库。

尝试将'USE ' + @DB移至@Create_Function