How to clean sys.conversation_endpoints - sql-server-2005

I have a table, a trigger on the table implemented using service broker. More than Half million records are inserted daily into the table.
The asynchronous SP is used to check sveral condition by using inserted data and update other tables. It was running fine for last 1 month and the SP was get executed withing 2-3 seconds of insertion of record. But now it take more than 90 minute.
At present sys.conversation_endpoints have too much records.
(Note that all the table are truncated daily as I do not need those records day after)
Other database activities are normal (average 60% CPU Utilization).
Now where i need to look??
I can re-create database without any problem but i don't think it is a good way to resolve the problem

you can 'end conversation #handle' or 'end conversation #handle with cleanup'

Script to end all conversations for a particular service:
DECLARE #sql NVARCHAR(MAX)
,#far_service NVARCHAR(256) = N'far_service_name';
WHILE EXISTS (
SELECT COUNT(*)
FROM sys.conversation_endpoints
WHERE far_service = #far_service
)
BEGIN
SET #sql = N'';
SELECT TOP 1000 #sql = #sql + N'END CONVERSATION ''' + CAST(conversation_handle AS NVARCHAR(50)) + N''' WITH CLEANUP;
'
FROM sys.conversation_endpoints
WHERE far_service = #far_service;
--PRINT #sql;
EXEC sys.sp_executesql #stmt = #sql;
--RETURN;
END;
GO

Here is my solution, after modifying code from #thesqldev
USE [MY_DATABASE_NAME_HERE]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[mqConversationsClearAll]
AS
-- Note: you can check the queue by running this query
-- SELECT * FROM sys.conversation_endpoints GO
DECLARE #getid CURSOR
,#sql NVARCHAR(MAX)
,#conv_id NVARCHAR(100)
,#conv_handle NVARCHAR(100)
,#conv_service NVARCHAR(100)
-- ,#far_service NVARCHAR(256) = N'One_Specific_Service_Target';
-- want to create and execute a chain of statements like this, one per conversation
-- END CONVERSATION 'FE851F37-218C-EA11-B698-4CCC6AD00AE9' WITH CLEANUP;
-- END CONVERSATION 'A4B4F603-208C-EA11-B698-4CCC6AD00AE9' WITH CLEANUP;
SET #getid = CURSOR FOR
SELECT [conversation_id], [conversation_handle], [far_service]
FROM sys.conversation_endpoints
WHERE NOT([State] = 'CO')
-- CO = CONVERSING [State]
-- alternatively, might like one service name only
-- WHERE far_service = #far_service
OPEN #getid
FETCH NEXT
FROM #getid INTO #conv_id, #conv_handle, #conv_service
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'END CONVERSATION ' + char(39) + #conv_handle + char(39) + ' WITH CLEANUP;'
EXEC sys.sp_executesql #stmt = #sql;
FETCH NEXT
FROM #getid INTO #conv_id, #conv_handle, #conv_service
END
CLOSE #getid
DEALLOCATE #getid
Then you can execute the newly created stored procedure "mqConversationsClearAll" whenever you want to clear sys.conversation_endpoints

Related

Transaction context in use by another session - SQL SERVER

Good afternoon,
I'm creating a trigger to be present on 3 databases, one of which is on a linkedserver.
When inserting data in one of the databases, the objective is to replicate the information in the remaining databases.
Whenever I do an insert, I get the error 'Transaction context in use by another session.'
Anyone knows why this is happening? And is there a way to surprass this?
DROP TRIGGER Insertdata
USE A
GO
SET ANSI_NULLS ON
SET ANSI_WARNINGS ON
SET QUOTED_IDENTIFIER OFF
GO
CREATE TRIGGER Insertdata ON DATA WITH ENCRYPTION FOR INSERT AS
SET NOCOUNT ON
if(Upper((SELECT DB_NAME()))=UPPER('A')) Begin
----------------------------------------------------------------------------------------------
--------------------------------------------- Insert Data ------------------------------------
----------------------------------------------------------------------------------------------
--Create variable for database name and query variable
DECLARE #DB_Name VARCHAR(100) -- database name
DECLARE #Local VARCHAR(1) -- database name
DECLARE #query VARCHAR(4000) -- query variable
--Declare the cursor
DECLARE db_cursor CURSOR LOCAL FOR
-- Populate the cursor with the selected database name
select t.name,t.islocal from (
Select name, '1' as islocal from master.sys.databases WHERE name in ('A','B','C')
union all
select name, '0' as islocal from LinkedServer.master.sys.databases WHERE name in ('A','B','C')
)t
OPEN db_cursor
--Moves the cursor to the first point and put that in variable name
FETCH NEXT FROM db_cursor INTO #DB_Name,#Local;
-- while loop to go through all the DB selected
WHILE ##FETCH_STATUS = 0
BEGIN
if(#Local ='0') begin
Set #DB_Name = 'LinkedServer.' + #DB_Name
end
-- Check if received data exists
SET #query = N'Select id from ' + #DB_Name +'.dbo.data where data.id='''+(Select inserted.id from inserted)+''''
EXEC(#query)
--If doesnt exists, then insert data
if ##ROWCOUNT < 1 begin
SET #query = N'INSERT INTO ' + #DB_Name +'.dbo.data(id,name,surname)
values('+''''+(select inserted.id from inserted)+''''
+','+''''+(select convert(varchar,inserted.name) from inserted)+''''
+','+''''+(select convert(varchar,inserted.surname) from inserted)+''''
+')'
EXEC (#query)
--Fetch the next record from the cursor
end
FETCH NEXT FROM db_cursor INTO #DB_Name,#Local
END
--Close and deallocate cursor
CLOSE db_cursor
DEALLOCATE db_cursor
End
GO
SET ANSI_NULLS OFF
SET ANSI_WARNINGS OFF
SET QUOTED_IDENTIFIER OFF
GO
EDIT:
I think the problem here is the fact, that my trigger is being executed on different databases. For example, the current database is A, so the trigger should execute on B and C, but I realized that when A calls B, B executes the trigger aswell. C doesn't executes because gives the error on B. Thought db_name() would fix this, but guess it doesn't

Drop databases with no tables

Is there a query which drops / deletes databases with no tables in them (deletes empty databases)?
Server is Microsoft SQL Server 2005
This should do it.
Tested on a lab machine and it dropped all databases with 0 user tables.
Note, however, that tables aren't the only things in a database, necessarily. There could be stored procedures, functions, etc that someone might still need.
NOTE THAT THIS IS A VERY DANGEROUS OPERATION, AS IT DROPS DATABASES. USE AT YOUR OWN RISK. I AM NOT RESPONSIBLE FOR DAMAGE YOU CAUSE.
USE [master];
DECLARE #name varchar(50);
DECLARE #innerQuery varchar(max);
DECLARE tableCursor CURSOR FOR SELECT name FROM sys.databases where owner_sid != 0x01;
OPEN tableCursor;
FETCH NEXT FROM tableCursor
INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #innerQuery = 'USE [' + #name + ']; IF (SELECT COUNT(*) FROM sys.objects WHERE type = ''U'') = 0
BEGIN
USE [master];
DROP DATABASE [' + #name + ']
END'
EXEC(#innerQuery)
FETCH NEXT FROM tableCursor INTO #name
END
CLOSE tableCursor;
DEALLOCATE tableCursor;
Note also that, if a database is in use, SQL Server will refuse to drop it. So, if there are other connections to a particular database that this tries to drop, the command will abort.
To avoid that problem, you can set the database in question to single-user mode.
The following script is the same as the above, except it also sets the target databases to single-user mode to kill active connections.
BE EVEN MORE CAREFUL WITH THIS, AS IT'S ESSENTIALLY THE NUCLEAR OPTION:
use [master];
DECLARE #name varchar(50);
DECLARE #innerQuery varchar(max);
DECLARE tableCursor CURSOR FOR SELECT name FROM sys.databases where owner_sid != 0x01;
OPEN tableCursor;
FETCH NEXT FROM tableCursor
INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #innerQuery =
'USE [' + #name + '];
IF (SELECT COUNT(*) FROM sys.objects WHERE type = ''U'') = 0
BEGIN
USE [master];
ALTER DATABASE [' + #name + '] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [' + #name + '];
END'
EXEC(#innerQuery)
FETCH NEXT FROM tableCursor INTO #name
END
CLOSE tableCursor;
DEALLOCATE tableCursor;
I think it is not possible (at least not with a TSQL command, might be a stored procedure somewhere). You would have to query sysobjects and abort if any found. (And you have to decide if you want to ignore some of the system objects like the design tables).
The script below will generate the needed DROP DATABASE script. You could tweak this to execute the statement too (in the master database context) but I suggest you review it first just in case.
EXEC sp_MSforeachdb N'
USE [?];
IF N''?'' NOT IN(N''master'', N''model'', N''msdb'', N''tempdb'')
BEGIN
IF NOT EXISTS(
SELECT *
FROM sys.tables
WHERE
OBJECTPROPERTYEX(object_id, ''IsMSShipped'') = 0
)
BEGIN
PRINT ''DROP DATABASE [?];'';
END;
END;';

sp_MSForEachDB doesn't seem to like GO

I have used this SP before. Now, I am trying to permission a user to 50 odd databases that start with the same letters, using the code below. It looks like it does not like "GO" in the code. Why is that ? and what is the work around?
Thanks for your time.. :)
RM
exec sp_MSForEachDB
'
IF ''?'' LIKE ''MYDBNames%''
BEGIN
Use [?]
Go
CREATE USER [MYDOMAIN\Analysts] FOR LOGIN [MYDOMAIN\Analysts]
GO
EXEC sp_addrolemember N''db_owner'', N''MYDOMAIN\Analysts''
GO
END
'
I just explained this in another question yesterday (here). This essence is this: GO isn't a SQL statement, it's an SSMS/SQLCMD command that is used to separate batches (groups of SQL statements that are compiled together). So you cannot use it in things like stored procedures or Dynamic SQL. Also, very few statement contexts can cross over a GO boundary (transactions and session-level temp tables are about it).
However, because both stored procedures and Dynamic SQL establish their own separate batches/execution contexts, you can use these to get around the normal need for GO, like so:
exec sp_MSForEachDB
'
IF ''?'' LIKE ''MYDBNames%''
BEGIN
Use [?]
EXEC(''
CREATE USER [MYDOMAIN\Analysts] FOR LOGIN [MYDOMAIN\Analysts]
'')
EXEC('' EXEC sp_addrolemember N''''db_owner'''', N''''MYDOMAIN\Analysts'''' '')
END
'
The word GO is a batch separator and is not a SQL keyword. In SSMS you can go to options and change it to anything - COME for example.
Try this:
exec sp_MSForEachDB
'
IF ''?'' LIKE ''MYDBNames%''
BEGIN;
Use [?];
CREATE USER [MYDOMAIN\Analysts] FOR LOGIN [MYDOMAIN\Analysts];
EXEC sp_addrolemember N''db_owner'', N''MYDOMAIN\Analysts'';
END;'
Just stop using GO, it's not necessary (and it's not even T-SQL; it's just a batch separator for Management Studio).
Stop using sp_MSForEachDB. Oh, the problems (if you want proof, see here, here and here).
Here is code that does this without using any stored procedure:
DECLARE #sql NVARCHAR(MAX) = N'';
SELECT #sql += N'USE ' + QUOTENAME(name) + ';
CREATE USER [MYDOMAIN\Analysts] FOR LOGIN [MYDOMAIN\Analysts];
EXEC sys.sp_addrolemember N''db_owner'', N''MYDOMAIN\Analysts'';
-- actually should use ALTER ROLE now
'
FROM sys.databases
WHERE state = 0 -- online
AND name LIKE N'MyDBName%';
PRINT #sql;
-- EXEC sys.sp_executesql #sql;
Like #RBarryYoung's answer, an embedded exec() can be used to avoid needing a "go" when the command carries the requirement that it must be the "first within a batch." I don't think the question's example needs a "go" but the headline itself may lead people here.
Here is a basic SP to wrap this that support's DDL statements like "create proc" and provides some error messages. I'm probably the ~5th person to do this and this one isn't the end all.
Blessing/Curse: There is a call to Common.ufn_UsvToNVarcharKeyTable() which you may need to patch in your own CSV splitter or remove:
alter proc [Common].[usp_ForEachDatabase]
#pSql nvarchar(max),
#pDatabaseNameLikeOpt nvarchar(300) = null, -- Optional pattern to match with like
#pDatabaseNameCsvOpt nvarchar(max) = null, -- Optional list of DBs
#pJustPrintDbNames bit = 0, -- Don't exec, just print database names
#pDebug bit = 1 -- may add additional print statements
as
/*-----------------------------------------------------------------------------------------------------------------
Purpose: Execute SQL on each database. Replacement for the standard sp_MSForEachDB which has a size character
limit and requires an exec() within for DDL commands which must be the first in a batch.
Sources: Ideas from http://www.experts-exchange.com/Database/MS-SQL-Server/SQL-Server-2005/Q_26337388.html
http://stackoverflow.com/questions/20125430
http://stackoverflow.com/questions/1819095/
Modified By Description
---------- ---------- -----------------------------------------------------------------------------------
2014.10.14 crokusek Initial version. Pieces from internet.
--------------------------------------------------------------------------------------------------------------*/
begin try
declare databaseCursor cursor local forward_only static for
select IsNull(sd.Name, ud.Name) as Name,
--
-- If a list was specified, flag when a name was not found
--
convert(bit, iif(sd.Name is null, 1, 0)) as IsNotFound
from
(
select Name from sys.databases
where Name not in ('master', 'tempdb', 'model', 'msdb')
and is_distributor = 0 -- http://stackoverflow.com/questions/1819095/
) sd
full outer join ( select Value as Name from Common.ufn_UsvToNVarcharKeyTable(#pDatabaseNameCsvOpt, ',' ) ) ud
on ud.Name = sd.Name
where (#pDatabaseNameLikeOpt is null or IsNull(sd.Name, ud.Name) like #pDatabaseNameLikeOpt)
and (#pDatabaseNameCsvOpt is null or ud.Name is not null)
order by IsNull(sd.Name, ud.Name);
declare
#matchingDatabaseNames nvarchar(max),
#databaseName nvarchar(300),
#isNotFound bit,
#errorCount int = 0,
#successCount int = 0,
--
-- Use an embedded exec() to place the user command(s) in a separate context to avoid errors like:
-- CREATE/ALTER PROCEDURE must be the first statement in a query batch.
--
#sqlExec nvarchar(max) = N'exec (''' + replace(#pSql, '''', '''''') + ''')';
open databaseCursor
fetch next from databaseCursor into #databaseName, #isNotFound;
while ##fetch_status = 0
begin
if (#isNotFound = 1)
begin
print 'Error: Database ' + #databaseName + ' was not found.';
set #errorCount += 1;
end
else
begin
set #matchingDatabaseNames = coalesce(#matchingDatabaseNames + ',', '') + #databaseName; -- Create Csv
print 'Database: ' + #databaseName;
if (#pJustPrintDbNames = 0)
begin
declare
#execSql nvarchar(max) = 'use ' + #databaseName + ';' + char(10) + #sqlExec;
begin try
exec (#execSql)
set #successCount += 1;
end try
begin catch
select #databaseName as [Database],
error_number() as ErrorNumber,
error_severity() as ErrorSeverity,
error_state() as ErrorState,
error_procedure() as ErrorProcedure,
error_line() as ErrorLine,
error_message() as ErrorMessage;
set #errorCount += 1;
end catch
end
end
fetch next from databaseCursor into #databaseName, #isNotFound;
end
if (#pJustPrintDbNames = 1)
print #matchingDatabaseNames; -- this can then be used as input
else
print 'Completed with ' + convert(varchar(30), #errorCount) + ' Errors ' +
'and ' + convert(varchar(30), #successCount) + ' Successes.';
end try
begin catch
if (xact_state() = -1)
rollback;
-- Log / Rethrow
end catch
go
Usage:
declare
#sql nvarchar(max) = N'
create proc usp_ReplaceEachSingleQuoteWithTwoSingleQuotesInTheDefinition
...';
exec [Common].[usp_ForEachDatabase]
#pSql = #sql,
#pDatabaseNameLikeOpt = null,
#pDatabaseNameCsvOpt = null,
#pJustPrintDbNames = 0,
#pDebug = 1;
Why not use a cursor?
They are great for management tasks like this. As long as the record set is small, they perform well.
The code below creates a SQL statement for each database that uses the ; to combine multiple tasks.
Unlike the code above, it remove any existing users before creating a new one. Avoids a possible issue.
Then it executes the code. If you want, you could even log these adhoc requests to a internal table and add error checking.
Good luck.
--
-- EXEC same statements against several like databases
--
-- Declare local variables
DECLARE #STMT NVARCHAR(4000);
-- Allocate cursor, return table names
DECLARE MYTRG CURSOR FAST_FORWARD FOR
SELECT
' use [' + ltrim(rtrim(d.name)) + ']; ' +
' IF EXISTS (SELECT * FROM sys.database_principals WHERE name = N''[MYDOMAIN\Analysts]'') DROP USER [MYDOMAIN\Analysts]; ' +
' CREATE USER [MYDOMAIN\Analysts] FOR LOGIN [MYDOMAIN\Analysts] WITH DEFAULT_SCHEMA=[DBO]; ' +
' sp_addrolemember N''db_owner'', N''MYDOMAIN\Analysts''; ' as STMT
FROM master.dbo.sysdatabases d
WHERE d.name like 'MYDBNames%'
ORDER BY d.name;
-- Open cursor
OPEN MYTRG;
-- Get the first row
FETCH NEXT FROM MYTRG INTO #STMT;
-- While there is data
WHILE (##FETCH_STATUS = 0)
BEGIN
-- Show detail database info
EXEC sp_executesql #STMT;
-- Get the first row
FETCH NEXT FROM MYTRG INTO #STMT;
END;
-- Close the cursor
CLOSE MYTRG;
-- Release the cursor
DEALLOCATE MYTRG;

get sql log file name from within a script

We have a few test databases that we are throwing test indexes into and the log file gets bloated real quickly due to us dropping the contents of the table and repopulating it.
I have found, thanks to Stack Overflow, a few scripts and put them together to do what I need.
Here is the script:
USE SSSIndexes
GO
ALTER DATABASE SSSIndexes SET RECOVERY SIMPLE WITH NO_WAIT
GO
DBCC SHRINKFILE(N'SSSIndexes_Log', 1) <-- my issue is here
GO
The problem is the log file name. Is there a way to get the log file name without having to look it up manually and include it in the script to where this part is automated?
Btw, we never intend on restore this database. These are temporary indexes.
Thanks!
USE SSSIndexes
GO
ALTER DATABASE SSSIndexes SET RECOVERY SIMPLE WITH NO_WAIT
GO
DECLARE #Name NVARCHAR(50)
DECLARE cur CURSOR FOR
SELECT [name]
FROM [sys].[database_files]
where [type] = 1
OPEN cur
FETCH NEXT FROM cur INTO #Name
WHILE ##FETCH_STATUS = 0
BEGIN
DBCC SHRINKFILE(#Name, 1)
FETCH NEXT FROM cur INTO #Name
END
CLOSE cur
DEALLOCATE cur
You can use this to generate script for log file truncating of all databases on a specific server. To stick to specific databases use a filter.
SELECT
' USE [' + name + ']; GO
-- Truncate the log by changing the database recovery model to SIMPLE.
ALTER DATABASE [' + name + '] SET RECOVERY SIMPLE;
GO
-- Shrink the truncated log file to 1 MB.
DECLARE #logname varchar(128);
SELECT TOP 1 #logname=[name] FROM [' + name + '].[sys].[database_files] WHERE [type] = 1;
DBCC SHRINKFILE (#logname, 1);
GO
-- Reset the database recovery model.
ALTER DATABASE [' + name + '] SET RECOVERY FULL;
GO
' AS qry FROM master.dbo.sysdatabases
WHERE dbid > 6
WARNING!!!: YOU SHOULD ONLY DO THIS ON TEST DB SERVERS. Production DBs typically WANT to have FULL recovery mode. Test DBs you usually don't care.
Expanding on Abdul's answer (I had trouble getting it to show in new lines) and Dis' answers this finds the first db that has recovery FULL, sets it to simple and shrinks it... just keep clicking execute until it returns null
declare #dbname nvarchar(50)
declare #logfilename nvarchar(100)
select top(1) #dbname = name from sys.databases where recovery_model_desc <> 'SIMPLE' AND name <> 'tempdb'
declare #shrinkdb nvarchar(500)
set #shrinkdb = 'USE master
ALTER DATABASE [' + #dbname + '] SET RECOVERY SIMPLE
'
select #shrinkdb
execute sp_executesql #shrinkdb
set #shrinkdb = 'USE [' + #dbname + ']
DECLARE #Name NVARCHAR(50)
DECLARE cur CURSOR FOR
SELECT [name]
FROM [sys].[database_files]
where [type] = 1
OPEN cur
FETCH NEXT FROM cur INTO #Name
WHILE ##FETCH_STATUS = 0
BEGIN
DBCC SHRINKFILE(#Name, 1)
FETCH NEXT FROM cur INTO #Name
END
CLOSE cur
DEALLOCATE cur
'
select #shrinkdb
execute sp_executesql #shrinkdb

SQL Server Kill Command

I have an SQL script which backs up a database and then restores it over another database. the problem I'm having is that the database being overwritten is being locked open by a user so the job fails. I can manually kill the attached user and it runs fine but I need this process to run automatically every night. Is there a kill command which I can time to execute every night? or is there something in the restore options to do the same thing?
Any thoughts?
Thanks
KILL isn't always effective if the client reconnects
I'd consider taking the database offline (or dbo only) then restoring. Thus will prevent further reconnects.
ALTER DATABASE TargetDB SET OFFLINE WITH ROLLBACK IMMEDIATE
or
ALTER DATABASE TargetDB SET RESTRICTED_USER WITH ROLLBACK IMMEDIATE
agree with #gbn you can use following code to get database restore success also
USE master
GO
ALTER DATABASE YourDatabaseName SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
ALTER DATABASE YourDatabaseName SET OFFLINE WITH ROLLBACK IMMEDIATE
GO
however you can use following script to kill connections to selected database.
-- Create the sql to kill the active database connections
DECLARE #execSql VARCHAR(4000),
#databaseName VARCHAR(100)
-- Set the database name for which to kill the connections
SET #databaseName = '[yourdatabase]'
SET #execSql = ''
SELECT #execSql = #execSql + 'kill ' + CONVERT(CHAR(10), spid) + ' '
FROM master.dbo.sysprocesses
WHERE DB_NAME(dbid) = #databaseName
AND DBID <> 0
AND spid <> ##spid
EXEC ( #execSql
)
DECLARE #pid AS INTEGER
DECLARE mycursor CURSOR FOR
select spid
from sys.sysprocesses
WHERE dbid = DB_ID('yourdatabasename')
OPEN mycursor
FETCH NEXT FROM mycursor INTO #pid
WHILE ##FETCH_STATUS = 0
BEGIN
EXECUTE ('KILL '+#pid)
FETCH NEXT FROM mycursor INTO #pid
END
CLOSE mycursor
DEALLOCATE mycursor
Select 'Kill '+ CAST(p.spid AS VARCHAR)KillCommand into #temp
from master.dbo.sysprocesses p (nolock)
join master..sysdatabases d (nolock) on p.dbid = d.dbid
Where d.[name] = 'your db name'
Declare #query nvarchar(max)
--Select * from #temp
Select #query =STUFF((
select ' ' + KillCommand from #temp
FOR XML PATH('')),1,1,'')
Execute sp_executesql #query
Drop table #temp
Use the ‘master’ database and run this query, it will kill all the active connections from your database.