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;

对于这种情况,我们用下面脚本生成重建索引的脚本(为什么要用脚本生成呢,实际情况中,可能有很多或大量的表需要移动表空间,此时脚本的效率就凸现出来了。)
--重新创建聚集索引,移动数据到文件组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文件组。

这个是因为通过重建索引的方式是无法移动LOB对象,如下所示,生成该表的创建脚本,你会发现TEXTIMAGE_ON位于[PRIMARY]文件组, 这种情形,只能通过重建表,导入数据的方式。

个人在实际操作中,总结了一下简单步骤:
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
SQL Server数据库文件与文件组总结的更多相关文章
- SQL SERVER数据库删除LOG文件和清空日志的方案
原文:SQL SERVER数据库删除LOG文件和清空日志的方案 数据库在使用过程中会使日志文件不断增加,使得数据库的性能下降,并且占用大量的磁盘空间.SQL Server数据库都有log文件,log文 ...
- [转]SQL SERVER数据库删除LOG文件和清空日志的方案
本文转自:https://www.cnblogs.com/ShaYeBlog/archive/2012/09/04/2670505.html 数据库在使用过程中会使日志文件不断增加,使得数据库的性能下 ...
- SQL Server数据库状态和文件状态
数据库状态 (database states) 查询数据库的当前状态 : 1.查询所有数据库的状态 ,通过sys.databases目录视图的state_desc列 user master go se ...
- sql server 数据库 只有mdf文件,如何附加
直接附加就可以,SQL 会提示LOG文件找不到,可删除提示的错误行,只附加MDF文件,附加成功后,SQL会自动创建LOG文件.
- 关于SQL Server中分区表的文件与文件组的删除(转)
在SQL Server中对表进行分区管理时,必定涉及到文件与文件组,关于文件与文件组如何创建在网上资料很多,我博客里也有两篇相关转载文件,可以看看,我这就不再细述,这里主要讲几个一般网上很少讲到的东西 ...
- 5、SQL Server数据库、T-SQL
SQL Server数据库基础 一.安装SQL Server数据库 setup.exe->安装->全新SQL Server独立安装或向现有安装添加功能->输入序列号->下一步- ...
- Sql Server数据库备份和恢复:原理篇
本文与您探讨为什么Sql Server有完整备份.差异备份和事务日志备份三种备份方式,以及为什么数据库又有简单模式.完整模式和大容量日志模式这三种恢复模式.本文内容适用于2005以上所有版本的Sql ...
- SQL Server数据库 bcp导出备份文件应用
/** * 授权 */ EXEC sp_configure 'show advanced options',1; go reconfigure; go exec sp_configure 'xp_ ...
- 人人都是 DBA(V)SQL Server 数据库文件
SQL Server 数据库安装后会包含 4 个默认系统数据库:master, model, msdb, tempdb. SELECT [name] ,database_id ,suser_sname ...
随机推荐
- 项目分层-----MVC
MVC设计模式:modle层,view层,controller层 以前学习的servlet其实就是一个java类,或者说经过规范的java类,实际进行跳转时,还是要在web.xml文件中配置才能正常跳 ...
- 必看!如何让你的LBS服务性能提升十倍!
本文由云+社区发表 作者:腾讯云数据库团队 随着国内服务共享化的热潮普及,共享单车,共享雨伞,共享充电宝等各种服务如雨后春笋,随之而来的LBS服务定位问题成为了后端服务的一个挑战.MongoDB对LB ...
- Postgres全文搜索功能
当构建一个Web应用时,经常被要求加上搜索功能.其实有时候我们也不知道我要搜索个啥,反正就是要有这个功能.搜索确实很重要的特性,这也是为什么像Elasticsearch和Solr这样基于Lucene的 ...
- Sql server数据库定时任务,数据库作业,数据库定时任务
当需要周期性的去执行一个方法时,我们可以先写好方法,然后交给数据库去完成就可以的. 步骤:首先打开SQL数据库中SQLServer代理-->右键作业-->新建作业: 如果SQL Serve ...
- cmd命令重定向到剪切板
Windows下 使用系统自带的 clip 命令. # 位于 C:\Windows\system32\clip.exe. 示例: # 将字符串 Hello 放入 Windows 剪贴板 echo He ...
- Loj #2192. 「SHOI2014」概率充电器
Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...
- mysql建数据库的字符集与排序规则
1.字符集说明: 一般选择utf8.下面介绍一下utf8与utfmb4的区别. utf8mb4兼容utf8,且比utf8能表示更多的字符.至于什么时候用,看你的做什么项目了,到http://blog. ...
- ASP.NET Aries 高级开发教程:Excel导入配置之规则说明(下)
前言: 前面两篇都是大体介绍流程,有一些配置细节,没有细说,这里用一篇补上. 1.Excel配置项 起始行索引.列头跨行数: 对于自定义的Excel导入模板(有时候模板是由客户提供,模板的规则很乱) ...
- 免费申请使用IBM Cloud Lite(轻量套餐) 续
之前尝试申请了IBM的轻量套餐,过程很简单,操作起来也比较方便,就是能够用到的地方不多,虽说几乎是无限流量且永久免费,我能做的也只是做个小网站 免费用户默认的是轻量应用服务,如果需要功能更多更全的应用 ...
- 重新发现MATLAB
现场编辑 创建不仅捕获代码的脚本 - 它们讲述了可以与他人共享的故事.自动化的上下文提示可让您在编程时快速移动,并将结果和可视化与代码一起显示. 学到更多 访问MATLAB实时脚本库 ...