You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

SQL Server 2014:无需备份与向导,用脚本克隆带数据的数据库

在SQL Server 2014中无备份创建带数据的数据库副本(脚本方式)

首先得明确:DBCC CLONEDATABASE确实只复制架构、统计信息和元数据,完全不会包含用户数据,所以要实现带数据的副本,咱们可以用以下几种纯脚本方式来搞定:

方法1:手动复制架构+批量导入数据

这个方法分两步走:先复刻源库的所有架构对象(表、约束、索引、存储过程等),再把源库的数据批量插入到目标库的对应表中。

步骤1:创建空的目标数据库

先搭一个和源库文件配置匹配的空库(可以根据自己的存储路径调整参数):

CREATE DATABASE [YourTargetDB]
ON PRIMARY 
( NAME = N'YourTargetDB', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\YourTargetDB.mdf' , SIZE = 5120KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
LOG ON 
( NAME = N'YourTargetDB_log', FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\YourTargetDB_log.ldf' , SIZE = 1024KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
GO

步骤2:复制源库的架构对象

可以通过系统视图自动生成所有对象的创建脚本,这里先给你表、主键约束和非主键索引的生成逻辑(你可以扩展到视图、存储过程等对象):

-- 生成所有用户表的CREATE TABLE脚本(包含主键约束)
SELECT 
    'USE [YourTargetDB]; CREATE TABLE ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' (' +
    STRING_AGG(
        QUOTENAME(c.name) + ' ' + 
        CASE WHEN c.system_type_id = c.user_type_id THEN t.name ELSE ut.name END +
        CASE WHEN c.max_length <> -1 THEN '(' + CASE WHEN c.system_type_id IN (167, 175, 231, 239) THEN CAST(c.max_length/2 AS VARCHAR) ELSE CAST(c.max_length AS VARCHAR) END + ')' ELSE '' END +
        CASE WHEN c.is_nullable = 0 THEN ' NOT NULL' ELSE ' NULL' END +
        CASE WHEN dc.name IS NOT NULL THEN ' DEFAULT ' + dc.definition ELSE '' END,
        ', '
    ) + 
    CASE WHEN pk.name IS NOT NULL THEN ', CONSTRAINT ' + QUOTENAME(pk.name) + ' PRIMARY KEY (' + STRING_AGG(QUOTENAME(ic.name), ', ') + ')' ELSE '' END +
    ');' AS CreateTableScript
FROM sys.tables t
JOIN sys.schemas s ON t.schema_id = s.schema_id
JOIN sys.columns c ON t.object_id = c.object_id
JOIN sys.types ut ON c.user_type_id = ut.user_type_id
LEFT JOIN sys.default_constraints dc ON c.object_id = dc.parent_object_id AND c.column_id = dc.parent_column_id
LEFT JOIN sys.key_constraints pk ON t.object_id = pk.parent_object_id AND pk.type = 'PK'
LEFT JOIN sys.index_columns ic ON pk.parent_object_id = ic.object_id AND pk.unique_index_id = ic.index_id
GROUP BY s.name, t.name, pk.name
ORDER BY s.name, t.name;

-- 生成所有非主键索引的创建脚本
SELECT 
    'USE [YourTargetDB]; CREATE ' + 
    CASE WHEN i.is_unique = 1 THEN 'UNIQUE ' ELSE '' END +
    i.type_desc + ' INDEX ' + QUOTENAME(i.name) + 
    ' ON ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' (' +
    STRING_AGG(QUOTENAME(c.name) + CASE WHEN ic.is_descending_key = 1 THEN ' DESC' ELSE ' ASC' END, ', ') +
    ');' AS CreateIndexScript
FROM sys.indexes i
JOIN sys.tables t ON i.object_id = t.object_id
JOIN sys.schemas s ON t.schema_id = s.schema_id
JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
WHERE i.type <> 0 -- 排除堆表的默认索引
  AND i.is_primary_key = 0
GROUP BY s.name, t.name, i.name, i.is_unique, i.type_desc
ORDER BY s.name, t.name;

把生成的脚本执行一遍,目标库就有和源库完全一致的表结构、约束和索引了。

步骤3:批量导入数据

对每个用户表执行INSERT INTO ... SELECT语句就行,要是表有自增列,记得先开IDENTITY_INSERT

-- 示例:复制dbo.Customers表的数据
USE [YourTargetDB];
INSERT INTO dbo.Customers
SELECT * FROM [YourSourceDB].dbo.Customers;

-- 带自增列的表处理方式
USE [YourTargetDB];
SET IDENTITY_INSERT dbo.Orders ON;
INSERT INTO dbo.Orders (OrderID, CustomerID, OrderDate)
SELECT OrderID, CustomerID, OrderDate FROM [YourSourceDB].dbo.Orders;
SET IDENTITY_INSERT dbo.Orders OFF;

嫌手动写麻烦?可以用下面的脚本批量生成所有表的插入语句:

SELECT 
    'USE [YourTargetDB]; ' +
    CASE WHEN EXISTS (SELECT 1 FROM sys.identity_columns WHERE object_id = t.object_id) THEN
        'SET IDENTITY_INSERT ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' ON; '
    ELSE '' END +
    'INSERT INTO ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' SELECT * FROM [YourSourceDB].' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ';' +
    CASE WHEN EXISTS (SELECT 1 FROM sys.identity_columns WHERE object_id = t.object_id) THEN
        ' SET IDENTITY_INSERT ' + QUOTENAME(s.name) + '.' + QUOTENAME(t.name) + ' OFF;'
    ELSE '' END AS InsertDataScript
FROM sys.tables t
JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE t.is_ms_shipped = 0 -- 排除系统表
ORDER BY s.name, t.name;

方法2:使用SELECT INTO快速复制表(适合小型数据库)

如果你的数据库不大,且能接受先复制表(自动创建结构)再补全约束/索引的方式,SELECT INTO会更快捷:

-- 先创建目标库(同方法1)
CREATE DATABASE [YourTargetDB];
GO

-- 复制所有用户表到目标库(自动创建表,但不包含非主键约束、索引、触发器)
USE [YourTargetDB];
GO
SELECT * INTO dbo.Customers FROM [YourSourceDB].dbo.Customers;
SELECT * INTO dbo.Orders FROM [YourSourceDB].dbo.Orders;
-- ... 其他表依次类推

-- 之后需要手动添加缺失的约束、索引和触发器(可以用方法1中的脚本生成)

注意:SELECT INTO会自动创建表,但不会复制默认约束、外键、非主键索引、触发器这些对象,所以后续得手动补全。

一些注意事项

  • 执行脚本前,确保你有足够的权限(CREATE DATABASE、源库的SELECT权限、目标库的INSERT权限等)。
  • 如果源库数据量很大,建议在业务非高峰时段执行,避免影响线上业务。
  • 有外键约束的表要注意插入顺序:先插父表数据,再插子表数据,否则会触发外键冲突。

内容的提问来源于stack exchange,提问作者carlosm

火山引擎 最新活动