Pages

Wednesday, November 10, 2010

SQL Server Agent Not getting started after a Restart of Services Post Fresh Installation:

There are times where the SQL Server and SQL Server Agent Services are running fine post installation but your SQL Server Agent might not start after a restart of your SQL Server and SQL Server Agent services post installation. There may be many causes out of which the one which I am discussing is one.

Root Cause for the Problem:

We have a tendency to change the SQLAgent.Out file default location to some specific location as per out convenience. We may use EXEC master..xp_instance_regwrite to do the same.

Unfortunately in SQL Server 2008 no doubt this will be written in Registry but will not be updated in SQL Server Agent Properties.

Resolution:

This can be resolved by either changing the ErrorLogFile path in Registry to the one which we have configured in SQL Server Agent properties.

Ideally you can find this ErrorLogFile registry key in path

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL10.MSSQLSERVER\SQLServerAgent\ErrorLogFile.

Hope this might be of some help

Tuesday, November 9, 2010

Unable to Shrink/FreeUp TempDB Space

Sometimes we come across situation where in the Entire Space in TempDB will be unallocated space but we are unable to free up space neither we are able to shrink the database or file. This was how I was able to Free Up the unallocated space and it worked for me. So thought of sharing it.

USE TempDB

GO

DBCC FREEPROCCACHE
GO
DBCC DROPCLEANBUFFERS
go
DBCC FREESYSTEMCACHE ('ALL')
GO
DBCC FREESESSIONCACHE
GO
dbcc shrinkfile (<TempDBLogicalFileName>,SizeInMB)
GO

Note: Logical File name can be obtained using SP_HELPFILE

Tuesday, November 2, 2010

Updating Email ID for Alerts and Mail Items

Recently I came across a situation where in I had to change the Email ID for all alerts and Mail Items on multiple servers. If you want to accomplish this through GUI it would be quite painful. After digging out I found out an easy way.

1. You can accomplish this by running the below set of query on individual servers.

2. You can accomplish this by running the below set of query on Central Management Server.

For how to configure Central Management Server(CMS) I will add a link soon or may in next post.

Query:

update msdb..sysoperators set email_address = 'YourEmailID@YourDomain' where name='YourAlertName'

GO

update a SET a.recipients='YourEmailID@YourDomain' 

from msdb..sysmail_mailitems a

inner join msdb..sysmail_profile b

on a.profile_id = b.profile_id

where b.description=’YourDescriptionName’

Enabling CDC while Restoring Database.

Restore the Source Database to Destination Database with parameter KEEP_CDC.

Only “KEEP_CDC” needs to be included in “WITH” Option while restoring database.

Ex: WITH KEEP_CDC

The above process will only work if the Source Database is CDC enabled and CDC jobs are already existing on Destination Server.

If the CDC Jobs for destination database are not exiting then two jobs <Databasename>_Capture and <Databasename>_Change needs to be created manually and needs to be added to msdb cdc jobs using below mentioned procedure.

use msdb

GO

exec sys.sp_cdc_add_job 'change'

GO

exec sys.sp_cdc_add_job 'cleanup'

GO

Once the above query is executed you will get message as jobs started successfully. After this the changes made to CDC Enabled table will be captured in CDC table.

If the CDC Jobs are already existing for destination database then restoring database with KEEP_CDC option is enough after which it will capture all the changed made to CDC Enabled tables to CDC tables as the Jobs are already existing and running

Technorati Tags: ,,

Tuesday, October 26, 2010

Find Index’s Table Name

Hello!!..

At times i came across situations when i need to search for particular index name and on which table it is and for which database. To help me out i came across with this small piece of code yet very useful. So posting it out thought might help out.

Query:

EXEC sp_MSforeachdb 'USE ?
select ''?'' as DatabaseName, st.name as TableName,si.name as IndexName from sys.indexes si
inner join sys.tables st
on
si.object_id = st.object_id
where si.name like ''%INDEXNAME%'''

Saturday, October 16, 2010

Change Data Capture Known as CDC

Change Data Capture popularly known as CDC is a new feature in SQL Server 2008 that records insert, update and delete activity in SQL Server tables.  A good example of how this feature can be used is in performing periodic updates to a data warehouse.  CDC records (or captures) DML activity on designated tables. CDC works by scanning the transaction log for a designated table's 'captured columns' whose content has changed and then making those changes available for data synchronizing purposes in a relational format. As you can see this feature in entrenched in transaction log architecture and thus a lot of the metadata in CDC is related around the concept of a Log Sequence Number (LSN). After performing some setup and configuration steps (which we will cover below), CDC will begin scanning the database transaction log for changes to certain tables that you specify, and will insert these changes into change tables.  These change tables are created during the setup and configuration process.  The setup and configuration process will also create table-valued functions which can be used to query for the changes.  You use the table-valued functions in lieu of querying the underlying change tables directly.  Based on this high level description, let's proceed to the demo.

Use the below query to check which databases is enabled for CDC.

select name,is_cdc_enabled from sys.databases where is_cdc_enabled = 1

Enabling CDC for database.

use <DatabaseName>

GO

Exec sys.sp_cdc_enable_db

GO

Replace <DatabaseName> with the database name on which the you want to enable CDC.

The above query enables CDC on database level.

Note: Just enabling CDC on database level does not capture any data change to any table.

Enabling CDC for Database Table.

These are parameter which we need to set for enabling CDC for new investran database tables for our environment.

exec sys.sp_cdc_enable_table

@source_schema = '<schemaname>',

@source_name = '<TableName>',

@role_name = '<CDCRoleName>',

@supports_net_changes = 1 or 0,

@captured_column_list = '[Column1],[ Column1],...,[ ColumnN]',

@filegroup_name = '<FileGroupName>';

Replace <schemaname>,<TableName>,<CDCRoleName>,[Column1],[ Column1],...,[ ColumnN] and <FileGroupName> as per your database details where

@source_schema is the name of the schema in which the source table belongs.

@source_name is the name of the source table on which to enable change data capture

@role_name is the name of the database role used to gate access to change data

@supports_net_changes indicates whether support for querying for net changes is to be enabled for this capture instance

@captured_column_list identifies the source table columns that are to be included in the change table

@filegroup_name is the filegroup to be used for the change table created for the capture instance

After running the above query two jobs <Databasename>_Capture and <Databasename>_Change get created. Once these jobs are running all the changes made to CDC enabled table gets captured in CDC tables.

Will cover more in next articles: Follow below link for Enabling CDC while restoring a Database

Enabling CDC while Restoring database

READING WINDOWS EVENTS AND INSERTING IN SQL SERVER

As a DBA its not only we need to check for SQL Server Error Logs but often we also need to check for Windows Event Log for any Critical Error or Warning as well. Incase a daily check list needs to be followed at that time reading events if in thousands and filtering is a big pain. Here is where WMI and Script comes handy.

So i have written below VBScript which reads Windows Application and System events for Errors and Warnings for 1 day back till date and inserts into a table into SQL Server. This can later be used for querying and multiple purposes etc. This can we turned to Weekly as well by Selecting number of days back you want the logs to to be Read.

This can further be used in SSIS Which i will be posting shortly.

Sub Main()
Const CONVERT_TO_LOCAL_TIME = False
Set dtmStartDate = CreateObject("WbemScripting.SWbemDateTime")
Set dtmEndDate = CreateObject("WbemScripting.SWbemDateTime")
DateToCheck = CDate(Date)
'SET Number of Days back u want the logs to be read
dtmStartDate.SetVarDate DateToCheck - 1, CONVERT_TO_LOCAL_TIME
dtmEndDate.SetVarDate DateToCheck, CONVERT_TO_LOCAL_TIME
dim sServer, sConn, oConn,oRS
'Connection to SQL Server
'Provide Server Name instead of "." if any other machine
'Replace <DBServerName>,<USERID>,<PASSWORD>,<DATABASENAME> with your desired details
sServer="."
sConn= _
    "Provider=SQLOLEDB;Data Source=<DBServerName>;" & _
        "Trusted_Connection=Yes;Initial Catalog=<DATABASENAME>;" & _
             "User ID=<USERID>;Password=<PASSWORD>;"

Set oConn = CreateObject("ADODB.Connection")
oConn.Open sConn
Set oRS =CreateObject("ADODB.Recordset")
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & "." & "\root\cimv2")
'USE THIS FOR READING SYSTEM EVENTS AND TYPE ERROR
                                             Set colLoggedEvents = objWMIService.ExecQuery("Select * from Win32_NTLogEvent Where TimeGenerated >= '" & dtmStartDate &

"'  and Logfile = 'System' and Type = 'Error'")
For Each objEvent in colLoggedEvents
strLogFile = objEvent.LogFile
strSourceName = objEvent.SourceName
strType = objEvent.Type
strEventCode = objEvent.EventCode
strMessage = objEvent.Message
strMessage = Replace(strMessage,"'","")
strDate = WMIDateStringToDate(objEvent.TimeGenerated)   
'REPLACE <TABLENAME> with your table Name
sSQL = "Insert into <TABLENAME> (LogFile,"_
&"EventSource,"_
&"EventType,"_
&"EventID,"_
&"EventMessage,"_
&"TimeGenerated)"_
&"Values"_
&"('" &strLogFile& "',"_
&"'" &strSourceName& "',"_
&"'" &strType& "',"_
&"'" &strEventCode& "',"_
&"'" &strMessage& "',"_
&"'" &strDate& "')"
ors.open sSQL, oconn
'ors.close
Next

'USE THIS FOR READING SYSTEM EVENTS AND TYPE WARNING
Set colLoggedEvents = objWMIService.ExecQuery("Select * from Win32_NTLogEvent Where TimeGenerated >= '" & dtmStartDate & "' and Logfile = 'System' and  Type =

'Warning'")
For Each objEvent in colLoggedEvents
strLogFile = objEvent.LogFile
strSourceName = objEvent.SourceName
strType = objEvent.Type
strEventCode = objEvent.EventCode
strMessage = objEvent.Message
strMessage = Replace(strMessage,"'","")
strDate = WMIDateStringToDate(objEvent.TimeGenerated)
'REPLACE <TABLENAME> with your table Name   
sSQL = "Insert into <TABLENAME>(LogFile,"_
&"EventSource,"_
&"EventType,"_
&"EventID,"_
&"EventMessage,"_
&"TimeGenerated)"_
&"Values"_
&"('" &strLogFile& "',"_
&"'" &strSourceName& "',"_
&"'" &strType& "',"_
&"'" &strEventCode& "',"_
&"'" &strMessage& "',"_
&"'" &strDate& "')"
ors.open sSQL, oconn
'ors.close
Next

'USE THIS FOR READING APPLICATION EVENTS AND TYPE ERROR
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & "." & "\root\cimv2")
                                             Set colLoggedEvents = objWMIService.ExecQuery("Select * from Win32_NTLogEvent Where TimeGenerated >= '" & dtmStartDate &

"'  and Logfile = 'Application' and Type = 'Error'")
For Each objEvent in colLoggedEvents
strLogFile = objEvent.LogFile
strSourceName = objEvent.SourceName
strType = objEvent.Type
strEventCode = objEvent.EventCode
strMessage = objEvent.Message
strMessage = Replace(strMessage,"'","")
strDate = WMIDateStringToDate(objEvent.TimeGenerated)
'REPLACE <TABLENAME> with your table Name   
sSQL = "Insert into <TABLENAME>(LogFile,"_
&"EventSource,"_
&"EventType,"_
&"EventID,"_
&"EventMessage,"_
&"TimeGenerated)"_
&"Values"_
&"('" &strLogFile& "',"_
&"'" &strSourceName& "',"_
&"'" &strType& "',"_
&"'" &strEventCode& "',"_
&"'" &strMessage& "',"_
&"'" &strDate& "')"
ors.open sSQL, oconn
'ors.close
Next

'USE THIS FOR READING APPLICATION EVENTS AND TYPE WARNING
Set colLoggedEvents = objWMIService.ExecQuery("Select * from Win32_NTLogEvent Where TimeGenerated >= '" & dtmStartDate & "' and Logfile = 'Application' and  Type =

'Warning'")
For Each objEvent in colLoggedEvents
strLogFile = objEvent.LogFile
strSourceName = objEvent.SourceName
strType = objEvent.Type
strEventCode = objEvent.EventCode
strMessage = objEvent.Message
strMessage = Replace(strMessage,"'","")
strDate = WMIDateStringToDate(objEvent.TimeGenerated)
'REPLACE <TABLENAME> with your table Name   
sSQL = "Insert into <TABLENAME>(LogFile,"_
&"EventSource,"_
&"EventType,"_
&"EventID,"_
&"EventMessage,"_
&"TimeGenerated)"_
&"Values"_
&"('" &strLogFile& "',"_
&"'" &strSourceName& "',"_
&"'" &strType& "',"_
&"'" &strEventCode& "',"_
&"'" &strMessage& "',"_
&"'" &strDate& "')"
ors.open sSQL, oconn
'ors.close
Next
'ors.close

End Sub
'FUNCTION FOR PARSING DATE
Function WMIDateStringToDate(dtmInstallDate)
    WMIDateStringToDate = CDate(Mid(dtmInstallDate, 5, 2) & "/" & Mid(dtmInstallDate, 7, 2) & "/" & Left(dtmInstallDate, 4) _
            & " " & Mid (dtmInstallDate, 9, 2) & ":" & Mid(dtmInstallDate, 11, 2) & ":" & Mid(dtmInstallDate, 13, 2))
End Function

Hope this helps you. Shortly will incorporate it with SSIS for Automation.

Thanks,

Manjunath C Bhat

Thursday, September 23, 2010

T-SQL to get all the Error Messages and Error Codes

Technorati Tags: ,,,

Sometimes We Tend to search for Error Codes and Error messages. We require this for trouble shooting.

Here is a Small T-SQL which will get you all the Error Codes and the Respective Error Messages. Hope this Might Help us.

SELECT
    message_id as Error_Code,
    severity,
    is_event_logged as Logged_Event,
    text as [Error_Message]
  FROM sys.messages
  WHERE language_id = 1033;

Thursday, July 29, 2010

T-SQL to Get Fixed Drives Free Space in Percentage - ms-sqlserver

use master
go
sp_configure 'Ole Automation Procedures', 1

reconfigure

go

SET NOCOUNT ON
DECLARE @hr int
DECLARE @fso int
DECLARE @drive char(1)
DECLARE @odrive int
DECLARE @TotalSize varchar(20) DECLARE @MB Numeric ; SET @MB = 1048576
CREATE TABLE #drives (drive char(1) PRIMARY KEY,
FreeSpace float NULL,
TotalSize float NULL)
INSERT #drives(drive,FreeSpace)
EXEC master.dbo.xp_fixeddrives
EXEC @hr=sp_OACreate 'Scripting.FileSystemObject',@fso OUT
IF @hr <> 0 EXEC sp_OAGetErrorInfo @fso
DECLARE dcur CURSOR LOCAL FAST_FORWARD
FOR SELECT drive from #drives ORDER by drive
OPEN dcur FETCH NEXT FROM dcur INTO @drive
WHILE @@FETCH_STATUS=0
BEGIN
EXEC @hr = sp_OAMethod @fso,'GetDrive', @odrive OUT, @drive
IF @hr <> 0 EXEC sp_OAGetErrorInfo @fso
EXEC @hr =sp_OAGetProperty @odrive,'TotalSize', @TotalSize OUT
IF @hr <> 0 EXEC sp_OAGetErrorInfo @odrive
UPDATE #drives SET TotalSize=@TotalSize/@MB
WHERE drive=@drive FETCH NEXT FROM dcur INTO @drive
End
Close dcur
DEALLOCATE dcur
EXEC @hr=sp_OADestroy @fso IF @hr <> 0 EXEC sp_OAGetErrorInfo @fso
SELECT drive+':', CAST(CAST((FreeSpace/(TotalSize*1.0))*100.0 as int) as VARCHAR(MAX))+'%' as 'Free(%)' FROM #drives
ORDER BY drive DROP TABLE #drives Return
go
sp_configure 'Ole Automation Procedures', 0
reconfigure
go

Tuesday, July 27, 2010

Stored Procedure To find all users and roles on a Single/Individual Database in SQL Server 2000

Using this Stored Procedure you can get all the users in an SQL Server Instance. This Has been modified for use with SQL Server 2000 Only.

USE [master]
GO
--CHECK IF THE STORED PROCEDURE ALREADY EXISTS
IF  EXISTS
      (SELECT * FROM sysobjects WHERE id = OBJECT_ID(N'[dbo].[spGetUsers]') AND type in (N'P', N'PC'))
      DROP PROCEDURE [dbo].[spGetUsers]
GO
--NAME OF THE DATABASE ON WHICH THE STORED PROCEDURE SHOULD BE CREATED.
--GOOD TO CREATE IT ON MASTER
USE [master]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--CREATION OF STORED PROCEDURE
CREATE PROCEDURE [dbo].[spGetUsers]
AS
SET NOCOUNT ON
--VARIABLES DECLARATION
DECLARE @Count INTEGER
DECLARE @varDBName VARCHAR(50)
DECLARE @varDBid INT
DECLARE @varSQL NVARCHAR(4000)
DECLARE @login varchar(50)
DECLARE @getlogin CURSOR
--TEMPORARY TABLE TO HOLD DATABASE AND USER-DEFIINE ROLE USER NAMES
CREATE TABLE #UserDetail
(
      varServerName VARCHAR(255) DEFAULT @@SERVERNAME
      ,varDBName VARCHAR(255)
      ,varDBid INT
      ,varRoleName VARCHAR(255)
      ,varUserName VARCHAR(255)
      ,varUserID VARBINARY(255)
      ,Active CHAR(3)
)
--TEMPORARY TABLE TO DISPLAY DATA
CREATE TABLE #UserDetailDisplay
(
      varServerName VARCHAR(255) DEFAULT @@SERVERNAME
      ,varDBName VARCHAR(255)
      ,varDBid INT
      ,varRoleName VARCHAR(255)
      ,varUserName VARCHAR(255)
      ,varUserID VARBINARY(255)
      ,Active CHAR(3)
)
--TEMPORARY TABLE TO HOLD DATABASE NAMES
CREATE TABLE #DBNameDet
(
      CountID INTEGER IDENTITY(1,1)
      ,varDBName VARCHAR(50)
      ,varDBid INT
)
--TEMPORARY TABLE TO HOLD SP_WHO2 RESULTS
CREATE TABLE #spwho2
(
      spid INT
      ,status CHAR(50)
      ,login VARCHAR(255)
      ,hostname VARCHAR(50)
      ,blkby VARCHAR(50)
      ,dbname VARCHAR(255)
      ,command CHAR(200)
      ,cputime INT
      ,diskio INT
      ,lastbatch VARCHAR(255)
      ,programname VARCHAR(255)
      ,spid1 INT
      --,requestid INT
)
--OBTAIN MEMBERS OF EACH SERVER ROLE
INSERT INTO #UserDetail (varRoleName, varUserName, varUserID)
EXEC dbo.sp_helpsrvrolemember
--OBTAIN DATABASE NAMES
INSERT INTO #DBNameDet (varDBName,varDBid)
SELECT name,dbid FROM MASTER.DBO.SYSDATABASES
--IF you want to exclude any database include where else comment the same.
where name not in ('','','')
      SET @Count = @@ROWCOUNT
--LOOP THROUGH DATABASES TO OBTAIN MEMBERS  OF DATABASE ROLES AND USER-DEFINED ROLES
WHILE @Count > 0
BEGIN
      --GET DATABASE NAME FROM TEMPORARY TABLE
      SET @varDBName = (SELECT varDBName FROM #DBNameDet WHERE CountID = @Count)
      SET @varDBid = (SELECT varDBid FROM #DBNameDet WHERE CountID = @Count)
      --OBTAIN MEMBERS OF EACH DATABASE AND USER-DEFINED ROLE
      SET @varSQL = 'INSERT INTO #UserDetail (varRoleName, varUserName, varUserID)
      EXEC ' + @varDBName + '.dbo.sp_helprolemember'
      EXEC sp_executesql @varSQL
      --UPDATE DATABASE NAME IN TEMPORARY TABLE
      UPDATE #UserDetail
      SET varDBName = @varDBName
      ,varDBid = @varDBid
      WHERE varDBName IS NULL
      SET @Count = @Count - 1
END
--TEMPORARY TABLE TO GET VALUES FROM SP_WHO2
INSERT INTO #spwho2
(
      SPID
      ,Status,Login
      ,HostName
      ,BlkBy
      ,DBName
      ,Command
      ,CPUTime
      ,DiskIO
      ,LastBatch
      ,ProgramName
      ,SPID1
     -- ,REQUESTID
)
EXEC dbo.sp_who2
--CURSUR TO FETCH OPEN USER WHO ARE CURRENTLY CONNECTED TO DATABASES
SET @getlogin = CURSOR FOR
SELECT a.login FROM
#spwho2 a
INNER JOIN #UserDetail t
      ON a.DBname = t.varDBName and a.login = t.varUserName
OPEN @getlogin
FETCH NEXT
FROM @getlogin INTO @login
WHILE @@FETCH_STATUS = 0
BEGIN
      UPDATE #UserDetail SET Active = 'YES' where varUserName in(SELECT a.login FROM
      #spwho2 a
      INNER JOIN #UserDetail t
      ON a.DBname = t.varDBName and a.login = t.varUserName)
      FETCH NEXT
      FROM @getlogin INTO @login
END
CLOSE @getlogin
DEALLOCATE @getlogin
UPDATE #UserDetail set active = 'NO' where active IS NULL
--INSERT DATA INTO TEMPORARY TABLE TO DISPLAY
INSERT INTO #UserDetailDisplay
SELECT tgus.* FROM #UserDetail tgus
LEFT JOIN #UserDetailDisplay tdus
ON tgus.varUserName = tdus.varUserName
AND tgus.varDBName = tdus.varDBName
AND tgus.varRoleName = tdus.varRoleName
AND tgus.varServerName = tdus.varServerName
WHERE tdus.varServerName IS NULL
--SELECT THE RECORDS BY USER--YOU CAN CHANGE AS PER YOUR CONVINIENCE
PRINT 'Display by User'
SELECT varUserName as UserName, varDBName as DBName, varRoleName as RoleName, varServerName as ServerName,Active FROM #UserDetailDisplay
WHERE varUserName<>'dbo'
ORDER BY varUserName
--DROPPING THE TEMPORARY TABLE CREATED
DROP TABLE #UserDetailDisplay
DROP TABLE #UserDetail
DROP TABLE #spwho2
DROP TABLE #DBNameDet
GO

Authenticate Issue in SQL Server due to SID

Found this to be useful so sharing the same.

User is unable to access the database even though his account is existing in the Group and having relevant access.

Cause:

Before Moving to one of the cause let me take you through how authenticate process is done for authenticating to SQL Server using a Domain Account

Process of SQL Authentication:

Whenever a Domain User is logged on to a Machine his SID is being cached in the Machine. When this User Authenticates with SQL Server the below mentioned process is carried out for Authentication.
  1. Domain User ID and Password along with SID for Domain Account are passed in.
  2. The User ID and Passwords along with SID received are Authenticated with the User ID and Password along with SID which is existing on SQL Server.
  3. When all there are verified successfully the respective existing access is granted to that particular user.
Why the problem is occurred:

Basically the groups in Windows can be classified as Domain Global and Domain Local.
The existing Domain Groups may on Domain either of the Hierarchy i.e. Domain Global and Domain Local.  These Domain Group if dropped and Same Domain groups were created at Domain Local / Global Hierarchy then u will come across the issue for sure if this activity is done only at windows level but not at SQL Server.
Now here is where the tricky Part Lies.
Whenever a Domain Account or Group is dropped and Re-Created a New SID is assigned to that Login.
So when this New Login with same name is trying to authenticate with SQL Server the Authentication fails because only the User ID and Password match with that of existing in SQL Server but not the SID because At windows level it is having the new SID and at SQL Server the Old SID which one is Prior to the New Windows login is created.
To resolve the Issue the existing Group/Logins on SQL Server Needs to be dropped and Re-Created so that the New SID gets Synced and Updated and the Authentication can be done successfully.
You can get the Invalid Logins using Stored Procedure sp_validatelogins. But again this is not trust worthy because if an it checks only for SID Matching. Suppose if a Login is dropped and created again this will not show correct results as it compares only SID.

Wednesday, July 14, 2010

Stored Procedure To find all users and roles on a Single/Individual Database

-- ======================================================================================
-- Author Name: Manjunath C Bhat
-- Author EMail ID: manjunathcbhat@gmail.com
-- Author Designation: Senior Database Engineer
-- Stored Procedure Purpose: To find all users and roles on a Single/Individual Database
-- Stored Procedure Name: spGetDBUsers
-- Parameter1: @DatabaseName
-- ======================================================================================
-- Stored Procedure To find all users and roles on a Single/Individual Database
-- ======================================================================================

USE [YOURDATABASENAME]
GO
--CHECK IF THE STORED PROCEDURE ALREADY EXISTS
IF  EXISTS
      (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[spGetDBUsers]') AND type in (N'P', N'PC'))
      DROP PROCEDURE [dbo].[spGetDBUsers]
GO
--NAME OF THE DATABASE ON WHICH THE STORED PROCEDURE SHOULD BE CREATED.
--GOOD TO CREATE IT ON MASTER
USE [YOURDATABASENAME]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--CREATION OF STORED PROCEDURE
CREATE PROCEDURE [dbo].[spGetDBUsers]
@DatabaseName VARCHAR(255)
AS
SET NOCOUNT ON
--VARIABLES DECLARATION
DECLARE @Count INTEGER
DECLARE @varDBName VARCHAR(50)
DECLARE @varDBid INT
DECLARE @varSQL NVARCHAR(4000)
DECLARE @login varchar(50)
DECLARE @getlogin CURSOR
--TEMPORARY TABLE TO HOLD DATABASE AND USER-DEFIINE ROLE USER NAMES
CREATE TABLE #UserDetail
(
      varServerName VARCHAR(255) DEFAULT @@SERVERNAME
      ,varDBName VARCHAR(255)
      ,varDBid INT
      ,varRoleName VARCHAR(255)
      ,varUserName VARCHAR(255)
      ,varUserID VARBINARY(MAX)
      ,Active CHAR(3)
)
--TEMPORARY TABLE TO DISPLAY DATA
CREATE TABLE #UserDetailDisplay
(
      varServerName VARCHAR(255) DEFAULT @@SERVERNAME
      ,varDBName VARCHAR(255)
      ,varDBid INT
      ,varRoleName VARCHAR(255)
      ,varUserName VARCHAR(255)
      ,varUserID VARBINARY(MAX)
      ,Active CHAR(3)
)
--TEMPORARY TABLE TO HOLD DATABASE NAMES
CREATE TABLE #DBNameDet
(
      CountID INTEGER IDENTITY(1,1)
      ,varDBName VARCHAR(50)
      ,varDBid INT
)
--TEMPORARY TABLE TO HOLD SP_WHO2 RESULTS
CREATE TABLE #spwho2
(
      spid INT
      ,status CHAR(50)
      ,login VARCHAR(255)
      ,hostname VARCHAR(50)
      ,blkby VARCHAR(50)
      ,dbname VARCHAR(255)
      ,command CHAR(200)
      ,cputime INT
      ,diskio INT
      ,lastbatch VARCHAR(255)
      ,programname VARCHAR(255)
      ,spid1 INT
      ,requestid INT
)
--OBTAIN MEMBERS OF EACH SERVER ROLE
INSERT INTO #UserDetail (varRoleName, varUserName, varUserID)
EXEC dbo.sp_helpsrvrolemember
--OBTAIN DATABASE NAMES
INSERT INTO #DBNameDet (varDBName,varDBid)
SELECT name,dbid FROM MASTER.DBO.SYSDATABASES WHERE name = @DatabaseName 
--LOOP THROUGH DATABASES TO OBTAIN MEMBERS  OF DATABASE ROLES AND USER-DEFINED ROLES
BEGIN
      --GET DATABASE NAME FROM TEMPORARY TABLE
      SET @varDBName = (SELECT varDBName FROM #DBNameDet)
      --OBTAIN MEMBERS OF EACH DATABASE AND USER-DEFINED ROLE
      SET @varSQL = 'INSERT INTO #UserDetail (varRoleName, varUserName, varUserID)
      EXEC ' + @varDBName + '.dbo.sp_helprolemember'
      EXEC sp_executesql @varSQL
      --UPDATE DATABASE NAME IN TEMPORARY TABLE
      UPDATE #UserDetail
      SET varDBName = @varDBName
      ,varDBid = @varDBid
      WHERE varDBName IS NULL
END
--TEMPORARY TABLE TO GET VALUES FROM SP_WHO2
INSERT INTO #spwho2
(
      SPID
      ,Status,Login
      ,HostName
      ,BlkBy
      ,DBName
      ,Command
      ,CPUTime
      ,DiskIO
      ,LastBatch
      ,ProgramName
      ,SPID1
      ,REQUESTID
)
EXEC dbo.sp_who2
--CURSUR TO FETCH OPEN USER WHO ARE CURRENTLY CONNECTED TO DATABASES
SET @getlogin = CURSOR FOR
SELECT a.login FROM
#spwho2 a
INNER JOIN #UserDetail t
      ON a.DBname = t.varDBName and a.login = t.varUserName
OPEN @getlogin
FETCH NEXT
FROM @getlogin INTO @login
WHILE @@FETCH_STATUS = 0
BEGIN
      UPDATE #UserDetail SET Active = 'YES' where varUserName in(SELECT a.login FROM
      #spwho2 a
      INNER JOIN #UserDetail t
      ON a.DBname = t.varDBName and a.login = t.varUserName)
      FETCH NEXT
      FROM @getlogin INTO @login
END
CLOSE @getlogin
DEALLOCATE @getlogin
UPDATE #UserDetail set active = 'NO' where active IS NULL
--INSERT DATA INTO TEMPORARY TABLE TO DISPLAY
INSERT INTO #UserDetailDisplay
SELECT tgus.* FROM #UserDetail tgus
LEFT JOIN #UserDetailDisplay tdus
ON tgus.varUserName = tdus.varUserName
AND tgus.varDBName = tdus.varDBName
AND tgus.varRoleName = tdus.varRoleName
AND tgus.varServerName = tdus.varServerName
WHERE tdus.varServerName IS NULL
--SELECT THE RECORDS BY USER--YOU CAN CHANGE AS PER YOUR CONVINIENCE
PRINT 'Display by User'
SELECT varUserName as UserName, varDBName as DBName, varRoleName as RoleName, varServerName as ServerName,Active FROM #UserDetailDisplay
WHERE varUserName <> 'dbo'
ORDER BY varUserName
--DROPPING THE TEMPORARY TABLE CREATED
DROP TABLE #UserDetailDisplay
DROP TABLE #UserDetail
DROP TABLE #spwho2
DROP TABLE #DBNameDet
GO