About Me

Colorado
Paul has 18 years experience with Microsoft SQL Server. He has worked in the roles of production DBA, database developer, database architect, applications developer, business intelligence and data warehouse developer, and instructor for students aspiring for MCDBA certification. He has performed numerous data migrations and supported large databases (3 Terabyte, 1+ billion rows) with high transactions. He is a member of PASS, blogs about lessons learned from a developer’s approach to SQL Server administration, and has been the president of the Boulder SQL Server Users’ Group for 11 years, from January 2009 to 2020.

Monday, October 15, 2012

vwWho3 - a filtered vwWho2


In the last post we created a view that is the contents of sp_who2, plus some other valuable information.

Here we will create vwWho3, which is the same as vwWho2, with a filter to see only active or pertinent spids.


USE Admin
GO

IF object_id('dbo.vwWho3') Is Not Null
      DROP VIEW dbo.vwWho3

go

CREATE VIEW [dbo].[vwWho3]
AS
/*    DATE            AUTHOR          REMARKS
      10/15/12          PPaiva          Initial creation.
     
      DESCRIPTION
            Same as vwWho2 except this only gets active processes, plus
                  one row from spids between 1 and 50, so that it is easy
                  to show the date/time this instance of SQL Server was
                  last started.

      NOTE
            If spid = -2, it is an orphaned distributed transaction. 
                  This can be terminated with
                        KILL 'Unit Of Work ID (this is a guid)'
     
      USAGE
            SELECT *
            FROM vwWho3

*/

SELECT *
FROM vwWho2
WHERE Status Not In ('sleeping')
   OR Blocked <> 0
   OR Open_Tran <> 0
   OR spid < 1



Tuesday, October 2, 2012

vwWho2 - a better sp_who2

sp_who and sp_who2 are wonderful system procs that give you immediate information on all spids, (active or sleeping or any status), that are currently connected  to your SQL Server instance.

However, you cannot intrinsically JOIN these results to any other queries you may wish to write, perhaps for filtering purposes.   

SOLUTION:  Let’s make a view with the contents of sp_who2, then add some more useful information such as the SQL text for a given spid.

Find sp_who2 in the master database, and you will see it is mostly this query:

SELECT *
FROM sys.sysprocesses

In sys.sysprocesses, spids 1 thru 50 can be ignored because they are used by the system.  In our view, we will retain just one row from these 50, to make it easy to show the date/time that this instance of SQL Server was started.  Columns last_batch and login_time will contain that information.

USE Admin
GO
IF OBJECT_ID('dbo.vwWho2') Is Not Null
      DROP VIEW dbo.vwWho2
GO


CREATE  VIEW [dbo].[vwWho2]
AS
/*      DATE            AUTHOR          REMARKS
            10/1/12                 PPaiva          Initial creation.

            DESCRIPTION
                  Pertinent info from sysprocesses.  This is an alternative
                        to using sp_who2 because results from sp_who2 cannot be
                        intrinsically joined nor queried.
                  Note that the first row in the output is given just to get the
                        start time of the SQL Server service (login_time).

                  NOTE:   Executing this Trace Flag will force SQL Server to record the
                              sql_handle for even trivial queries.
                                    DBCC TRACEON (2861)
                              However, beware as this has been known to sometimes cause
                                    performance issues.

            USAGE
                  SELECT *
                  FROM vwWho2
                  WHERE WaitTimeMin > 0

                  -- By User
                  SELECT *
                  FROM vwWho2
                  ORDER BY LogiName

                  -- By User, summary
                  SELECT LogiName, Count(*) Qty
                  FROM vwWho2
                  GROUP BY LogiName
                  ORDER BY LogiName


                  NOTE:  For versions of SQL Server that do not support the
                              view sys.dm_exec_sql_text, you can use this system
                              function to get the SQL text from the SQL Handle.
                                    SELECT *
                                    FROM :: fn_get_sql(0x010006007756E52D60372D320000000000000000)

            DEBUG
                  SELECT *
                  FROM sys.sysprocesses

*/

SELECT  TOP 1
                  spid,
                  RTrim(LogiName) LogiName,
                  RTrim(db_name(dbid)) DB,
                  RTrim(Program_Name) Program,
                  Null SqlText,
                  Convert(decimal(7,1), WaitTime/60000.0) WaitTimeMin,
                  Convert(decimal(7,1), WaitTime/60000.0/60) WaitTimeHr,
                  LastWaitType,
                  blocked,
                  Status,
                  RTrim(HostName) HostName,
                  dbo.udfPadLeft(Convert(varchar, DateDiff(MINUTE, last_batch, GetDate())),
                                                2, '        ') MinsFromLastBatch,
                  last_batch,
                  login_time,
                  cpu,
                  physical_io PhysicalIO,
                  MemUsage,
                  WaitTime WaitTimeMs,
                  open_tran,
                  cmd,
                  RTrim(nt_UserName) NTUser,
                  net_address,
                  net_library,
                  sql_handle
FROM master.dbo.sysprocesses P
WHERE spid <= 50

UNION ALL

SELECT  TOP 100 PERCENT
                  spid,
                  RTrim(LogiName) LogiName,
                  RTrim(db_name(p.dbid)) DB,
                  RTrim(Program_Name) Program,
                  sh.Text,
                  Convert(decimal(7,1), WaitTime/60000.0) WaitTimeMin,
                  Convert(decimal(7,1), WaitTime/60000.0/60) WaitTimeHr,
                  LastWaitType,
                  blocked,
                  Status,
                  RTrim(HostName) HostName,
                  dbo.udfPadLeft(Convert(varchar, DateDiff(MINUTE, last_batch, GetDate())),
                                                2, '        ') MinsFromLastBatch,
                  last_batch,
                  login_time,
                  cpu,
                  physical_io PhysicalIO,
                  MemUsage,
                  WaitTime WaitTimeMs,
                  open_tran,
                  cmd,
                  RTrim(nt_UserName) NTUser,
                  net_address,
                  net_library,
                  sql_handle
FROM master.dbo.sysprocesses p
OUTER APPLY sys.dm_exec_sql_text(sql_handle) sh
WHERE spid > 50
   OR Blocked <> 0

Monday, September 17, 2012

Grant Permissions for Viewing Object Definitions

Here's a way to grant permissions to view object definitions.  Very handy when you don't want someone to have full sysadmin permissions.


--------------------------------------------------
-- View Definition Permissions
-- Group in domain is called Admin_ViewDefinition
--          or, specify a user individually
--------------------------------------------------
Use Admin

Exec sp_addrole 'db_ViewDefinition'
Exec sp_addrolemember @rolename = 'db_ViewDefinition',@membername = 'MyDomainName\Admin_ViewDefinition'
GRANT VIEW Definition TO db_ViewDefinition


Monday, June 18, 2012

Job_EnableJobsThatWereEnabled

This will be referred to in the next post.



USE Admin
GO

IF OBJECT_ID('dbo.Job_EnableJobsThatWereEnabled') Is Not Null
      DROP PROC dbo.Job_EnableJobsThatWereEnabled
GO

CREATE   PROC dbo.Job_EnableJobsThatWereEnabled
      @Debug bit = 0
AS
/*    DATE        AUTHOR            REMARKS    
      6/19/12           Paiva       Initial creation.

      DESCRIPTION
            Enables the jobs that were previously disabled
                  via Job_DisableJobsThatAreEnabled.

      USAGE
            Job_EnableJobsThatWereEnabled 1
            Job_EnableJobsThatWereEnabled

      DEBUG
            SELECT Enabled, *
            FROM msdb.dbo.JobsBUPreviouslyEnabled

*/
SET NOCOUNT ON

DECLARE @LastBUDate datetime,
            @NumEnabled smallint,
            @NumDisabled smallint,
            @Sql varchar(300)

IF OBJECT_ID('msdb.dbo.JobsBUPreviouslyEnabled') Is Not Null                   
      BEGIN
            -- Get stats of last backkup

            SELECT @LastBUDate = Max(InsertDate),
                        @NumEnabled = Count(*)
            FROM msdb.dbo.JobsBUPreviouslyEnabled
            WHERE Enabled = 1

            SELECT @LastBUDate = Max(InsertDate),
                        @NumDisabled = Count(*)
            FROM msdb.dbo.JobsBUPreviouslyEnabled
            WHERE Enabled = 0

      END

ELSE
      BEGIN
            SET @Sql = 'Cannot enable jobs that were previously disabled because a backup was not made.'
            SET @Sql = @Sql + Char(13) + Char(10) + 'To make backup and disable jobs use < Job_DisableJobsThatAreEnabled >.'
            RaisError (@Sql, 16, 1)
            RETURN
      END


IF @Debug = 1
      BEGIN
            SET @Sql = '    Date of last backup:  ' + Convert(varchar, @LastBUDate, 120)
            SET @Sql = @Sql + Char(13) + Char(10) + ' Num Previously Enabled:  ' + Convert(varchar, @NumEnabled)
            SET @Sql = @Sql + Char(13) + Char(10) + 'Num Previously Disabled:  ' + Convert(varchar, @NumDisabled)
            Print @Sql
            RETURN
      END



-- Re-Enable the previously enabled jobs
DECLARE @JobID varchar(38),
            @MaxJobID varchar(38),
            @JobName nvarchar(256),
            @Num smallint

SELECT  @MaxJobID = Max(Convert(varchar(38), Job_ID)),
            @Num = Count(*),
            @JobID = ''
FROM msdb.dbo.JobsBUPreviouslyEnabled
WHERE Enabled = 1

BEGIN TRANSACTION

WHILE @JobID < @MaxJobID
      BEGIN
            -- Get next JobID
            SELECT @JobID = Min(Convert(varchar(38), Job_ID))
            FROM msdb.dbo.JobsBUPreviouslyEnabled
            WHERE Enabled = 1
              AND Convert(varchar(38), Job_ID) > @JobID

            -- Get other attributes
            SELECT @JobName = Name
            FROM msdb.dbo.JobsBUPreviouslyEnabled
            WHERE Convert(varchar(38), Job_ID) = @JobID


            SET @Sql = 'Exec msdb.dbo.sp_update_job @Job_ID = ''' + @JobID + ''', @Enabled = 1'
            --Print @Sql     
            Exec (@Sql)
      END

      Exec dbo.DropObjectIfExists 'u', 'msdb', 'dbo', 'JobsBUPreviouslyEnabled'


COMMIT




Print 'Number of jobs that were enabled:  ' + Convert(varchar, @Num)

SELECT Name CurrentlyEnabledJobName, Job_ID JobID, Enabled
FROM msdb.dbo.sysjobs
WHERE Enabled = 1
ORDER BY Name

SELECT Name CurrentlyDisabledJobName, Job_ID JobID, Enabled
FROM msdb.dbo.sysjobs
WHERE Enabled = 0
ORDER BY Name








Sunday, June 17, 2012

Job_DisableJobsThatAreEnabled

This will be referred to in a future post.



USE Admin
GO

IF OBJECT_ID('dbo.Job_DisableJobsThatAreEnabled') Is Not Null
      DROP PROC Job_DisableJobsThatAreEnabled
GO

 CREATE PROC dbo.Job_DisableJobsThatAreEnabled
      @Debug bit = 0
AS
/*    DATE        AUTHOR            REMARKS    
      6/19/12           PPaiva            Initial creation.


      DESCRIPTION
            1.  Checks for a previous backup of table JobsBUPreviouslyEnabled.
            2.  Backs up sysjobs in msdb to JobsBUPreviouslyEnabled in msdb.
            3.  Disables all jobs that were enabled.
            4.  Calls out sp_update job for each job, so that the Agent
                  cache is refreshed.

      NOTES
            A.  This is useful if you want to shut down SQL Agent but don't
                  want to do it while any jobs are executing, and if you don't
                  want to manually disable all the jobs.
            B.  Run this proc, then wait for jobs to finish executing, then
                  shut down SQL Agent.
            C.  Do whatever task you have in mind. 
            D.  When SQL Agent is running again and you are ready to return
                  the jobs to their original state, run <Job_EnableJobsThatWereEnabled>.
           
      USAGE
            -- To disable jobs, show SQL only
            Exec Job_DisableJobsThatAreEnabled 1

            -- To disable jobs, Execute
            Exec Job_DisableJobsThatAreEnabled

            -- After running the above, verify that all jobs are disabled
            SELECT *
            FROM vwJob


            -- When ready to re-enable the jobs, run
            Exec Job_EnableJobsThatWereEnabled 1      -- Show stats only
            Exec Job_EnableJobsThatWereEnabled  -- Execute

      DEBUG
            Exec dbo.DropObjectIfExists 'u', 'msdb', 'dbo', 'JobsBUPreviouslyEnabled'
           
            SELECT *
            FROM msdb.dbo.JobsBUPreviouslyEnabled
            WHERE Enabled = 1

            SELECT *
            FROM msdb.dbo.sysjobs
            WHERE Enabled = 1

            SELECT *
            FROM msdb..JobsBUPreviouslyEnabled

            Exec Admin.dbo.JobStatus

*/
SET NOCOUNT ON

DECLARE @LastBUDate datetime,
            @Sql varchar(300)




IF @Debug = 0
      BEGIN

            IF OBJECT_ID('msdb.dbo.JobsBUPreviouslyEnabled') Is Not Null
                  BEGIN
                        Print 'Table < msdb.dbo.JobsBUPreviouslyEnabled > already exists. 
You may first need to run < Job_EnableJobsThatWereEnabled > so as
to restore job enabled/disabled attributes.'
                        RETURN
                  END

            -- Back up job info
            SELECT GetDate() InsertDate, *
            INTO msdb.dbo.JobsBUPreviouslyEnabled
            FROM msdb.dbo.sysjobs

            ALTER TABLE msdb.dbo.JobsBUPreviouslyEnabled ADD CONSTRAINT
                  PK_JobsBUPreviouslyEnabled PRIMARY KEY (Job_ID)

            DECLARE @JobID varchar(38),
                        @MaxJobID varchar(38),
                        @JobName sysname,
                        @Num smallint


            SELECT  @MaxJobID = Max(Convert(varchar(38), Job_ID)),
                        @Num = Count(*),
                        @JobID = ''
            FROM msdb.dbo.JobsBUPreviouslyEnabled
            WHERE Enabled = 1

            -- Diable jobs
            WHILE @JobID < @MaxJobID
                  BEGIN
                        -- Get next JobID
                        SELECT @JobID = Min(Convert(varchar(38), Job_ID))
                        FROM msdb.dbo.JobsBUPreviouslyEnabled
                        WHERE Enabled = 1
                          AND Convert(varchar(38), Job_ID) > @JobID

                        -- Get other attributes
                        SELECT @JobName = Name
                        FROM msdb.dbo.JobsBUPreviouslyEnabled
                        WHERE Convert(varchar(38), Job_ID) = @JobID

                        SET @Sql = 'Exec msdb.dbo.sp_update_job @Job_ID = ''' + @JobID + ''', @Enabled = 0'

                        IF @Debug = 1
                              Print @Sql 
                        ELSE
                              Exec (@Sql)
                  END

            Print '-- Number of jobs that were disabled:  ' + Convert(varchar, @Num)
            Print ''

            SELECT Name EnabledJobName, Job_ID JobID, Enabled
            FROM msdb.dbo.JobsBUPreviouslyEnabled
            WHERE Enabled = 1
            ORDER BY Name

            SELECT Name DisabledJobName, Job_ID JobID, Enabled
            FROM msdb.dbo.JobsBUPreviouslyEnabled
            WHERE Enabled = 0
            ORDER BY Name


            Print 'To re-enable these jobs, run < Exec Admin.dbo.Job_EnableJobsThatWereEnabled >.'



      END

ELSE IF @Debug = 1
      BEGIN
            IF Object_ID('msdb.dbo.JobsBUPreviouslyEnabled') Is Not Null
                  BEGIN
                        -- Get date of last BU
                        SELECT @LastBUDate = crDate
                        FROM msdb.dbo.sysobjects
                        WHERE Name = 'JobsBUPreviouslyEnabled'
                              AND xType = 'u'


                        -- Check for previous Back Up
                        IF @LastBUDate Is Not Null
                              BEGIN

                                    SET @Sql = 'There is already a backup from ' + Convert(varchar, @LastBUDate, 120) + '.'
                                    SET @Sql = @Sql + Char(13) + Char(10) + 'Consider running < Exec Job_EnableJobsThatWereEnabled > to restore all jobs to original state.'
                                    SET @Sql = @Sql + Char(13) + Char(10) + 'Or, to permanently destroy the last back up, make a new backup, and disable all jobs, rerun this proc as such:'
                                    SET @Sql = @Sql + Char(13) + Char(10) + ' Exec Job_DisableJobsThatAreEnabled 1'
                                    RaisError (@Sql, 0, 1)

                                    SELECT Name EnabledJobName, Job_ID JobID, Enabled
                                    FROM msdb.dbo.JobsBUPreviouslyEnabled
                                    WHERE Enabled = 1
                                    ORDER BY Name

                                    SELECT Name DisabledJobName, Job_ID JobID, Enabled
                                    FROM msdb.dbo.JobsBUPreviouslyEnabled
                                    WHERE Enabled = 0
                                    ORDER BY Name

                                    RETURN
                              END
                  END
     
            ELSE
                  BEGIN
                        -- Summary of jobs
                        SELECT Enabled, Count(*) QtyJobs
                        FROM msdb.dbo.sysjobs
                        GROUP BY Enabled
                        ORDER BY 1


                  END

      END