SQL Server數據庫文件與文件組總結


 

文件和文件組概念

 

關於文件與文件組,簡單概括如下,詳情請參考官方文檔數據庫文件和文件組Database Files and Filegroups或更多相關資料:

 

數據文件概念:

 

每個SQL Server數據庫至少包含兩個作系統文件:一個數據文件(data file)和一個日志文件(log file)。數據文件包含數據和對象,例如表、索引、存儲過程和視圖....。日志文件包含恢復數據庫所需的所有事務的相關數據。其實在SQL Server中,數據文件分為三類,分別為:

 

主數據文件

 

        主數據文件包含數據庫的啟動信息,並指向數據庫中的其他文件。 用戶數據和對象可存儲在此文件中,也可以存儲在輔助數據文件中。每個數據庫有一個主要數據文件。 

        主要數據文件的建議文件擴展名是 .mdf。

 

輔助數據文件

 

        輔助數據文件是可選的,由用戶定義並存儲用戶數據。 通過將每個文件放在不同的磁盤驅動器上,輔助數據文件可用於將數據分散到多個磁盤上。 

        另外,如果數據庫超過了單個 Windows 文件的最大大小,可以使用次要數據文件,這樣數據庫就能繼續增長。

        輔助數據文件的建議文件擴展名是 .ndf。

 

    事務日志文件

        事務日志文件保存用於恢復數據庫的日志信息。 每個數據庫必須至少有一個日志文件。 事務日志的建議文件擴展名是 .ldf。

 

 

 

    注意:雖然文件的擴展名是可以修改的,但強烈建議不要去改擴展名。

 

 

文件組概念:

 

文件組(File Group),簡單來說,就是數據文件的組合。為了便於分配和管理,可以將數據文件集合起來,放到文件組中。每個數據庫有一個主要文件組(Primary File Group),這個是默認的。 此文件組包含主要數據文件和未放入其他文件組的所有次要文件。 可以創建用戶定義的文件組,用於將數據文件集合起來,以便於管理、數據分配和放置。總體來說,文件組是一個邏輯概念,類似於Oracle數據庫的表空間,文件組與文件的關系就類似於ORACLE中表空間與文件的概念。

 

文件組分類:

 

        主文件組(Primary)

        內存優化數據文件組(Memory Optimized Data)

        Filestream 文件組(Filestream)

        用戶自定義文件組(User-defined)

 

注意事項:PRIMARY 文件組是默認文件組,除非使用 ALTER DATABASE 語句進行了修改。另外,即使指定其它文件組為默認文件組。但系統對象和表仍然分配給 PRIMARY 文件組,而不是新的默認文件組。

   

 

 

文件組的優缺點

 

 文件組是邏輯概念,一個文件組對應一個或多個文件。創建表的時候指定文件組即可(默認,指定PRIMARY文件組),這樣可以隔離用戶對文件的依賴,耦合性就要低很多。另外,使用多個文件組和文件不僅僅是為了分散IO和提高性能,還有高可用性方面的原因。有關一個數據庫應該包含幾個文件或文件組[下面部分內容來自SQL Server中數據庫文件的存放方式,文件和文件組]

 

  數據庫中使用多個文件或文件組在高可用性方面的好處包括:

·         某文件的IO損壞,數據庫還可以保證部分在線。

·         將索引和表分開存放,假如索引文件不在線,數據依然可以被訪問。

·         歷史數據和熱數據分開,歷史歸檔數據損壞,不影響熱數據。

·         分開數據文件使得在災難恢復時僅僅恢復部分數據從而縮短了宕機時間

·         數據庫分為多個文件使得可以通過增加文件或移動文件的方式解決空間不足的問題

 

 

 從用戶的角度來說,創建對象時需要指定存儲文件組的只有三種數據對象:表,索引和大對象(LOB)

  

  使用文件組可以隔離用戶對文件的依賴,使得用戶僅僅針對文件組來建立表和索引,而不用關心實際磁盤中的文件的情況。當文件移動或修改時,由於用戶建立的表和索引是建立在文件組上的,並不依賴具體文件,因此SQL Server可以放心的管理文件。

 

  另外,使用文件組的方式來管理文件,可以使得同一文件組內的文件分布在不同的硬盤中,能夠大大提供IO性能。

 

  SQL Server根據每個文件設置的初始大小和增量值自動分配新加入的空間,假設在同一文件A設置的大小為文件B的兩倍,新增一個數據占用3頁,則按比例將2頁分配到文件A中,1頁分配到文件B

 

 

 

新增文件組

 

如下所示,創建兩個文件組,DB_Data_Groups用來存放數據(聚集索引),DB_Index_Groups用來存放索引(非聚集索引)

 

 

--創建文件組DB_Index_Groups
USE [master]
GO
ALTER DATABASE [YourSQLDb] ADD FILEGROUP [DB_Index_Groups]
GO
 
--創建文件組DB_Data_Groups
USE [master]
GO
ALTER DATABASE [YourSQLDb] ADD FILEGROUP [DB_Data_Groups]
GO
 
 
--向文件組新增文件
USE [master];
GO
ALTER DATABASE [YourSQLDb] ADD FILE ( NAME = N'YourSQLDb_IND_01', FILENAME = N'D:\SQL_DATA\YourSQLDb_IND_01.ndf' , SIZE = 16GB , FILEGROWTH = 256MB ) TO FILEGROUP [DB_Index_Groups];
GO
ALTER DATABASE [YourSQLDb] ADD FILE ( NAME = N'YourSQLDb_IND_02', FILENAME = N'E:\SQL_DATA\YourSQLDb_IND_02.ndf' , SIZE = 16GB , FILEGROWTH = 256MB ) TO FILEGROUP [DB_Index_Groups];
GO
ALTER DATABASE [YourSQLDb] ADD FILE ( NAME = N'YourSQLDb_IND_03', FILENAME = N'F:\SQL_DATA\YourSQLDb_IND_03.ndf' , SIZE = 16GB , FILEGROWTH = 256MB ) TO FILEGROUP [DB_Index_Groups];
GO
ALTER DATABASE [YourSQLDb] ADD FILE ( NAME = N'YourSQLDb_IND_04', FILENAME = N'G:\SQL_DATA\YourSQLDb_IND_04.ndf' , SIZE = 16GB , FILEGROWTH = 256MB ) TO FILEGROUP [DB_Index_Groups];
GO
 
USE [master];
GO
ALTER DATABASE [YourSQLDb] ADD FILE ( NAME = N'YourSQLDb_DATA_01', FILENAME = N'D:\SQL_DATA\YourSQLDb_DATA_01.ndf' , SIZE = 26GB , FILEGROWTH = 256MB ) TO FILEGROUP [DB_Data_Groups];
GO
ALTER DATABASE [YourSQLDb] ADD FILE ( NAME = N'YourSQLDb_DATA_02', FILENAME = N'E:\SQL_DATA\YourSQLDb_DATA_02.ndf' , SIZE = 26GB , FILEGROWTH = 256MB ) TO FILEGROUP [DB_Data_Groups];
GO
ALTER DATABASE [YourSQLDb] ADD FILE ( NAME = N'YourSQLDb_DATA_03', FILENAME = N'F:\SQL_DATA\YourSQLDb_DATA_03.ndf' , SIZE = 26GB , FILEGROWTH = 256MB ) TO FILEGROUP [DB_Data_Groups];
GO
ALTER DATABASE [YourSQLDb] ADD FILE ( NAME = N'YourSQLDb_DATA_04', FILENAME = N'G:\SQL_DATA\YourSQLDb_DATA_04.ndf' , SIZE = 26GB , FILEGROWTH = 256MB ) TO FILEGROUP [DB_Data_Groups];
GO
 
 

 

 

 

 

 

文件組間移動數據

 

如何在SQL Server的文件組直接移動數據呢?關於這個問題呢,有時候也算簡單,但是如果你沒有踩過坑,不是一個老司機的話,建議你還是看一看下面內容。對於在文件組移動數據文件,分下面幾種情況:

 

非分區表

 

1: 聚集索引表

   

    1.1 表沒有LOB對象

       

        方法:重建聚集索引和非聚集索引可以移動文件組。

 

    1.2 表擁有LOB對象

           

        方法:重建表到新文件組,將數據導入。因為重建聚集索引,不會將LOB對象移動到對應表空間。

   

2: 堆表

   

    2.1 表沒有LOB對象

 

        方法1:新建一個聚集索引,轉移文件組后,刪除聚集索引。

        方法2:重建表到新文件組,導入數據。

       

    2.1 表有LOB對象

 

        方法:重建表到新文件組,導入數據。

 

案例演示:我們要將數據庫AdventureWorks2014中的表BusinessEntityAddress 從文件組Primary移動到文件組[DB_Data_Groups],在移動之前,我們先檢查一下表BusinessEntityAddress的相關情況,如下所示,這個表屬於上述情況的1.1。

 

 

DECLARE @file_group_name  NVARCHAR(32);
 
SET @file_group_name ='PRIMARY'
 
SELECT ds.name                         AS DataSpaceName 
      ,au.type_desc                    AS AllocationDesc 
      ,au.total_pages*1.0 / 128        AS [TotalSize(MB)] 
      ,au.used_pages*1.0  / 128        AS [UsedSize(MB)] 
      ,au.data_pages*1.0  / 128        AS [DataSize(MB)] 
      ,sch.name                        AS SchemaName     
      ,obj.name                        AS ObjectName 
      ,obj.type_desc                   AS ObjectType   
      ,idx.type_desc                   AS IndexType 
      ,idx.name                        AS IndexName 
      INTO #tmp_tables
FROM sys.data_spaces AS ds 
     INNER JOIN sys.allocation_units AS au 
         ON ds.data_space_id = au.data_space_id 
     INNER JOIN sys.partitions AS pa 
         ON (AU.type IN (1, 3)  
             AND au.container_id = pa.hobt_id) 
            OR 
            (au.type = 2 
             AND au.container_id = pa.partition_id) 
     INNER JOIN sys.objects AS obj
         ON pa.object_id = obj.object_id 
     INNER JOIN sys.schemas AS sch 
         ON obj.schema_id = sch.schema_id 
     LEFT JOIN sys.indexes AS idx 
         ON pa.object_id = idx.object_id 
            AND pa.index_id = idx.index_id 
WHERE DS.name=@file_group_name
ORDER BY AU.total_pages DESC
 
 
 
SELECT  *
FROM    #tmp_tables
WHERE   ObjectType = 'USER_TABLE' AND ObjectName='Person'
ORDER BY  [TotalSize(MB)]  DESC;
 
 
SELECT  COUNT(DISTINCT ObjectName)
FROM    #tmp_tables
WHERE   ObjectType != 'SYSTEM_TABLE';
DROP TABLE #tmp_tables;

 

 

clip_image001_thumb

 

對於這種情況,我們用下面腳本生成重建索引的腳本(為什么要用腳本生成呢,實際情況中,可能有很多或大量的表需要移動表空間,此時腳本的效率就凸現出來了。)

 

--重新創建聚集索引,移動數據到文件組DB_Data_Groups
SELECT ' CREATE '
    CASE WHEN I.is_unique = 1 THEN ' UNIQUE ' ELSE '' END  +   
    I.type_desc COLLATE DATABASE_DEFAULT +' INDEX ' +    
    QUOTENAME(I.name)  + ' ON '  +   
    Schema_name(T.Schema_id)+'.'+ QUOTENAME(T.name) + ' ( '
    KeyColumns + ' )  '
    ISNULL(' INCLUDE ('+IncludedColumns+' ) ','') +  
    ISNULL(' WHERE  '+I.Filter_definition,'') + ' WITH ( '
    CASE WHEN I.is_padded = 1 THEN ' PAD_INDEX = ON ' ELSE ' PAD_INDEX = OFF ' END + ','  +  
    'FILLFACTOR = '+CONVERT(CHAR(5),CASE WHEN I.Fill_factor = 0 THEN 100 ELSE I.Fill_factor END) + ','  +  
    -- default value  
    'SORT_IN_TEMPDB = OFF '  + ','  +  
    CASE WHEN I.ignore_dup_key = 1 THEN ' IGNORE_DUP_KEY = ON ' ELSE ' IGNORE_DUP_KEY = OFF ' END + ','  +  
    CASE WHEN ST.no_recompute = 0 THEN ' STATISTICS_NORECOMPUTE = OFF ' ELSE ' STATISTICS_NORECOMPUTE = ON ' END + ','  +  
    -- default value   
    ' DROP_EXISTING = ON '  + ','  +  
    -- default value   
    ' ONLINE = OFF '  + ','  +  
   CASE WHEN I.allow_row_locks = 1 THEN ' ALLOW_ROW_LOCKS = ON ' ELSE ' ALLOW_ROW_LOCKS = OFF ' END + ','  +  
   CASE WHEN I.allow_page_locks = 1 THEN ' ALLOW_PAGE_LOCKS = ON ' ELSE ' ALLOW_PAGE_LOCKS = OFF ' END  + ' ) ON [DB_Data_Groups]; '  [CreateIndexScript]  
   --,I.is_unique
FROM sys.indexes I    
 JOIN sys.tables T ON T.Object_id = I.Object_id         
 JOIN (SELECT * FROM (   
    SELECT IC2.object_id , IC2.index_id ,   
        STUFF((SELECT ' , ' + C.name + CASE WHEN MAX(CONVERT(INT,IC1.is_descending_key)) = 1 THEN ' DESC ' ELSE ' ASC ' END 
    FROM sys.index_columns IC1   
    JOIN Sys.columns C    
       ON C.object_id = IC1.object_id    
       AND C.column_id = IC1.column_id    
       AND IC1.is_included_column = 0   
    WHERE IC1.object_id = IC2.object_id    
       AND IC1.index_id = IC2.index_id    
    GROUP BY IC1.object_id,C.name,index_id   
    ORDER BY MAX(IC1.key_ordinal)   
       FOR XML PATH('')), 1, 2, '') KeyColumns    
    FROM sys.index_columns IC2    
    GROUP BY IC2.object_id ,IC2.index_id) tmp3 )tmp4    
  ON I.object_id = tmp4.object_id AND I.Index_id = tmp4.index_id   
 JOIN sys.stats ST ON ST.object_id = I.object_id AND ST.stats_id = I.index_id    
 JOIN sys.data_spaces DS ON I.data_space_id=DS.data_space_id    
 JOIN sys.filegroups FG ON I.data_space_id=FG.data_space_id    
 LEFT JOIN (SELECT * FROM (    
    SELECT IC2.object_id , IC2.index_id ,    
        STUFF((SELECT ' , ' + C.name  
    FROM sys.index_columns IC1    
    JOIN Sys.columns C     
       ON C.object_id = IC1.object_id     
       AND C.column_id = IC1.column_id     
       AND IC1.is_included_column = 1    
    WHERE IC1.object_id = IC2.object_id     
       AND IC1.index_id = IC2.index_id     
    GROUP BY IC1.object_id,C.name,index_id    
       FOR XML PATH('')), 1, 2, '') IncludedColumns     
   FROM sys.index_columns IC2     
   GROUP BY IC2.object_id ,IC2.index_id) tmp1    
   WHERE IncludedColumns IS NOT NULL ) tmp2     
ON tmp2.object_id = I.object_id AND tmp2.index_id = I.index_id    
WHERE  I.type=1 AND i.data_space_id=1 
AND t.name ='BusinessEntityAddress'
AND FG.name !='DB_Data_Groups'

 

 

此時上面腳本會生成下面腳本

 

 

CREATE  UNIQUE CLUSTERED INDEX [PK_BusinessEntityAddress_BusinessEntityID_AddressID_AddressTypeID] 
ON Person.[BusinessEntityAddress] (  BusinessEntityID ASC  , AddressID ASC  , AddressTypeID ASC  )   
WITH (  PAD_INDEX = OFF ,FILLFACTOR = 100  ,SORT_IN_TEMPDB = OFF , IGNORE_DUP_KEY = OFF , STATISTICS_NORECOMPUTE = OFF ,
    DROP_EXISTING = ON , ONLINE = OFF , ALLOW_ROW_LOCKS = ON , ALLOW_PAGE_LOCKS = ON  ) ON [DB_Data_Groups]; 

 

 

下面腳本生成對應表的非聚集索引,將其轉移到表空間[DB_Index_Groups]

 

WITH NoClustTable AS
(
SELECT DISTINCT
        SCHEMA_NAME(so.schema_id) AS 'SchemaName' ,
        OBJECT_NAME(so.object_id) AS 'TableName' ,
        so.object_id AS 'object_id' ,
        CASE OBJECTPROPERTY(MAX(so.object_id), 'TableHasClustIndex')
          WHEN 0 THEN COUNT(si.index_id) - 1
          ELSE COUNT(si.index_id)
        END AS 'IndexCount' 
FROM    sys.objects so ( NOLOCK )
        JOIN sys.indexes si ( NOLOCK ) ON so.object_id = si.object_id
                                          AND so.type IN ( N'U', N'V' )
        JOIN sysindexes dmv ( NOLOCK ) ON so.object_id = dmv.id
                                          AND si.index_id = dmv.indid
        FULL OUTER JOIN ( SELECT    object_id ,
                                    COUNT(1) AS ColumnCount
                          FROM      sys.columns (NOLOCK)
                          GROUP BY  object_id
                        ) d ON d.object_id = so.object_id
WHERE   so.is_ms_shipped = 0
        AND so.object_id NOT IN (
        SELECT  major_id
        FROM    sys.extended_properties (NOLOCK)
        WHERE   name = N'microsoft_database_tools_support' )
        AND INDEXPROPERTY(so.object_id, si.name, 'IsStatistics') = 0
GROUP BY so.schema_id ,
        so.object_id
HAVING  ( OBJECTPROPERTY(MAX(so.object_id), 'TableHasClustIndex') = 0
          AND COUNT(si.index_id) - 1 > 0
        )
)
 
SELECT ' CREATE '
    CASE WHEN I.is_unique = 1 THEN ' UNIQUE ' ELSE '' END  +   
    I.type_desc COLLATE DATABASE_DEFAULT +' INDEX ' +    
    QUOTENAME(I.name)  + ' ON '  +   
    Schema_name(T.Schema_id)+'.'+ QUOTENAME(T.name) + ' ( '
    KeyColumns + ' )  '
    ISNULL(' INCLUDE ('+IncludedColumns+' ) ','') +  
    ISNULL(' WHERE  '+I.Filter_definition,'') + ' WITH ( '
    CASE WHEN I.is_padded = 1 THEN ' PAD_INDEX = ON ' ELSE ' PAD_INDEX = OFF ' END + ','  +  
    'FILLFACTOR = '+CONVERT(CHAR(5),CASE WHEN I.Fill_factor = 0 THEN 100 ELSE I.Fill_factor END) + ','  +  
    -- default value  
    'SORT_IN_TEMPDB = OFF '  + ','  +  
    CASE WHEN I.ignore_dup_key = 1 THEN ' IGNORE_DUP_KEY = ON ' ELSE ' IGNORE_DUP_KEY = OFF ' END + ','  +  
    CASE WHEN ST.no_recompute = 0 THEN ' STATISTICS_NORECOMPUTE = OFF ' ELSE ' STATISTICS_NORECOMPUTE = ON ' END + ','  +  
    -- default value   
    ' DROP_EXISTING = ON '  + ','  +  
    -- default value   
    ' ONLINE = OFF '  + ','  +  
   CASE WHEN I.allow_row_locks = 1 THEN ' ALLOW_ROW_LOCKS = ON ' ELSE ' ALLOW_ROW_LOCKS = OFF ' END + ','  +  
   CASE WHEN I.allow_page_locks = 1 THEN ' ALLOW_PAGE_LOCKS = ON ' ELSE ' ALLOW_PAGE_LOCKS = OFF ' END  + ' ) ON [DB_Index_Groups]; '  [CreateIndexScript]  
   --,I.is_unique
FROM sys.indexes I    
 JOIN sys.tables T ON T.Object_id = I.Object_id     
 --JOIN sys.sysindexes SI ON I.Object_id = SI.id AND I.index_id = SI.indid    
 JOIN (SELECT * FROM (   
    SELECT IC2.object_id , IC2.index_id ,   
        STUFF((SELECT ' , ' + C.name + CASE WHEN MAX(CONVERT(INT,IC1.is_descending_key)) = 1 THEN ' DESC ' ELSE ' ASC ' END 
    FROM sys.index_columns IC1   
    JOIN Sys.columns C    
       ON C.object_id = IC1.object_id    
       AND C.column_id = IC1.column_id    
       AND IC1.is_included_column = 0   
    WHERE IC1.object_id = IC2.object_id    
       AND IC1.index_id = IC2.index_id    
    GROUP BY IC1.object_id,C.name,index_id   
    ORDER BY MAX(IC1.key_ordinal)   
       FOR XML PATH('')), 1, 2, '') KeyColumns    
    FROM sys.index_columns IC2    
    GROUP BY IC2.object_id ,IC2.index_id) tmp3 )tmp4    
  ON I.object_id = tmp4.object_id AND I.Index_id = tmp4.index_id   
 JOIN sys.stats ST ON ST.object_id = I.object_id AND ST.stats_id = I.index_id    
 JOIN sys.data_spaces DS ON I.data_space_id=DS.data_space_id    
 JOIN sys.filegroups FG ON I.data_space_id=FG.data_space_id    
 LEFT JOIN (SELECT * FROM (    
    SELECT IC2.object_id , IC2.index_id ,    
        STUFF((SELECT ' , ' + C.name  
    FROM sys.index_columns IC1    
    JOIN Sys.columns C     
       ON C.object_id = IC1.object_id     
       AND C.column_id = IC1.column_id     
       AND IC1.is_included_column = 1    
    WHERE IC1.object_id = IC2.object_id     
       AND IC1.index_id = IC2.index_id     
    GROUP BY IC1.object_id,C.name,index_id    
       FOR XML PATH('')), 1, 2, '') IncludedColumns     
   FROM sys.index_columns IC2     
   GROUP BY IC2.object_id ,IC2.index_id) tmp1    
   WHERE IncludedColumns IS NOT NULL ) tmp2     
ON tmp2.object_id = I.object_id AND tmp2.index_id = I.index_id    
WHERE   T.object_id NOT IN (
        SELECT  major_id
        FROM    sys.extended_properties (NOLOCK)
        WHERE   name = N'microsoft_database_tools_support' )
        AND I.type = 2 AND T.object_id NOT IN (SELECT object_id  FROM NoClustTable)
        AND i.data_space_id =1
        AND T.name='BusinessEntityAddress'

 

 

 

CREATE UNIQUE NONCLUSTERED INDEX [AK_BusinessEntityAddress_rowguid] ON Person.[BusinessEntityAddress] (  rowguid ASC  )   WITH (  PAD_INDEX = OFF ,FILLFACTOR = 100  ,SORT_IN_TEMPDB = OFF , IGNORE_DUP_KEY = OFF , STATISTICS_NORECOMPUTE = OFF , DROP_EXISTING = ON , ONLINE = OFF , ALLOW_ROW_LOCKS = ON , ALLOW_PAGE_LOCKS = ON  ) ON [DB_Index_Groups]; 
CREATE NONCLUSTERED INDEX [IX_BusinessEntityAddress_AddressID] ON Person.[BusinessEntityAddress] (  AddressID ASC  )   WITH (  PAD_INDEX = OFF ,FILLFACTOR = 100  ,SORT_IN_TEMPDB = OFF , IGNORE_DUP_KEY = OFF , STATISTICS_NORECOMPUTE = OFF , DROP_EXISTING = ON , ONLINE = OFF , ALLOW_ROW_LOCKS = ON , ALLOW_PAGE_LOCKS = ON  ) ON [DB_Index_Groups]; 
CREATE NONCLUSTERED INDEX [IX_BusinessEntityAddress_AddressTypeID] ON Person.[BusinessEntityAddress] (  AddressTypeID ASC  )   WITH (  PAD_INDEX = OFF ,FILLFACTOR = 100  ,SORT_IN_TEMPDB = OFF , IGNORE_DUP_KEY = OFF , STATISTICS_NORECOMPUTE = OFF , DROP_EXISTING = ON , ONLINE = OFF , ALLOW_ROW_LOCKS = ON , ALLOW_PAGE_LOCKS = ON  ) ON [DB_Index_Groups]; 
 

 

 

對於聚集索引表,如果擁有LOB對象,那么上述方法不完全可行,我們以Person.Person為例,這個表有LOB對象。執行上面操作后,你會發現LOB_DATA 還是位於PRIMARY文件組。

 

 

clip_image002_thumb

 

 

 

這個是因為通過重建索引的方式是無法移動LOB對象,如下所示,生成該表的創建腳本,你會發現TEXTIMAGE_ON位於[PRIMARY]文件組,  這種情形,只能通過重建表,導入數據的方式。

 

clip_image003_thumb

 

 

個人在實際操作中,總結了一下簡單步驟:

 

 

1:使用Generate And Publish Scripts 生成含有LOB對象的表的腳本(具體細節有很多需要注意的地方),修改對應的文件組(例如將PRIMARY 改為[DB_Data_Groups] 或[DB_Index_Groups]

 

 

2確認表單獨授權給了那些用戶或角色(如果是默認角色,可以忽略),新建這些表必須考慮權限問題,否則重建這些表就會出現權限問題。

 

 

--查看某個對象被授予了那些用戶或角色

SELECT o.name as ObjectName,u.name as objname,

CASE o.type

     WHEN 'AF'  THEN 'Aggregate Function(CLR)'

     WHEN 'C'   THEN 'Check Constraint'

     WHEN 'D'   THEN 'Default Constraint'

     WHEN 'F'   THEN 'Foreign Key Constraint'

     WHEN 'FN'  THEN 'Scalar Function'

     WHEN 'FS'  THEN 'Assembly (CLR) Scalar-Function'

     WHEN 'FT'  THEN 'Assembly (CLR) Table-Valued Function'

     WHEN 'IT'  THEN 'Internal Table'

     WHEN 'K'   THEN 'Primary Key or Unique Constraint'

     WHEN 'U'   THEN 'User Table'

     WHEN 'P'   THEN 'Stored Procedure'

     WHEN 'PC'  THEN 'Assembly (CLR) Stored Procedure'

     WHEN 'S'   THEN 'System Table'

     WHEN 'SN'  THEN 'Synonym'

     WHEN 'V'   THEN 'View'

     WHEN 'X'   THEN 'Extended Store Procedure'

     WHEN 'TF'  THEN 'Table Function'

     ELSE 'OTHER'

END AS TYPE,

CASE WHEN  p.ACTION = 26  AND p.PROTECTTYPE = 205 THEN '' ELSE '' END AS 'REFERENCES',

CASE WHEN  p.ACTION = 193 AND p.PROTECTTYPE = 205 THEN '' ELSE '' END AS 'SELECT',

CASE WHEN  p.ACTION = 195 AND p.PROTECTTYPE = 205 THEN '' ELSE '' END AS 'DELETE',

CASE WHEN  p.ACTION = 196 AND p.PROTECTTYPE = 205 THEN '' ELSE '' END AS 'INSERT',

CASE WHEN  p.ACTION = 197 AND p.PROTECTTYPE = 205 THEN '' ELSE '' END AS 'UPDATE',

CASE WHEN  p.ACTION = 224 AND p.PROTECTTYPE = 205 THEN '' ELSE '' END AS 'EXECUTE',

CASE p.PROTECTTYPE

    WHEN 204 THEN 'GRANT_W_GRANT '

    WHEN 205 THEN 'GRANT'

    WHEN 206 THEN 'DENY'

    ELSE 'OTHER'

END AS PROTECTTYPE

FROM sysprotects p

 INNER JOIN sysobjects o on p.id = o.id

 INNER JOIN sysusers u on p.uid = u.uid

WHERE o.name='Person.Person'  --具體表名

 

 

3:我們需要將原表重命名(包括約束, 如下所示,使用下面腳本生成對應的腳本,執行后修改表名或約束名稱),然后執行步驟1里面生成的腳本,創建新表()

 

 

--USE [AdventureWorks2014]
--GO
 
DECLARE @ori_tab_name NVARCHAR(64);
DECLARE @old_tab_name NVARCHAR(64);
 
SET @ori_tab_name = 'Person.Person';
SET @old_tab_name = RTRIM(@ori_tab_name) + '_old';
 
 
 
 
 
SELECT  'sp_rename ''' + SCHEMA_NAME(schema_id) +'.' + name + ''' ,'''
        + name + '_old'';' + CHAR(10) + 'GO'
FROM    sys.key_constraints
WHERE   parent_object_id = OBJECT_ID(@ori_tab_name);
 
 
SELECT  'sp_rename ''' + SCHEMA_NAME(schema_id) +'.' + name + ''', ''' 
        + name + '_old'';' + CHAR(10) + 'GO'
FROM    sys.default_constraints
WHERE   parent_object_id = OBJECT_ID(@ori_tab_name);
 
 
SELECT 'sp_rename ''' +  SCHEMA_NAME(schema_id) +'.' + name + ''', ''' 
        + name + '_old'';' + CHAR(10) + 'GO'
FROM sys.foreign_keys
WHERE parent_object_id =OBJECT_ID(@ori_tab_name);
 
 
SELECT 'sp_rename '''  + SCHEMA_NAME(schema_id) + '.' + name +''', ''' 
        + name + '_old'';'  + CHAR(10) + 'GO'
FROM sys.check_constraints
WHERE parent_object_id =OBJECT_ID(@ori_tab_name);
 
 
SELECT  'sp_rename ''' +SCHEMA_NAME(schema_id) + '.' +  name + ''', ''' + name + '_old'';'
FROM    sys.tables
WHERE   object_id = OBJECT_ID(@ori_tab_name);

 

 

 

注意:有外鍵約束指向這個表的,還需要額外調整。

 

 

 

INSERT  INTO Person.Person
        ( BusinessEntityID ,
          PersonType ,
          NameStyle ,
          Title ,
          FirstName ,
          MiddleName ,
          LastName ,
          Suffix ,
          EmailPromotion ,
          AdditionalContactInfo ,
          Demographics ,
          rowguid ,
          ModifiedDate
        )
        SELECT  BusinessEntityID ,
                PersonType ,
                NameStyle ,
                Title ,
                FirstName ,
                MiddleName ,
                LastName ,
                Suffix ,
                EmailPromotion ,
                AdditionalContactInfo ,
                Demographics ,
                rowguid ,
                ModifiedDate
        FROM    Person.Person_old;
 
sp_spaceused 'Person.Person';
GO
sp_spaceused 'Person.Person_old';
GO
 
TRUNCATE TABLE Person.Person_old;
GO
DROP TABLE Person.Person_old;
GO

 

 

如果遇到有自增字段,還必須考慮插入自增字段相關值。 總的來說,這樣轉移文件組是一個相當麻煩的事情。對於堆表,這里也沒啥好介紹的。無法是細節問題需要注意。轉移文件組倒沒有什么技術含量。還有一個因素就是這樣的操作會影響業務。

 

 

 

SET IDENTITY_INSERT [dbo].[YourTableName] ON;

GO

INSERT INTO [dbo].[YourTableName](............)

SELECT .............

GO

SET IDENTITY_INSERT [dbo].[YourTableName] OFF;

GO

 

 

 

 

 

分區表   

 

對於分區表而言,也可以通過重新創建聚集索引和非聚集索引來移動數據。另外,就是重新創建該表,轉移數據。這個大同小異。當然,分區表還有切換分區(Switch Partition)這個機制來轉移數據。

 

 

 

 

刪除文件和文件組

 

刪除文件前,必須確保文件為空,否則就會報The file 'xxxx' cannot be removed because it is not empty.",所以,首先必須轉移文件當中的對象,然后收縮文件,最后刪除文件。

 

DBCC SHRINKFILE (FileName, EMPTYFILE);

 

USE [AdventureWorks2014]

GO

ALTER DATABASE [AdventureWorks2014]  REMOVE FILE [AdventureWorks2014_DATA_01]

GO

 

 

正常情況下,刪除文件組對應的文件后,就可以刪除文件組。

 

ALTER DATABASE [AdventureWorks2014] REMOVE FILEGROUP [DB_Data_Groups]

GO

 

但是如果存在分區表,你可能無法刪除文件組。因為還有幾個東西依賴文件組,一是分區方案,二是使用該分區方案的分區表。

     

所以要刪除分區方案才能刪除文件組。但要刪除分區方案之前要先更改依賴它的分區表,使其不依賴它。 這個主要是更改分區表的分區列,使其不使用分區方案,如果實在不會更改,在表里數據已經備份的前提下,可以直接刪除表來解決。  然后再刪除分區表方案,最后就可以直接刪除文件組了。

 

 

1、修改分區表,使其不依賴分區方案。

 

2、刪除分區方案(依賴要刪除的文件組)。

 

DROP PARTITION SCHEME [PART_FUNC_SCHEME_NAME]

 

3、直接刪除文件組。

 

ALTER DATABASE [DataBaseName] REMOVE FILEGROUP [xxxx]

 

 

參考資料:

 

https://www.sqlskills.com/blogs/paul/files-and-filegroups-survey-results/

https://docs.microsoft.com/zh-cn/sql/relational-databases/databases/database-files-and-filegroups?view=sql-server-2017

http://www.cnblogs.com/CareySon/archive/2011/12/26/2301597.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM