I want a script that will quickly delete a Test instance of a database and recreate it with only a sample of rows in each table. I know there is no referential integrity issues or other object restraints such as stored procedures linked to records. I have tried the following but it states database in use. I think I am missing something in between the drop and clone. What am I doing wrong here? Or does this have to be split between two scripts?
-- ensure "master" is selected in SSMS
IF EXISTS(select * from sys.databases where name='shoesales_TEST')
ALTER DATABASE shoesales_TEST set single_user with rollback immediate
GO
IF EXISTS(select * from sys.databases where name='shoesales_TEST')
DROP DATABASE shoesales_TEST
GO
DBCC CLONEDATABASE
(
shoesales
, shoesales_TEST
)
GO
ALTER DATABASE shoesales_TEST SET READ_WRITE
GO
USE shoesales_TEST
GO
INSERT INTO shoesales_TEST.dbo.sizesales
SELECT TOP 30 * FROM shoesales.dbo.sizesales
GO
Related
I can't create/alter a VIEW because it already exists.
I also can't drop the same VIEW because it does not exists!
First of all, I'm SA on a SQL Server 2012.
CREATE VIEW [dbo].[some_name]
There is already an object named 'some_name' in the database.
DROP VIEW [dbo].[some_name]
Cannot drop the view 'dbo.some_name', because it does not exist or you
do not have permission.
SELECT OBJECT_ID(N'dbo.some_name', N'V')
SELECT OBJECT_ID('dbo.some_name', 'V')
SELECT OBJECT_ID(N'dbo.some_name')
SELECT OBJECT_ID('dbo.some_name')
SELECT OBJECT_ID(N'some_name')
SELECT OBJECT_ID('some_name')
NULL (for each query)
SELECT * FROM sys.objects WHERE name LIKE '%some_name%'
SELECT * FROM sys.objects WHERE name ='some_name'
SELECT * FROM sys.objects WHERE name = N'some_name'
empty result (for each query)
SELECT * FROM fn_my_permissions(N'dbo.some_name','OBJECT')
SELECT * FROM fn_my_permissions('dbo.some_name','OBJECT')
SELECT * FROM fn_my_permissions(N'some_name','OBJECT')
SELECT * FROM fn_my_permissions('some_name','OBJECT')
SELECT * FROM fn_my_permissions(N'some_name','VIEW')
SELECT * FROM fn_my_permissions('some_name','VIEW')
empty result (for each query)
Alt+F1 on 'some_name':
The object 'some_name' does not exist in database 'X' or is invalid
for this operation.
EXEC sp_rename
#objname = 'other_name',
#newname = 'some_name'
Error: The new name 'some_name' is already in use as a object name and
would cause a duplicate that is not permitted.
EXEC sp_rename
#objname = 'some_name',
#newname = 'other_name'
No item by the name of 'some_name' could be found in the current
database 'X', given that #itemtype was input as '(null)'.
And now something interesting. If I run:
DROP FUNCTION [dbo].[some_name]
DROP SYNONYM [dbo].[some_name]
DROP TABLE [dbo].[some_name]
DROP TRIGER[dbo].[some_name]
DROP TYPE [dbo].[some_name]
DROP VIEW [dbo].[some_name]
I get only 5 messages:
Cannot drop the [FUNCTION, SYNONYM, TABLE, TRIGGER, TYPE]
'dbo.some_name', because it does not exist or you do not have
permission. (no mention of the VIEW)
But if I move the View command to the top :
DROP VIEW [dbo].[some_name]
DROP FUNCTION [dbo].[some_name]
DROP SYNONYM [dbo].[some_name]
DROP TABLE [dbo].[some_name]
DROP TRIGER[dbo].[some_name]
DROP TYPE [dbo].[some_name]
I get 6 messages, as expected.
Please help.
UPDATE
After a few CHECKDB commands I found what seems to be the problem. As suspected, this view is in an unconsistend state. By the DBCC command log I got the object_id of it. It does not existis on sys.objects, but it does existis on sys.columns. So, there are a few orphan columns on my database, which indicates a failure when creating or dropping this object.
So, by running DBCC CHECKCATALOG (and a few other DBCC commands) I got the messages:
Msg 3853, State 1: Attribute
(referenced_major_id=859202161,referenced_minor_id=42) of row
(class=0,object_id=990678627,column_id=0,referenced_major_id=859202161,referenced_minor_id=42)
in sys.sql_dependencies does not have a matching row
(object_id=859202161,column_id=42) in sys.columns.
Msg 8956, Level 16, State 1, Line 1 Index row (1:123:12) with values
(nsclass = 0 and nsid = 1 and name = 'some_name') points to the data
row identified by (RID = (1:456:45)).
SOLUTION
0 - Backup your database.
1 - Reboot your server on Single User Mode (-m parameter)
2 - Set the database to Single User Mode:
ALTER DATABASE X
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
GO
3 - Run a CHECKDB command with a repair option. In my case it was:
DBCC CHECKDB (X,REPAIR_REBUILD)
4 - Set the database back to Multi User Mode
ALTER DATABASE X
SET MULTI_USER
WITH ROLLBACK IMMEDIATE;
GO
'X' is the name of the database.
I'm not sure if step 0 and 1 are really necessary, but better safe than sorry.
You may find more info about the CHECKDB command and its repair otions at: DBCC CHECKDB
I think SQL server requires that object names be unique. You certainly cannot have a table and a view with the same name, as it would be impossible to distinguish which object was intended when you refer to it. But I think function and procedure names also have to be unique ( and this may not be strictly necessary, as far as I know the context in which these names are used always allows them to be distinguished ).
From your error messages, I understand that dbo.some_name exists, but, you don't have enough permissions to view dbo.some_name.
sys.objects will show only the objects, to which you have got permission to view .MSDN reference
Permissions
The visibility of the metadata in catalog views is limited
to securables that a user either owns or on which the user has been
granted some permission.
You can see what permissions you have got on an object by running the below query:
SELECT * FROM fn_my_permissions('dbo.some_name','OBJECT')
You need to have CONTROL permission to drop it. You need to have ALTER permission to rename the object.
UPDATE
can you please again check whether you are part of sysadmin role at server level and part of db_owner role at db level (sysadmins enter database as db_owners).
use database_name
go
SELECT Is_srvrolemember('sysadmin')
select IS_ROLEMEMBER('db_owner')
At work, we have production databases on which developers have read permission. When developers have to fix something in the database, they must test their scripts in a copy of the production databases and then ask the DBA team to execute it in production.
Sometimes however, the data that must be fixed is not in the test databases. Developers then ask for a new copy of production databases, and this can take a lot of time.
Of course, we could grant them update permission and ask them to use BEGIN TRANSACTION / ROLLBACK, but it is too risky. Nobody wants that, not even the developers.
My question: is it possible to create a profile on SQL Server - or grant special permission - that would allow to execute update and delete commands but would always, no matter what the developer wrote, rollback after a GO or after the last command issued in a session?
This would be really helpful to test scripts before sending them to production.
You could create a sproc and give EXEC access to devs on that sproc only, SOLUTION #1 - SPROCS. This is probably the most elegant solution as you want them to have a simple way to run their query and also want to control their perms on the production environment. Example to execute a command would be: EXEC [dbo].[usp_rollback_query] 'master', 'INSERT INTO table1 SELECT * FROM table2
SOLUTION #1
USE [DATABASENAME]
GO
ALTER PROC dbo.usp_rollback_query
(
#db VARCHAR(128),
#query NVARCHAR(max)
)
AS
BEGIN
DECLARE #main_query NVARCHAR(max) = 'USE [' + #db + ']
' + #query;
BEGIN TRAN
EXEC sp_executesql #main_query;
ROLLBACK TRAN
END
If you can afford to have snapshot created and dropped each time, SOLUTION #2 - DB SNAPSHOTS is the best way to go about it. It's super fast, the only two drawbacks are that you need to kick people off the DB before you can restore and it will restore all changes made since the snapshot was created.
SOLUTION #2
-- CREATE SNAPSHOT
CREATE DATABASE [DATABASENAME_SS1]
ON
(
NAME = DATABASENAME,
FILENAME = 'your\path\DATABASENAME_SS1.ss'
) AS SNAPSHOT OF [DATABASENAME];
GO
-- let devs run whatever they want
-- CLOSE CONNECTIONS
USE [master];
GO
ALTER DATABASE [DATABASENAME]
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
GO
-- RETORE DB
RESTORE DATABASE [DATABASENAME]
FROM DATABASE_SNAPSHOT = 'DATABASENAME_SS1';
GO
-- CLEANUP SNAPSHOT COPY
DROP DATABASE [DATABASENAME_SS1];
I don't think ROLLBACK on each query is a good idea or a good design but if you have to go that route, you would need to use triggers. The limitation with triggers is that a DATABASE or SERVER level trigger can only be for DDL and not DML. Creating triggers on each TABLE object that you think is being altered is doable, however, the drawback here is that you need to know which tables are being modified and even then it's quite messy. Regardless please look at SOLUTION #3 - TABLE TRIGGERS below. To make this better you could create a role and check if the user is part of that role, then rollback.
SOLUTION #3
USE DATABASENAME
GO
ALTER TRIGGER dbo.tr_rollback_devs
ON dbo.table_name
AFTER INSERT, DELETE, UPDATE
AS
BEGIN
SET NOCOUNT ON;
IF SYSTEM_USER IN ('dev1', 'dev2')
ROLLBACK
END
GO
I cannot alter/drop any objects in my database, only create, insert, and delete records. For instance, I'm getting...
You can not
Msg 3609, Level 16, State 2, Line 1
The transaction ended in the trigger. The batch has been aborted.
on the following code (on the drop table step):
create schema Test
go
create table test.test (
test varchar(5)
)
go
insert into test.test(test) values('test')
go
select *
from Test.test
go
drop table Test.test
go
drop schema Test
go
I have definitely not created any triggers on the database EVER. I do not have control of the server so my permissions are limited. The problem just occurred. I have been using this database for years. This is the first time this has happened. I believe it has something to do with permissions.
I have no idea what is causing this new error to occur.
I found the problem. Someone hacked my database by adding a database trigger "test_ddl_trigger":
`SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
create trigger [test_ddl_trigger] on database for drop_table, alter_table as print 'You can not'
rollback
;
GO
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
ENABLE TRIGGER [test_ddl_trigger] ON DATABASE
GO`
I have tried the FILESTREAM feature for MSSQL (2008R2 Data Center) on a local database, to experiment. The real database is running on a server. I have setup the whole FILESTREAM, using this query:
/* CREATE FILESTREAM AND FILESTREAM TABLE */
USE [master]
GO
ALTER DATABASE SenONew
ADD FILEGROUP [FileStream]
CONTAINS FILESTREAM
GO
ALTER DATABASE SenONew
ADD FILE
(
NAME = 'fsSenONew',
FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\SenONew.ndf'
)
TO FILEGROUP [FileStream]
GO
USE [SenONew]
GO
CREATE TABLE Filestore(
FileID int PRIMARY KEY,
RowID uniqueidentifier ROWGUIDCOL NOT NULL UNIQUE DEFAULT NEWSEQUENTIALID(),
FileDescr nvarchar(max),
FileIndex varbinary(max) FILESTREAM NULL)
GO
And I was experimenting with adding a few files then deleting them.
Now since this was only meant to be an experiment, I also want to get rid of it. I'm using my local server for the development of the database that will be used on the real server, thus I'm creating BackUp's on my local server then Restore this on the real server, so it gets updated (software is in development, so the database structure changes much as well as the data and I need to do a full restore to the real server, where the software is being tested on).
After hours of searching, I couldn't find anything on my problem.
I understand that I need to:
Remove the database table storing the FILESTREAM information
I need to remove the FILE of the FILESTREAM
Remove the filegroup
So I'm using this query to get rid of everything I set up in the first place:
/* DROP FILESTREAM TABLE AND FILEGROUP */
USE SenONew
DROP TABLE Filestore
GO
ALTER DATABASE SenONew
REMOVE FILE fsSenONew
ALTER DATABASE SenONew
REMOVE FILEGROUP [FileStream]
GO
So I do everything as I should and it completes without error as well. So when I enter my filegroups, files and my file location, I see they are all completely removed:
But when I do a BACKUP of my local database (which include the deleted FILESTREAM, file path and filegroup) and try to restore the server with it, I get errors.
SQL to create a BACKUP:
/* CREATE BACKUP OF DATABASE WITHIN CURRECT CONNECTION */
DECLARE #FileName2 nvarchar(250)
SELECT #FileName2 = (SELECT 'C:\SenO BackUp\' + convert(nvarchar(200),GetDate(),112) + ' SenONew.BAK')
BACKUP DATABASE SenONew TO DISK=#FileName2
GO
Then do the Restore on the server:
/* CREATE RESTORE OF DATABASE WITHIN REAL SERVER CONNECTION */
use master
alter database SenONew set offline with rollback immediate;
DECLARE #FileName2 nvarchar(250)
SELECT #FileName2 = (SELECT '' + convert(nvarchar(200),GetDate(),112) + ' SenONew.BAK')
RESTORE DATABASE SenONew
FROM DISK = #FileName2
alter database SenONew set online with rollback immediate;
I get the following error:
*(Msg 5121, Level 16, State 2, Line 7
The path specified by "C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\SenONew.ndf" is not in a valid directory.
Msg 3156, Level 16, State 3, Line 7 File 'fsSenONew' cannot be restored to 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\SenONew.ndf'. Use WITH MOVE to identify a valid location for the file.
Msg 3119, Level 16, State 1, Line 7 Problems were identified while planning for the RESTORE statement. Previous messages provide details.
Msg 3013, Level 16, State 1, Line 7 RESTORE DATABASE is terminating abnormally. )*
I deleted the .ndf FILESTREAM location, why is it a specified path? Also, why is fsSenONew trying to restore? I can't get my head around it. Are there paths internally that I need to delete?
You can check:
SELECT * FROM SenONew.sys.data_spaces WHERE name = 'FileStream'
it should return 0 rows.
There is a procedure to remove FILESTREAM features from a SQL Server 2008 database :
ALTER TABLE Filestore DROP column FileIndex
GO
ALTER TABLE Filestore SET (FILESTREAM_ON="NULL")
GO
ALTER Database SenONew REMOVE FILE fsSenONew
GO
ALTER Database SenONew REMOVE FILEGROUP [FileStream]
GO
as described in this article. But the steps you did should do the same thing.
Your problem is certainly strange, but I suggest that you try using following
USE SenONew
EXEC Sp_help
EXEC Sp_helpfile
EXEC Sp_helpfilegroup
You may find something suspicious there like another table using that FILEGROUP.
I have done exactly the steps you describe and cannot reproduce your problem. Check how your Restore database screen looks like.
1.Remove the FILESTREAM attribute from columns and tables. You'll need to move data to a new column.
ALTER TABLE MyTable
ADD FileData varbinary(max) NULL;
GO
update MyTable
set FileData = FileStreamData
GO
ALTER TABLE MyTable
DROP column FileStreamData
GO
ALTER TABLE MyTable SET (FILESTREAM_ON="NULL")
GO
EXEC sp_RENAME 'MyTable.FileData', 'FileStreamData', 'COLUMN'
GO
2.Remove files from the FILESTREAM and drop the FILE and FILESTEAM.
ALTER DATABASE [MyDatabase] SET RECOVERY Simple
GO
EXEC SP_FILESTREAM_FORCE_GARBAGE_COLLECTION
ALTER DATABASE [MyDatabase] REMOVE FILE [MyFile]
GO
ALTER DATABASE [MyDatabase] REMOVE FILEGROUP [MyFileGroup]
GO
ALTER DATABASE [MyDatabase] SET RECOVERY FULL
GO
This is my script that worked for me. It was a command missing, to empty the file:
ALTER TABLE FilesTable DROP column FileContent
GO
ALTER TABLE FilesTable SET (FILESTREAM_ON="NULL")
GO
USE mydbname
GO
DBCC SHRINKFILE (N'filestreamfile', EMPTYFILE)
GO
EXEC sp_filestream_force_garbage_collection #dbname = N'mydbname'
ALTER Database mydbname REMOVE FILE filestreamfile
GO
ALTER Database mydbname REMOVE FILEGROUP FILESTREAMGROUP
GO
IF COL_LENGTH('FilesTable','FileContent') IS NULL
BEGIN
ALTER TABLE FilesTable ADD FileContent [varbinary](max) NULL
END
GO
During my integration tests, I try to drop database using:
USE master
ALTER DATABASE TestXyz SET SINGLE_USER WITH ROLLBACK IMMEDIATE
DROP DATABASE TestXyz
However, quite often (given the number of tests) one of the application background processes manages to get between SET SINGLE_USER and DROP DATABASE, which makes it single user of the database and breaks the DROP.
I can not use RESTRICTED_USER, as the application currently has db_owner permission (due to a large amount of legacy code, some of which requires it, so it will not be changed just for the tests).
I can not use OFFLINE as it does not delete database files from the disk.
How would you solve this problem?
OK plan b... iterate a drop of connections and rename the DB to get it away from the applications domain. Then drop it. To handle iterating through connections a try catch on the rename will hopefully allow it to run until it is able to drop the connection. Example code below creates a DB TestDB; renames it to testdb2 in the while loop before dropping it after the loop has succeeded.
-- Setup a scratch Db for testing
create database testdb
go
use testdb
while exists (select name from sys.databases where name = 'testdb')
Begin
DECLARE #DbName nvarchar(50) SET #DbName = N'testdb'
DECLARE #EXECSQL varchar(max) SET #EXECSQL = ''
SELECT #EXECSQL = #EXECSQL + 'Kill ' + Convert(varchar, SPId) + ';'
FROM MASTER..SysProcesses
WHERE DBId = DB_ID(#DbName) AND SPId <> ##SPId
EXEC(#EXECSQL)
Begin try
EXEC sp_renamedb 'testdb', 'testdb2'
end try
Begin Catch
print 'failed to rename'
End Catch
end
drop database testdb2
Try this once:
Stop application services and run your query.
Stop application services and restart SQL Server Services and then run your query.
I have finally solved it using the following approach:
ALTER LOGIN MyAppUser DISABLE
ALTER DATABASE TestXyz SET SINGLE_USER WITH ROLLBACK IMMEDIATE
DROP DATABASE TestXyz
ALTER LOGIN MyAppUser ENABLE
Since I can use different login for test database management process, this allows me to block application from accessing the DB. (The reason for SINGLE_USER here is just to kick already connected users. I haven't checked if ALTER LOGIN already does that, but I assume it does not).
Alternative option is to delete MyAppUser from the database before dropping it, however I thought about it only now and do not have code for it.