ChatGPT解决这个技术问题 Extra ChatGPT

如何在 SQL Server 2008 Express 的同一台服务器上克隆 SQL Server 数据库?

我有一个 MS SQL Server 2008 Express 系统,其中包含一个我想“复制和重命名”(用于测试目的)的数据库,但我不知道实现此目的的简单方法。

我注意到在 R2 版本的 SQL Server 中有一个复制数据库向导,但遗憾的是我无法升级。

有问题的数据库围绕着一个演出。我试图将要复制到新数据库中的数据库的备份还原,但没有成功。

恢复备份应该可以工作。您能否提供有关如何失败的更多详细信息?
我意识到从备份恢复时我犯了一个错误。我首先创建了一个新的空数据库并尝试从那里恢复备份。我应该做的是打开恢复对话框并在其中输入新数据库的名称,而不是先创建它。这样做很好地克隆了数据库!
当我尝试以不同的名称恢复数据库时,它只是给了我:“数据库'我的数据库(新)'的恢复失败。......由于数据库正在使用中,无法获得独占访问权限。” - 新名称下的数据库不存在,怎么可能使用?!

M
MarredCheese

安装 Microsoft SQL Management Studio,您可以从 Microsoft 网站免费下载: 版本 2008 Microsoft SQL Management Studio 2008 是 SQL Server 2008 Express 的一部分,具有高级服务版本 2012 单击下载按钮并检查 ENU\x64\SQLManagementStudio_x64_ENU.exe 版本 2014 单击下载按钮并检查 MgmtStudio 64BIT\SQLManagementStudio_x64_ENU.exe 打开 Microsoft SQL Management Studio。将原始数据库备份到 .BAK 文件(db -> 任务 -> 备份)。使用新名称(克隆)创建空数据库。请注意下面的注释,因为这是可选的。单击以克隆数据库并打开恢复对话框(见图) 选择设备并添加步骤 3 中的备份文件。将目标更改为测试数据库 更改数据库文件的位置,它必须与原始文件不同。您可以直接在文本框中输入,只需添加后缀。 (注意:顺序很重要。选择复选框,然后更改文件名。)检查 WITH REPLACE 和 WITH KEEP_REPLICATION


1. 不要创建空数据库并将 .bak 文件还原到其中。 2. 使用可通过右键单击 SQL Server Management Studio 的“数据库”分支访问的“恢复数据库”选项,并在提供要恢复的源的同时提供数据库名称。参考:stackoverflow.com/questions/10204480/…
Microsoft SQL Management Studio - 免费
不起作用 - “无法获得独占访问权限,因为数据库正在使用中”。
我还必须取消选中“还原前进行尾日志备份”。这在默认情况下被选中,并导致“无法获得独占访问权限,因为数据库正在使用中”错误。
我的原始数据库卡在“正在恢复”中
u
user3071284

右键单击要克隆的数据库,单击 Tasks,单击 Copy Database...。按照向导操作,您就完成了。


我认为这仅在 SQL Server 的 R2 版本中可用 :-(
下面是它在 express 中的工作方式:stackoverflow.com/questions/4269450/…
如果您的数据库中有加密对象,这将不起作用。
我会说,重点实际上是在哪里做呢?你描述的很直观。我之前已经在某些工具(0xDBE、Visual Studio SQL Server 对象资源管理器)中尝试过,但在那里没有找到这样的功能。
不可能!任务 -> 没有复制数据库的菜单项
R
RMalke

您可以尝试分离数据库,在命令提示符下将文件复制到新名称,然后附加两个数据库。

在 SQL 中:

USE master;
GO 
EXEC sp_detach_db
    @dbname = N'OriginalDB';
GO

在命令提示符下(为了这个示例,我简化了文件路径):

copy c:\OriginalDB.mdf c:\NewDB.mdf
copy c:\OriginalDB.ldf c:\NewDB.ldf

再次在 SQL 中:

USE master;
GO
CREATE DATABASE OriginalDB
    ON (FILENAME = 'C:\OriginalDB.mdf'),
       (FILENAME = 'C:\OriginalDB.ldf')
    FOR ATTACH;
GO
CREATE DATABASE NewDB
    ON (FILENAME = 'C:\NewDB.mdf'),
       (FILENAME = 'C:\NewDB.ldf')
    FOR ATTACH;
GO

完美的!这是对我有用的独特解决方案!多谢!
select * from OriginalDB.sys.sysfiles 查找数据库文件的位置。
是的,我也最喜欢这个解决方案,因为它不需要任何特殊工具。但我无法创建 NewDB,它在 .mdf 文件上显示 Permission denied。我现在不需要它,我只需要原始数据库的备份,所以我以后可以用它覆盖原始数据库,我只是好奇为什么会出现这样的错误。
如果可以停止 sql 服务,则不必分离原始数据库,复制 mdf 和 ldf 文件,为新数据库重命名它们,再次启动 sql 服务并在 master 下运行最后一个创建数据库命令:USE master ;去创建数据库 NewDB ON (FILENAME = 'C:\NewDB.mdf'), (FILENAME = 'C:\NewDB.ldf') FOR ATTACH;去
+1 最快的方式。除了@JohnLBevan 优秀的评论,您还可以使用exec sp_helpdb @dbname='TEMPDB';
S
Sergio

事实证明,我曾错误地尝试从备份中恢复。

最初我创建了一个新数据库,然后尝试在此处恢复备份。我应该做的以及最终起作用的是打开恢复对话框并在目标字段中输入新数据库的名称。

所以,简而言之,从备份中恢复就可以了。

感谢所有反馈和建议的家伙


当我这样做时,对话框告诉我文件与我最初备份的数据库位于同一位置。所以没有胆量恢复,怕文件被覆盖。
尼尔斯,默认情况下,您拍摄的快照中的文件是相同的。您可以更改它们的名称以为新命名的数据库创建新文件。
PS:此方法需要 SQL Agent 服务,在开始 db 复制操作之前确保它正在运行。
你现在已经用这个答案帮助了我三遍。我一直忘记输入它而不是创建它。 +啤酒
这和重命名“文件”窗口中的 .mdf 和 .log 文件对我有用。
b
bluish

这是我使用的脚本。有点棘手,但它有效。在 SQL Server 2012 上测试。

DECLARE @backupPath nvarchar(400);
DECLARE @sourceDb nvarchar(50);
DECLARE @sourceDb_log nvarchar(50);
DECLARE @destDb nvarchar(50);
DECLARE @destMdf nvarchar(100);
DECLARE @destLdf nvarchar(100);
DECLARE @sqlServerDbFolder nvarchar(100);

SET @sourceDb = 'db1'
SET @sourceDb_log = @sourceDb + '_log'
SET @backupPath = 'E:\DB SQL\MSSQL11.MSSQLSERVER\MSSQL\Backup\' + @sourceDb + '.bak'    --ATTENTION: file must already exist and SQL Server must have access to it
SET @sqlServerDbFolder = 'E:\DB SQL\MSSQL11.MSSQLSERVER\MSSQL\DATA\'
SET @destDb = 'db2'
SET @destMdf = @sqlServerDbFolder + @destDb + '.mdf'
SET @destLdf = @sqlServerDbFolder + @destDb + '_log' + '.ldf'

BACKUP DATABASE @sourceDb TO DISK = @backupPath

RESTORE DATABASE @destDb FROM DISK = @backupPath
WITH REPLACE,
   MOVE @sourceDb     TO @destMdf,
   MOVE @sourceDb_log TO @destLdf

在我的环境中,文件名与数据库名称不匹配(来自 another 恢复),因此我需要 SET @sourceDb_log = (SELECT files.name FROM sys.databases dbs INNER JOIN sys.master_files files ON dbs.database_id=files.database_id WHERE dbs.name=@sourceDb AND files.type=1) 和具有类似查询的 @sourceDb_data 的单独变量(替换为 files.type=0 )。 !
出现错误:Msg 137, Level 15, State 2, Line 25 Must declare the scalar variable "@destDb". 原来我在变量声明及其用法之间放置了 GO 语句,我们不能这样做,请参阅:stackoverflow.com/a/55347161/6184866
确实非常使用(r)完整的脚本......;)一个小的语法更正:只需在第 11 行的变量上放置一个 '@'SET @backupPath = 'E:\tmp\' + @sourceDb + '.bak' --ATTENTION: file must already exist and SQL Server must have access to it. 此外,您可以将备份文件夹设置为:` E:\DB SQL\ MSSQL11.MSSQLSERVER\MSSQL\Backup\ ` 以避免设置安全访问权限。
E
Emanuele Ciriachi

这里提到的所有解决方案都不适合我——我使用的是 SQL Server Management Studio 2014。

相反,我不得不取消选中“选项”屏幕中的“恢复前进行尾日志备份”复选框:在我的版本中,默认情况下它被选中并阻止完成恢复操作。取消选中后,还原操作继续进行,没有问题。

https://i.stack.imgur.com/Q0N5U.png


这个答案挽救了我的一天。
也拯救了我的一天:)
当不使用 SQL Server 2017 执行此操作时,原始数据库仍处于“正在恢复...”中。您的解决方案成功了-谢谢!
D
David Ferenczy Rogožan

使用 MS SQL Server 2012,您需要执行 3 个基本步骤:

首先,生成仅包含源数据库结构的 .sql 文件 右键单击源数据库,然后任务然后生成脚本按照向导并在本地保存 .sql 文件其次,将源数据库替换为 .sql 中的目标数据库文件 右键单击目标文件,选择 New Query 和 Ctrl-H 或(编辑 - 查找和替换 - 快速替换)最后,填充数据 右键单击目标数据库,然后选择任务和导入数据数据源下拉设置为".net framework data provider for SQL server" + 在 DATA ex 下设置连接字符串文本字段:Data Source=Mehdi\SQLEXPRESS;Initial Catalog=db_test;User ID=sa;Password=sqlrpwrd15 对目标执行相同操作检查表您要传输或选中“来源:...”之外的复选框以检查所有这些

你完成了。


顺便说一句,我猜如果目标表中不存在 Import Data 可以创建表.. 简单的解决方案 +1
J
James Graham

来自 SSMS:

- 将原始数据库备份到 .BAK 文件(your_source_db -> 任务 -> 备份)。

2 - 右键单击“数据库”和“恢复数据库”

3 - 设备 > ...(按钮)> 添加 > 选择 your_source_db.bak

4 - 在“常规”选项卡的“目标”部分中,将“数据库”中的 your_source_db 重命名为 new_name_db

5 - 在“文件”选项卡中,勾选“将所有文件重新定位到文件夹”,

在“恢复为”列中重命名两个法线以与 new_name_db (.mdf, _log.ldf) 保持一致

- 在“选项”选项卡的“恢复选项”部分,勾选两个第一个选项(“覆盖...”、“保留...”)和“恢复状态”:“恢复时恢复”

还要确保在“尾日志备份”部分选项未选中,以避免将源数据库保持在“恢复状态”!

https://i.stack.imgur.com/aqpmj.png


A
Andrew Barber

如果数据库不是很大,您可以查看 SQL Server Management Studio Express 中的“脚本数据库”命令,这些命令位于资源管理器中数据库项本身的上下文菜单中。

您可以选择要编写的全部内容;当然,您需要对象和数据。然后,您将把整个脚本保存到一个文件中。然后您可以使用该文件重新创建数据库;只需确保顶部的 USE 命令设置为正确的数据库即可。


谢谢,数据库非常大,(大约一个演出)所以我认为可能会发生不好的事情:-)
正确的;那不是最好的方法。相反,您可以使用脚本数据库在新数据库中创建结构,然后导入/导出来移动数据。只要确保你先做脚本数据库;如果表不存在,Import/Export 将创建表,您可能不喜欢它的方式。
p
pabloelustondo

在 SQL Server 2008 R2 中,将数据库作为文件备份到文件夹中。然后选择出现在“数据库”文件夹中的恢复选项。在向导中,输入您希望在目标数据库中使用的新名称。并选择从文件中恢复并使用您刚刚创建的文件。我只是做到了,而且速度非常快(我的数据库很小,但仍然如此)巴勃罗。


P
Pavel Samoylenko

解决方案,基于此评论: https://stackoverflow.com/a/22409447/2399045 。只需设置设置:数据库名称、临时文件夹、数据库文件文件夹。运行后,您将获得名称为“sourceDBName_yyyy-mm-dd”格式的数据库副本。

-- Settings --
-- New DB name will have name = sourceDB_yyyy-mm-dd
declare @sourceDbName nvarchar(50) = 'MyDbName';
declare @tmpFolder nvarchar(50) = 'C:\Temp\'
declare @sqlServerDbFolder nvarchar(100) = 'C:\Databases\'

--  Execution --
declare @sourceDbFile nvarchar(50);
declare @sourceDbFileLog nvarchar(50);
declare @destinationDbName nvarchar(50) = @sourceDbName + '_' + (select convert(varchar(10),getdate(), 121))
declare @backupPath nvarchar(400) = @tmpFolder + @destinationDbName + '.bak'
declare @destMdf nvarchar(100) = @sqlServerDbFolder + @destinationDbName + '.mdf'
declare @destLdf nvarchar(100) = @sqlServerDbFolder + @destinationDbName + '_log' + '.ldf'

SET @sourceDbFile = (SELECT top 1 files.name 
                    FROM sys.databases dbs 
                    INNER JOIN sys.master_files files 
                        ON dbs.database_id = files.database_id 
                    WHERE dbs.name = @sourceDbName
                        AND files.[type] = 0)

SET @sourceDbFileLog = (SELECT top 1 files.name 
                    FROM sys.databases dbs 
                    INNER JOIN sys.master_files files 
                        ON dbs.database_id = files.database_id 
                    WHERE dbs.name = @sourceDbName
                        AND files.[type] = 1)

BACKUP DATABASE @sourceDbName TO DISK = @backupPath

RESTORE DATABASE @destinationDbName FROM DISK = @backupPath
WITH REPLACE,
   MOVE @sourceDbFile     TO @destMdf,
   MOVE @sourceDbFileLog  TO @destLdf

帕维尔,谢谢你的回答。如果您不介意,我会添加动态 SQL,以便它可以重命名逻辑文件名:DECLARE @DynamicSQL [nvarchar](2000); SET @DynamicSQL = CONCAT('ALTER DATABASE [', @DestinationDBName, '] MODIFY FILE (NAME = ''', @SourceDBName, ''', NEWNAME = ''', @DestinationDBName, ''');'); SET @DynamicSQL = CONCAT(@DynamicSQL, 'ALTER DATABASE [', @DestinationDBName, '] MODIFY FILE (NAME = ''', CONCAT(@SourceDBName, '_log'), ''', NEWNAME = ''', CONCAT(@DestinationDBName, '_log'), ''');'); EXECUTE (@DynamicSQL);
J
Jean

基于 Joe 回答的脚本(分离、复制文件、附加两者)。

以管理员帐户运行管理工作室。

这不是必需的,但可能在执行时访问被拒绝错误。

配置 sql server 以执行 xp_cmdshel

EXEC sp_configure 'show advanced options', 1
GO
RECONFIGURE
GO
EXEC sp_configure 'xp_cmdshell', 1
GO
RECONFIGURE
GO

运行脚本,但之前在 @dbName 和 @copyDBName 变量中输入您的数据库名称。

USE master;
GO 

DECLARE @dbName NVARCHAR(255) = 'Products'
DECLARE @copyDBName NVARCHAR(255) = 'Products_branch'

-- get DB files
CREATE TABLE ##DBFileNames([FileName] NVARCHAR(255))
EXEC('
    INSERT INTO ##DBFileNames([FileName])
    SELECT [filename] FROM ' + @dbName + '.sys.sysfiles')

-- drop connections
EXEC('ALTER DATABASE ' + @dbName + ' SET OFFLINE WITH ROLLBACK IMMEDIATE')

EXEC('ALTER DATABASE ' + @dbName + ' SET SINGLE_USER')

-- detach
EXEC('EXEC sp_detach_db @dbname = ''' + @dbName + '''')

-- copy files
DECLARE @filename NVARCHAR(255), @path NVARCHAR(255), @ext NVARCHAR(255), @copyFileName NVARCHAR(255), @command NVARCHAR(MAX) = ''
DECLARE 
    @oldAttachCommand NVARCHAR(MAX) = 
        'CREATE DATABASE ' + @dbName + ' ON ', 
    @newAttachCommand NVARCHAR(MAX) = 
        'CREATE DATABASE ' + @copyDBName + ' ON '

DECLARE curs CURSOR FOR 
SELECT [filename] FROM ##DBFileNames
OPEN curs  
FETCH NEXT FROM curs INTO @filename
WHILE @@FETCH_STATUS = 0  
BEGIN
    SET @path = REVERSE(RIGHT(REVERSE(@filename),(LEN(@filename)-CHARINDEX('\', REVERSE(@filename),1))+1))
    SET @ext = RIGHT(@filename,4)
    SET @copyFileName = @path + @copyDBName + @ext

    SET @command = 'EXEC master..xp_cmdshell ''COPY "' + @filename + '" "' + @copyFileName + '"'''
    PRINT @command
    EXEC(@command);

    SET @oldAttachCommand = @oldAttachCommand + '(FILENAME = "' + @filename + '"),'
    SET @newAttachCommand = @newAttachCommand + '(FILENAME = "' + @copyFileName + '"),'

    FETCH NEXT FROM curs INTO @filename
END
CLOSE curs 
DEALLOCATE curs

-- attach
SET @oldAttachCommand = LEFT(@oldAttachCommand, LEN(@oldAttachCommand) - 1) + ' FOR ATTACH'
SET @newAttachCommand = LEFT(@newAttachCommand, LEN(@newAttachCommand) - 1) + ' FOR ATTACH'

-- attach old db
PRINT @oldAttachCommand
EXEC(@oldAttachCommand)

-- attach copy db
PRINT @newAttachCommand
EXEC(@newAttachCommand)

DROP TABLE ##DBFileNames

L
LearnByReading

另一种使用导入/导出向导来解决问题的方法是,首先创建一个空数据库,然后选择源数据库作为您的服务器的源,然后在目标中选择与目标数据库相同的服务器(使用空数据库您首先创建),然后点击完成

它将创建所有表并将所有数据传输到新数据库中,


谢谢你的月亮和回来!
T
Techie Philosopher

您可以只创建一个新数据库,然后转到任务,导入数据,然后将要复制的数据库中的所有数据导入刚刚创建的数据库。


D
Dror Cohen

该程序以不同的名称将数据库复制到同一台服务器。我依靠这个网站上给出的例子进行了一些改进。

-- Copies a database to the same server
-- Copying the database is based on backing up the original database and restoring with a different name

DECLARE @sourceDb nvarchar(50);    
DECLARE @destDb nvarchar(50);
DECLARE @backupTempDir nvarchar(200)

SET @sourceDb =  N'Northwind'         -- The name of the source database
SET @destDb =    N'Northwind_copy'    -- The name of the target database
SET @backupTempDir = N'c:\temp'       -- The name of the temporary directory in which the temporary backup file will be saved
-- --------- ---

DECLARE @sourceDb_ROWS nvarchar(50);  
DECLARE @sourceDb_LOG nvarchar(50);
DECLARE @backupPath nvarchar(400); 
DECLARE @destMdf nvarchar(100);
DECLARE @destLdf nvarchar(100);
DECLARE @sqlServerDbFolder nvarchar(100);

Declare @Ret as int = -1
Declare @RetDescription nvarchar(200) = ''

-- Temporary backup file name
SET @backupPath = @backupTempDir+ '\TempDb_' + @sourceDb + '.bak'    

-- Finds the physical location of the files on the disk
set @sqlServerDbFolder = (SELECT top(1) physical_name as dir
                           FROM sys.master_files where DB_NAME(database_id) = @sourceDb  );

-- Clears the file name and leaves the directory name
set @sqlServerDbFolder = REVERSE(SUBSTRING(REVERSE(@sqlServerDbFolder), CHARINDEX('\', REVERSE(@sqlServerDbFolder)) + 1, LEN(@sqlServerDbFolder))) + '\'

-- Finds the logical name for the .mdf file
set @sourceDb_ROWS = (SELECT f.name LogicalName FROM sys.master_files f INNER JOIN sys.databases d ON d.database_id = f.database_id
                      where d.name = @sourceDb  and   f.type_desc = 'ROWS' )

-- Finds the logical name for the .ldf file
set @sourceDb_LOG = (SELECT f.name LogicalName FROM sys.master_files f INNER JOIN sys.databases d ON d.database_id = f.database_id
                      where d.name = @sourceDb  and   f.type_desc = 'LOG' )

-- Composes the names of the physical files for the new database  
SET @destMdf = @sqlServerDbFolder + @destDb + N'.mdf'
SET @destLdf = @sqlServerDbFolder + @destDb + N'_log' + N'.ldf'

-- If the source name is the same as the target name does not perform the operation
if @sourceDb <> @destDb      
    begin 

    -- Checks if the target database already exists
    IF Not EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE name = @destDb)
    begin 
        -- Checks if the source database exists
        IF EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE name = @sourceDb) and (@sqlServerDbFolder is not null)
        begin 

            -- Opens the permission to run xp_cmdshell
            EXEC master.dbo.sp_configure 'show advanced options', 1
            RECONFIGURE WITH OVERRIDE
            EXEC master.dbo.sp_configure 'xp_cmdshell', 1
            RECONFIGURE WITH OVERRIDE
         
            -- If the temporary backup directory does not exist it creates it
            declare @md as nvarchar(100) = N'if not exist ' + @backupTempDir + N' md ' +@backupTempDir  
            exec xp_cmdshell  @md,  no_output

            -- Creates a backup to the source database to the temporary file
            BACKUP DATABASE @sourceDb TO DISK = @backupPath 

            -- Restores the database with a new name
            RESTORE DATABASE @destDb FROM DISK = @backupPath
            WITH REPLACE, 
            MOVE @sourceDb_ROWS TO @destMdf, 
            MOVE @sourceDb_LOG TO  @destLdf

            -- Deletes the temporary backup file
            declare @del as varchar(100) = 'if exist ' + @backupPath +' del ' +@backupPath 
            exec xp_cmdshell  @del ,  no_output

            -- Close the permission to run xp_cmdshell
            EXEC master.dbo.sp_configure 'xp_cmdshell', 0
            RECONFIGURE WITH OVERRIDE
            EXEC master.dbo.sp_configure 'show advanced options', 0
            RECONFIGURE WITH OVERRIDE
         
            set @ret = 1
            set @RetDescription = 'The ' +@sourceDb + ' database was successfully copied to ' + @destDb 
        
        end
        else
        begin
          set @RetDescription = 'The source database '''+ @sourceDb + ''' is not exists.'
          set @ret = -3
        end

    end
    else
    begin
      set @RetDescription = 'The target database '''+ @destDb + ''' already exists.'
      set @ret = -4
    end
end
else
begin
  set @RetDescription = 'The target database ''' +@destDb + ''' and the source database '''+ @sourceDb + ''' have the same name.'
  set @ret = -5
end

select @ret as Ret, @RetDescription as RetDescription

您似乎错过了提供“此站点”的链接
A
Alex Hinterleitner

<head>
    <title>Copy Database</title>
</head>

<body>
    
    <?php
    
    $servername = "localhost:xxxx";
    $user1 = "user1";
    $pw1 = "pw1";
    $db1 = "db1";
    
    $conn1 = new mysqli($servername,$user1,$pw1,$db1);
    
    if($conn1->connect_error) {
        die("Conn1 failed: " . $conn1->connect_error);
    }
    
    $user2 = "user2";
    $pw2 = "pw2";
    $db2 = "db2";
    
    $conn2 = new mysqli($servername,$user2,$pw2,$db2);
    
    if($conn2->connect_error) {
        die("Conn2 failed: " . $conn2->connect_error);
    }
    
    $sqlDB1 = "SELECT * FROM table1";
    $resultDB1 = $conn1->query($sqlDB1);
    
    if($resultDB1->num_rows > 0) {
        while($row = $resultDB1->fetch_assoc()) {
            $sqlDB2 = "INSERT INTO table2 (col1, col2) VALUES ('" . $row["tableRow1"] . "','" . $row["tableRow2"] . "')";
            $resultDB2 = $conn2->query($sqlDB2);
        }
    }else{
        echo "0 results";
    }
    
    $conn1->close();
    $conn2->close();
    
    ?>
    
</body>

欢迎使用 stack-overflow .... 请在此处阅读帮助中心的 how to ask good question 部分stackoverflow.com/help/how-to-ask,您可以在此处获得导览并赠送一枚徽章stackoverflow.com/tour