Delete from table in all databases containing table - tsql

I want to create a T-SQL query which are deleting all rows in the table Logins in ALL databases containing this exact table, so it can be run without any errors.
I want to reuse the code to other stuff as well, e.g. finding all active users from all databases containing the table Users. Therefore, I think, the best solution would be a pure T-SQL solution. This way the query can even become an automated job run by SQL Server Agent
Is it possible? And how?

Build some dynamic SQL:
declare #sql varchar(max) = ''
select #sql = #sql +
'use [' + name + '] ' +
'if exists (select * from sys.tables where name = ''Logins'') ' +
'delete from Logins '
from sys.databases where name not in ('master','model','msdb','tempdb')
print #sql
--exec (#sql)
Uncomment the exec line to actually run the code rather than just see what would be executed.

Related

How to insert dynamic sql result into temp table without knowing the columns in advance

I am trying to insert result of dynamic sql into temp table. Important thing is i dont know the column names in advance. As per the SO suggestion the following should work
INSERT into #T1 execute ('execute ' + #SQLString )
also, omit the EXECUTE if the sql string is something other than a procedure.
However this is not working on SQL 2017
CREATE TABLE Documents(DocumentID INT, Status NVARCHAR(10))
INSERT INTO Documents(DocumentID,Status)
VALUES
(1,'Active'),
(2,'Active'),
(3,'Active'),
(4,'Active')
DECLARE #SQLString NVARCHAR(MAX)
SET #SQLString = 'SELECT * FROM Documents'
INSERT into #T1 execute ('execute ' + #SQLString )
I get error `Invalid object name '#T1'.`
Then i tried by omitting execute
INSERT into #T1 execute (#SQLString)
with same error `Invalid object name '#T1'.`
I should be able to do
SELECT * FROM #T1
You cannot do an INSERT INTO without having the table predefined. But what I believe you are asking is to do a SELECT INTO. I am aware of two ways of doing it. The first uses OPENROWSET, but I believe this has some drawbacks for security purposes. You could do the following:
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
SELECT *
INTO #T1
FROM OPENROWSET('SQLNCLI',
'Server=localhost;Trusted_Connection=yes;',
'SELECT * from <YOURDATABASE>.dbo.Documents')
Your second option is to create an inline TVF that will generate the table structure for you. So you could do the following:
CREATE FUNCTION getDocuments()
RETURNS TABLE
AS
RETURN
SELECT * from Documents
GO
SELECT * into #T1 FROM getDocuments()

Modify table name at runtime

I want to take a backup of a table with the timestamp value linked in the backup table.So that it can be easily figured out to which date this backup belongs to.I am trying something like this which is obviously not working.
Please suggest how to modify table name at runtime.
Scenario:
Insert into original_table+'_'+Convert(varchar(10),GETDATE(),112)
select * from original_table
The output should be:
A table should be created original_table_20141015 with the data.
You can build a SQL string with the new table name, then execute it using sp_executesql.
Example:
DECLARE #sql nvarchar(MAX)
SET #sql = 'SELECT * INTO original_table_' +
CONVERT(varchar(8), GETDATE(), 112) +
' FROM original_table'
EXEC sp_executesql #sql

How to convert SQL Server 2008 R2 database to SQL Server 2012?

I installed SQL Server 2012, and attached a database originally generated by SQL Server 2008 R2.
Everything appeared to work perfectly, with one problem: merges dropped from 1000 per second to 10 per second (a 100x slowdown).
I'm surmising that its because I am accessing a SQL Server 2008 R2 database from SQL Server 2012. Is there some way to convert the database to SQL Server 2012 format? Or is there something else thats going on that might explain the 100x slowdown in performance?
Please make sure that you set the compatibility mode of the database to 110, and update statistics.
ALTER DATABASE MyDatabase SET COMPATIBILITY_LEVEL = 110;
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += CHAR(13) + CHAR(10) + 'UPDATE STATISTICS '
+ QUOTENAME(SCHEMA_NAME(schema_id))
+ '.' + QUOTENAME(name) + ' WITH FULLSCAN;'
FROM sys.tables;
PRINT #sql;
--EXEC sp_executesql #sql;
When I ran the SQL in the answer the nvarchar overflowed. The problem is when your database has too many tables the SQL is too long for an nvarchar. My database had enough tables to overflow a varchar (twice as long as a nvarchar). So I edited the SQL to loop through each table and execute separate statements. This way you wont miss updating the stats on any of your tables.
ALTER DATABASE MyDatabase SET COMPATIBILITY_LEVEL = 110;
DECLARE #SQL NVARCHAR(MAX) = N'';
Declare #Tables table
([Schema] nvarchar(50)
,[TableName] nvarchar(100))
Insert into #Tables
Select QUOTENAME(SCHEMA_NAME(schema_id)),QUOTENAME(name)
FROM sys.tables;
Declare #Schema nvarchar(50), #TableName nvarchar(100)
While Exists(Select * From #Tables)
Begin
Select Top 1 #Schema = [Schema], #TableName = [TableName] From #Tables
Set #SQL = 'UPDATE STATISTICS ' + #Schema + '.' + #TableName + ' WITH FULLSCAN;'
Begin Try
EXEC SP_ExecuteSql #SQLToExecute = #SQL
Print 'Completed: ' + #SQL
End Try
Begin Catch
DECLARE #ErrMsg nvarchar(4000)
SELECT #ErrMsg = SubString(ERROR_MESSAGE(),0,900)
Select GetDate(), 'Failed updating stats on ' + #Schema + ' ' + #TableName + '. Error: '+#ErrMsg
End Catch
Delete From #Tables Where [Schema] = #Schema and [TableName] = #TableName
End
Updating the Stats is must when you detach and attach database. Otherwise query planner cannot generate efficient execution plan and end-up with long execution time. This is what I noticed.
To upgrade a database file to use LocalDB:
1.In Server Explorer, choose the Connect to Database button.
2.In the Add Connection dialog box, specify the following information:
Data Source: Microsoft SQL Server (SqlClient)
Server Name: (LocalDB)\v11.0
Attach a database file: Path, where Path is the physical path of the primary .mdf file.
Logical Name: Name, where Name is the name that you want to use with the file.
Choose the OK button.
When prompted, choose the Yes button to upgrade the file.
Is this on the right track:
http://msdn.microsoft.com/en-us/library/ms189625.aspx
USE master;
GO
CREATE DATABASE MyDatabase
ON (FILENAME = 'C:\MySQLServer\MyDatabase.mdf'),
(FILENAME = 'C:\MySQLServer\Database.ldf')
FOR ATTACH;
GO

Does EXECUTE AS protect against SQL Injection?

I have a situation where I need to do this
CREATE PROCEDURE search_sp #condition varchar(8000) AS
SELECT * FROM tbl WHERE #condition
If I add a user to the database that only has the 'db_datareader' role and then use execute as to switch context to that user for the purposes of running the select statement, would this then protect me from SQL injection?
e.g.
DECLARE #cookie varbinary(100);
EXECUTE AS USER = 'restricted__user' WITH COOKIE INTO #cookie;
DECLARE #SQL AS NVARCHAR(MAX)
SET #SQL= 'SELECT * FROM tbl WHERE ' + #condition
EXEC sp_executesql #SQL
REVERT WITH COOKIE = #cookie;
No, it won't. This will limit the queries that could be run if injected if the "EXECUTE AS USER" is even evaluated. It won't stop something like foo' or 1 = 1; /*... which can be a case where "EXECUTE AS USER" is never reached. Properly handling the input is required.
What happens when I pass "1=1" as condition?
Now you send back all rows in 'tbl'. If table is, say, users, I now have all user names and all passwords (hopefully they're hashed and salted...).

How to create DDL trigger to all databases in SQL Server 2005 instance

I am going to create a DDL trigger to all databases in SQL Server instance. I'd like to do it in one run instead of many runs for each database.
Below are the two T-SQL statements I need to execute:
-- Create table
use <dbname>
GO
CREATE TABLE dbo.ChangeAttempt
(EventData xml NOT NULL,
AttemptDate datetime NOT NULL DEFAULT GETDATE(),
DBUser char(50) NOT NULL)
GO
-- Create DDL trigger
use <dbname>
GO
CREATE TRIGGER db_trg_ObjectChanges
ON DATABASE
FOR ALTER_PROCEDURE, DROP_PROCEDURE,
ALTER_INDEX, DROP_INDEX,
ALTER_TABLE, DROP_TABLE, ALTER_TRIGGER, DROP_TRIGGER,
ALTER_VIEW, DROP_VIEW, ALTER_SCHEMA, DROP_SCHEMA,
ALTER_ROLE, DROP_ROLE, ALTER_USER, DROP_USER
AS
SET NOCOUNT ON
INSERT dbo.ChangeAttempt
(EventData, DBUser)
VALUES (EVENTDATA(), USER)
GO
My question is: how can I programmaticaly create DDL trigger in one run?
why do you need one run? this is the only way to do it.
Msg 111, Level 15, State 1, Line 2
'CREATE TRIGGER' must be the first statement in a query batch.
run the output generated by this:
DECLARE #DatabaseName varchar(500)
DECLARE #Database_id int
DECLARE #Query varchar(8000)
DECLARE #CRLF char(2)
SET #CRLF=CHAR(13)+CHAR(10)
---MODIFY THIS TO INCLUDE THE DATABASES THAT YOU WANT TO WORk ON
---MODIFY THIS TO INCLUDE THE DATABASES THAT YOU WANT TO WORk ON
select #Database_id=MIN(database_id) from sys.databases where database_id IN (5,7,8,6)
WHILE #Database_id IS NOT NULL
BEGIN
SELECT #DatabaseName=name from sys.databases where database_id=#Database_id
SET #Query='-- Create table'+#CRLF+#CRLF
+'use '+#DatabaseName+#CRLF
+' GO'+#CRLF
+' CREATE TABLE dbo.ChangeAttempt'+#CRLF
+' (EventData xml NOT NULL,'+#CRLF
+' AttemptDate datetime NOT NULL DEFAULT GETDATE(),'+#CRLF
+' DBUser char(50) NOT NULL)'+#CRLF
+'GO'+#CRLF+#CRLF
+'-- Create DDL trigger '+#CRLF+#CRLF
+'use '+#DatabaseName+#CRLF
+'GO'+#CRLF
+'CREATE TRIGGER db_trg_ObjectChanges'+#CRLF
+'ON DATABASE'+#CRLF
+'FOR ALTER_PROCEDURE, DROP_PROCEDURE,'+#CRLF
+' ALTER_INDEX, DROP_INDEX,'+#CRLF
+' ALTER_TABLE, DROP_TABLE, ALTER_TRIGGER, DROP_TRIGGER,'+#CRLF
+' ALTER_VIEW, DROP_VIEW, ALTER_SCHEMA, DROP_SCHEMA,'+#CRLF
+' ALTER_ROLE, DROP_ROLE, ALTER_USER, DROP_USER'+#CRLF
+'AS'+#CRLF
+'SET NOCOUNT ON'+#CRLF
+'INSERT dbo.ChangeAttempt'+#CRLF
+'(EventData, DBUser)'+#CRLF
+'VALUES (EVENTDATA(), USER)'+#CRLF
+'GO'+#CRLF
PRINT #Query
---MODIFY THIS TO INCLUDE THE DATABASES THAT YOU WANT TO WORk ON
---MODIFY THIS TO INCLUDE THE DATABASES THAT YOU WANT TO WORk ON
select #Database_id=MIN(database_id) from sys.databases WHERE database_id IN (5,7,8,6) AND database_id>#Database_id
END
EDIT
to determine what databases to generate scripts for do the following:
run this query:
select database_id,name from sys.databases
find all of the databases you want to run the scripts for
change my above script in two places (before loop & at bottom in loop) so all of the database_id that you want are in the following code section:
WHERE database_id IN (AAA,BBB,CCC,DDD,....)
You could use sp_MSforeachdb.
Something like this
sp_MSforeachdb
'
CREATE TABLE ?.dbo.ChangeAttempt
etc. etc. etc.
'
sp_MSforeachdb
'
CREATE TRIGGER ?.dbo.db_trg_ObjectChanges
etc. etc. etc.
'
I haven't tested this, in theory I'd expect it to work though. You want to make sure you exclude the system databases though.