I posted yesterday because we have a database that was basically used for application purposes but it was using "master" - bad indeed. We created a new database called school that is now being used (same structure as how master was minus a table renamed correctly). I was trying to restore a .bak file from the old db (master from sql 2008) to the new school db (school in sql 2016).
Problem is, running the script gave me a bunch of lines saying it's updating, then the restore terminated abnormally... My db was then in a recovery pending state, I tried running emergency code but it seems pretty broken and i'm not sure 1. why it failed in the first place and 2. what to do now.
Below is the script code and the error message
use school;
DECLARE #TableSchema sys.sysname = N'dbo'
DECLARE #TableName sys.sysname = N'rolerights'
DECLARE #OldTableName sys.sysname = N'rolerigths'
DECLARE #OldTableWithSchema NVARCHAR(256) = QUOTENAME(#TableSchema) + '.' + QUOTENAME(#OldTableName)
DECLARE #TableWithSchema NVARCHAR(256) = QUOTENAME(#TableSchema) + '.' + QUOTENAME(#TableName)
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = #TableSchema
AND TABLE_NAME = #TableName))
BEGIN
EXEC sp_rename #TableWithSchema, #OldTableName
END
DECLARE #Table TABLE ([LogicalName] varchar(128),[PhysicalName] varchar(128), [Type] varchar, [FileGroupName] varchar(128), [Size] varchar(128),
[MaxSize] varchar(128), [FileId]varchar(128), [CreateLSN]varchar(128), [DropLSN]varchar(128), [UniqueId]varchar(128), [ReadOnlyLSN]varchar(128), [ReadWriteLSN]varchar(128),
[BackupSizeInBytes]varchar(128), [SourceBlockSize]varchar(128), [FileGroupId]varchar(128), [LogGroupGUID]varchar(128), [DifferentialBaseLSN]varchar(128), [DifferentialBaseGUID]varchar(128),
[IsReadOnly]varchar(128), [IsPresent]varchar(128), [TDEThumbprint]varchar(128), [SnapshotUrl]varchar(128)
)
DECLARE #Path varchar(1000)='C:\Program Files\Microsoft SQL Server\MSSQL13.SQLEXPRESS\MSSQL\Backup\SQL2008backup.bak'
DECLARE #LogicalNameData varchar(128),#LogicalNameLog varchar(128)
INSERT INTO #table
EXEC('
RESTORE FILELISTONLY
FROM DISK=''' +#Path+ '''
')
SET #LogicalNameData=(SELECT LogicalName FROM #Table WHERE Type='D')
SET #LogicalNameLog=(SELECT LogicalName FROM #Table WHERE Type='L')
SELECT #LogicalNameData, #LogicalNameLog
use master;
declare #MasterData nvarchar(512)
exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer\Parameters', N'SqlArg0', #MasterData output
select #MasterData=substring(#MasterData, 3, 255)
select #MasterData=substring(#MasterData, 1, len(#MasterData) - charindex('\', reverse(#MasterData)))
print #MasterData
print #LogicalNameData
declare #MasterLog nvarchar(512)
exec master.dbo.xp_instance_regread N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer\Parameters', N'SqlArg2', #MasterLog output
select #MasterLog=substring(#MasterLog, 3, 255)
select #MasterLog=substring(#MasterLog, 1, len(#MasterLog) - charindex('\', reverse(#MasterLog)))
print #MasterLog
print #LogicalNameLog
declare #DefaultData nvarchar(512)
select isnull(#DefaultData, CONVERT(nvarchar(512), #MasterData))
declare #DefaultLog nvarchar(512)
select isnull(#DefaultLog, CONVERT(nvarchar(512), #MasterLog))
declare #NewDefaultData nvarchar(512) = #MasterData + '\' + 'school.MDF'
declare #NewDefaultLog nvarchar(512) = #MasterLog + '\' + 'school.LDF'
SET DEADLOCK_PRIORITY 10
ALTER DATABASE school
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE school FROM DISK=#Path
WITH MOVE #LogicalNameData TO #NewDefaultData,
MOVE #LogicalNameLog TO #NewDefaultLog,
REPLACE
IF (EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = #TableSchema
AND TABLE_NAME = #OldTableName))
BEGIN
EXEC sp_rename #OldTableWithSchema, #TableName
END
And he is the emergency script
ALTER DATABASE [school] SET EMERGENCY;
GO
ALTER DATABASE [school] set single_user
GO
DBCC CHECKDB ([school], REPAIR_ALLOW_DATA_LOSS) WITH ALL_ERRORMSGS;
GO
ALTER DATABASE [school] set multi_user
GO
And the error:
Msg 5069, Level 16, State 1, Line 3
ALTER DATABASE statement failed.
Msg 946, Level 14, State 1, Line 5
Cannot open database 'school' version 677. Upgrade the database to the latest version.
Msg 946, Level 14, State 1, Line 7
Cannot open database 'school' version 677. Upgrade the database to the latest version.
Msg 5069, Level 16, State 1, Line 7
ALTER DATABASE statement failed.
Looking at the original logs... this is why it failed. An exception was thrown about filestream garbage collection apparently...
FILESTREAM Failed to find the garbage collection table.
The way to go into single user mode to restore the master db is to use the -m switch and then restore the master db from SQLCMD. If you want to go into single user mode you probably want to do so using this form:
Set Single_User with rollback immediate;
Please see this page how to use switches at startup:
https://learn.microsoft.com/en-us/sql/database-engine/configure-windows/database-engine-service-startup-options?view=sql-server-2017
Related
Looking for a stored procedure to restore from a .bak file, but would like to be able to enter the db name as a parameter, i.e. Exec sp_restore #dbname
The paths to the .bak will be identical for all the dbs i.e. all dbs are dev copies of the same production backup.
Each .mdf and .ldf will have the same name as the db itself i.e.
dbname = #dbname, mdf = D:\data\#dbname.mdf, ldf =D:\data\#dbname.ldf
The paths to .mdf and .ldf will be the identical for each db, i.e. D:\data\#dbname.mdf
Using Sql Server Management Studio, you can start performing a restore operation. Before completing the task, you should be able to see a "Script" button. This will create a script with all the parameters you have manually entered, including mdf and ldf location. You can then save this script and execute it at will. You can also modify the resulting script to make the database name an input variable. You can do anything really. It's SQL!
A sample script that restores a database and changes mdf and ldf file locations would look like this:
RESTORE DATABASE [example] FROM DISK = N'E:\Backup\example.BAK' WITH FILE = 1, MOVE N'ExampleData' TO N'E:\dbfiles\example.mdf', MOVE N'example_log' TO N'E:\dbfiles\example.ldf', NOUNLOAD, STATS = 10
GO
You can read more about the RESTORE statement
You can then insert the script in a stored procedure as such:
CREATE PROCEDURE RestoreDb
-- Add the parameters for the stored procedure here
#dbName nvarchar(50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
RESTORE DATABASE #dbName FROM DISK = N'C:\Data\MSSQL\Backup\lolwut.bak' WITH FILE = 1, NOUNLOAD, STATS = 10
END
GO
Procedure for restore DB from bak-file #DeviceName. It works with DBs, wich have two logical files.
How to use:
EXEC RestoreDb #dbName='qqq', #DeviceName = 'D:\temp\1\R.bak'
Sorry for my English, I improve it.
CREATE PROCEDURE RestoreDb
#dbName NVARCHAR(50),
#DeviceName NVARCHAR(400)
AS
SET NOCOUNT ON
DECLARE #Cmd NVARCHAR(1000),
#DataLogicalName NVARCHAR(200),
#LogLogicalName NVARCHAR(200),
#DatabasePath NVARCHAR(200),
#DataPath NVARCHAR(300),
#LogPath NVARCHAR(300)
CREATE TABLE #Files
(
LogicalName nvarchar(128),
PhysicalName nvarchar(260),
[Type] char(1),
FileGroupName nvarchar(128),
Size numeric(20,0),
MaxSize numeric(20,0),
FileID bigint,
CreateLSN numeric(25,0),
DropLSN numeric(25,0),
UniqueID uniqueidentifier,
ReadOnlyLSN numeric(25,0) ,
ReadWriteLSN numeric(25,0),
BackupSizeInBytes bigint,
SourceBlockSize int,
FileGroupID int,
LogGroupGUID uniqueidentifier,
DifferentialBaseLSN numeric(25,0),
DifferentialBaseGUID uniqueidentifier,
IsReadOnly bit,
IsPresent bit,
TDEThumbprint varbinary(32)
)
SELECT #DatabasePath = 'D:\data\'
SELECT #DataPath = #DatabasePath + #dbName + '.mdf',
#LogPath = #DatabasePath + #dbName + '.ldf'
SELECT #Cmd = 'RESTORE FILELISTONLY
FROM DISK = ''' + #DeviceName + ''''
INSERT #Files
EXEC (#Cmd)
IF NOT EXISTS(SELECT 1 FROM #Files) GOTO ERRORFILES
IF (SELECT COUNT(*) FROM #Files) > 2 GOTO ERRORFILESCOUNT
SELECT #DataLogicalName = LogicalName
FROM #Files
WHERE [Type] = 'D'
SELECT #LogLogicalName = LogicalName
FROM #Files
WHERE [Type] = 'L'
RESTORE DATABASE #DbName
FROM DISK = #DeviceName
WITH
MOVE #DataLogicalName TO #DataPath,
MOVE #LogLogicalName TO #LogPath
GOTO EXITSTAT
ERRORFILES:
BEGIN
RAISERROR( 'The list of files contained in the backup set is empty', 16, 1 )
GOTO EXITSTAT
END
ERRORFILESCOUNT:
BEGIN
RAISERROR( 'The count of files contained in the backup set is more than two', 16, 1 )
GOTO EXITSTAT
END
EXITSTAT:
I am trying to alter database through a DDL trigger which will fire on creation. However I am getting a below error.
CREATE TRIGGER ddl_trig_database
ON ALL SERVER
FOR CREATE_DATABASE
AS
declare #dbname as nvarchar(100)
declare #sql as nvarchar(max)
select #dbname =
CAST(eventdata().query(
'/EVENT_INSTANCE/DatabaseName[1]/text()'
) as NVarchar(128))
select #sql = N'SET IMPLICIT_TRANSACTIONS OFF
ALTER DATABASE ' + #dbname+ N' SET COMPATIBILITY_LEVEL = 110
SET IMPLICIT_TRANSACTIONS ON'
exec (#sql)
GO
create database test
Error:
Msg 226, Level 16, State 6, Line 22
ALTER DATABASE statement not allowed within multi-statement transaction.
The statement has been terminated.
I am on SQL Server 2014 on Windows 2012.
If you want a specific compatibility level for each new database created - just set that compatibility level in the model database which is the "template" for all new databases being created ...
No need for a system-level trigger for this ....
I realized the DDL trigger will be on its own transaction and Alter is not allowed if a transaction is already started. So to workaround with this problem I have created SQL Job. and put the Alters in the Job and modified the Trigger to call msdb..start_sql_job.
--Trigger
CREATE TRIGGER ddl_trig_database
ON ALL SERVER
FOR CREATE_DATABASE
AS
exec msdb..sp_start_job 'Initialize Database'
GO
--Job
declare #dbname as nvarchar(100)
declare #sql as nvarchar(max)
select top 1 #dbname = name from sys.databases
where name like 'gtp%' and create_date >= getdate() - .08
order by create_date desc
IF #dbname is not null
begin
select #sql = N'ALTER DATABASE ' + #dbname+ N' SET COMPATIBILITY_LEVEL = 110'
exec sp_executesql #sql
print 'Altered database'
end
print 'completed'
I use script for DB backup as below:
---- INITIALIZATION
-- UNIFIED DATE
DECLARE #DAT DATETIME
SELECT #DAT = GETDATE()
-- DEFAULT BACKUPS LOCATION
DECLARE #PATH VARCHAR(20)
SELECT #PATH = 'Q:\DBbackup\Default\'
---- DATABASE BACKUPS
-- DB1
DECLARE #DBNAME VARCHAR(64)
SELECT #DBNAME = 'DB1'
DECLARE #BACKUPNAME VARCHAR(64)
DECLARE #DESCRIPTION VARCHAR (255)
SELECT #BACKUPNAME = (#PATH + #DBNAME +'_'+ CONVERT(VARCHAR(19),#DAT,121) + '.bak')
SELECT #DESCRIPTION = #DBNAME + ' Full ad hoc backup ' + CONVERT(VARCHAR(20),#DAT)
SELECT #BACKUPNAME
BACKUP DATABASE #DBNAME TO DISK = #BACKUPNAME WITH FORMAT, INIT, NAME = #DESCRIPTION, SKIP, NOREWIND, NOUNLOAD, STATS = 10, COMPRESSION
GO
-- DB2
DECLARE #DBNAME VARCHAR(64)
SELECT #DBNAME = 'DB2'
DECLARE #BACKUPNAME VARCHAR(64)
DECLARE #DESCRIPTION VARCHAR (255)
SELECT #BACKUPNAME = (#PATH + #DBNAME + '_' + CONVERT(VARCHAR(19),#DAT,121) + '.bak')
SELECT #DESCRIPTION = #DBNAME + ' Full ad hoc backup ' + CONVERT(VARCHAR(20),#DAT)
BACKUP DATABASE #DBNAME TO DISK = #BACKUPNAME WITH FORMAT, INIT, NAME = #DESCRIPTION, SKIP, NOREWIND, NOUNLOAD, STATS = 10, COMPRESSION
GO
However, for backup DB2 the variables #PATH and #DAT are not found as they apply only to first backup. It seems the GO command cancels the effect of local variables for DB2 backup. Is there any workaround for this situation? I want to use the variables in INIT only once, not duplicate them for each database backup.
SQL Server does not know what GO means. That's an SSMS feature. It causes multiple batches to be sent. Either remove GO (and restructure the variables so that they are declared only once) or store that data in a persistent location such as a temp table.
Unfortunately, no great options. T-SQL is a stone-age language.
I am trying to change the recovery model of the current database.
This is what I have:
DECLARE #dbName VARCHAR(50)
SELECT #dbName = DB_NAME()
ALTER DATABASE #dbName SET RECOVERY SIMPLE WITH NO_WAIT
#dbName gives me:
Incorrect syntax near '#dbName'.
I tried:
ALTER DATABASE database_id SET RECOVERY SIMPLE WITH NO_WAIT
database_id gives me:
Msg 5011, Level 14, State 5, Line 3 User does not have permission to
alter database 'database_id', the database does not exist, or the
database is not in a state that allows access checks.
How should I execute this on the current database?
DECLARE #sql NVARCHAR(MAX) = N'ALTER DATABASE '
+ QUOTENAME(DB_NAME())
+ ' SET RECOVERY SIMPLE WITH NO_WAIT;';
EXEC sp_executesql #sql;
well i had sql 2008 express, and now i have installed, sql server, now i want to delete sql express, but in that instance i have all my database i have worked (plus of 20), so they are very important for me, how can i pass it to sql server 2008 r2, i know i can do database of all but i dont want to work a lot of, there is a way short, for to pass al database? coping a folder? something? thanks!
I Attached all database, and i added, all (but one for one) i added all in the same windows and in 5 minutos i had all database in sql (no express)
Create an SSIS package with 4 steps.
First, a Execute SQL task that backups all DBs to a specific location:
exec sp_msforeachdb '
IF DB_ID(''?'') > 4
Begin
BACKUP DATABASE [?] TO DISK = N''\\Backups\?.BAK'' WITH NOFORMAT, INIT,
NAME = N''?-Full Database Backup'', SKIP, NOREWIND, NOUNLOAD
declare #backupSetId as int
select #backupSetId = position from msdb..backupset where database_name=N''?''
and backup_set_id=(select max(backup_set_id) from msdb..backupset
where database_name=N''?'' )
if #backupSetId is null begin
raiserror(N''Verify failed. Backup information for database ''''?'''' not found.'', 16, 1)
end
RESTORE VERIFYONLY FROM DISK = N''\\Backups\?.BAK'' WITH
FILE = #backupSetId, NOUNLOAD, NOREWIND
End
'
Second, Create a SP to restore DBs using a variable for location and DB name:
CreatePROCEDURE [dbo].[uSPMas_RestoreDB] #DBname NVARCHAR(200),
#BackupLocation NVARCHAR(2000)
AS
BEGIN
SET nocount ON;
create table #fileListTable
(
LogicalName nvarchar(128),
PhysicalName nvarchar(260),
[Type] char(1),
FileGroupName nvarchar(128),
Size numeric(20,0),
MaxSize numeric(20,0),
)
insert into #fileListTable EXEC('RESTORE FILELISTONLY FROM DISK = '''+#BackupLocation+#dbname+'.bak''')
Declare #Dname varchar(500)
Set #Dname = (select logicalname from #fileListTable where type = 'D')
Declare #LName varchar(500)
Set #LName = (select logicalname from #fileListTable where type = 'L')
declare #sql nvarchar(4000)
SET #SQL = 'RESTORE DATABASE ['+ #dbname +'] FROM
DISK = N'''+#BackupLocation + #dbname +'.BAK'' WITH FILE = 1,
MOVE N'''+ #Dname +''' TO N''E:\YourLocation\'+ #dbname +'.mdf'',
MOVE N'''+ #LName +''' TO N''D:\Your2ndLocation\'+ #dbname +'_log.ldf'',
NOUNLOAD, REPLACE, STATS = 10'
exec sp_executesql #SQL
drop table #fileListTable
END
Third, Create a ForEach Loop Container and put a Execute Sql Statement with variable:
EXEC uSPMas_RestoreDB ? , ?
Use the for ForEach Loop Container to pass the variables
Fourth, Create a 'Transfer Logins' task to move over all DB logins