Time keeping

I’m developing a stored procedure that will present, for every employee, the time he/she worked, a time keeper.

Because the HR department, presented a lot of rules, complicating very much the logic behind it, I started to develop also some testes with tSQLt.

Here is one test :

ALTER PROCEDURE [TestTimeKeeping].[Test Entrace Without Exit Then Make it 8h]
 AS
 BEGIN
     SET XACT_ABORT ON;
     SET NOCOUNT ON;
     IF OBJECT_ID('actual') IS NOT NULL DROP TABLE actual;
     IF OBJECT_ID('expected') IS NOT NULL DROP TABLE expected;
     
     EXEC tSQLt.FakeTable 'dbo', 'Events';
     
     BEGIN TRY
         INSERT INTO dbo.Events ( UserNo, DT, Location)
         VALUES ( 1, cast('20191111 08:00:00' as datetime), 39);
    END TRY
    BEGIN CATCH
        SELECT ERROR_MESSAGE();
    END CATCH

    CREATE TABLE Actual (
         Department varchar(50),
         Marca varchar(13),
         Employee varchar(63),
         Occupation varchar(50),
         IntrareDT datetime,
         TotalHours time(0) null,
         NormalHours time(0) null,
         SDHours time(0) null,
         NightHours time(0) null
    );

      INSERT INTO Actual (Department,Marca,Employee, Occupation, IntrareDT, TotalHours, NormalHours,SDHours,NightHours)
     EXEC dbo.Company_Presence_GET @Departaments  = NULL, @dataStart='20191101', @dataEnd = '20191125', @Marca  = NULL;


     CREATE TABLE expected (
         Department varchar(50),
         Marca varchar(13),
         Employee varchar(63),
         Occupation varchar(50),
         IntrareDT datetime,
         TotalHours time(0) null,
         NormalHours time(0) null,
         SDHours time(0) null,
         NightHours time(0) null
     );

    INSERT INTO expected (Department, Marca, Employee, Occupation, IntrareDT,TotalHours,NormalHours,SDHours,NightHours)
    VALUES('DEPARTAMENT IT','3313','First Last Name','Ocup IT','20191111 00:00:00','08:00:00','08:00:00',NULL,NULL);

 /*select * from Actual;*/
 EXEC tSQLt.AssertEqualsTable 'expected', 'actual';
END;


 

Search for objects

If you are wondering what objects were used to insert some records in a table … (today was a case , when there were some records , but don’t know what stored object was used …)

;WITH RoleMembers (member_principal_id, role_principal_id) AS
(SELECT
rm1.member_principal_id,
rm1.role_principal_id
FROM sys.database_role_members rm1 (NOLOCK)
UNION ALL
SELECT
d.member_principal_id,
rm.role_principal_id
FROM sys.database_role_members rm (NOLOCK)
INNER JOIN RoleMembers AS d
ON rm.member_principal_id = d.role_principal_id
)
–SELECT * FROM RoleMembers where
SELECT DISTINCT
S.[name] + N’.’ + O.[name] AS ObjName,
SM.[definition]
,STUFF(dp.lstUsers,1,1,”) as lstUsers
FROM sys.sql_modules AS SM
INNER JOIN sys.objects AS O
ON SM.[object_id] = O.[object_id]
INNER JOIN sys.schemas AS S
ON O.[schema_id] = S.[schema_id]
OUTER APPLY
(SELECT ‘,’+ u.name
FROM sys.database_permissions as dp
INNER JOIN sys.database_principals AS pr
ON dp.grantee_principal_id = pr.principal_id
INNER JOIN RoleMembers as r
ON r.role_principal_id = pr.principal_id
INNER JOIN sysusers U ON U.uid = r.member_principal_id
WHERE
dp.major_id = o.object_id
FOR XML PATH(”)
)dp(lstUsers)
WHERE SM.[definition] LIKE ‘%tblXXX%’
and SM.[definition] LIKE ‘%Insert%’
and SM.[definition] LIKE ‘%keyText1%’
and SM.[definition] LIKE ‘%keyText2%’
and SM.[definition] LIKE ‘%keyText3%’
and SM.[definition] LIKE ‘%keyText4%’
and SM.[definition] NOT LIKE ‘%afield%’
and dp.lstUsers like ‘%aUserName%’
ORDER BY 1 asc

So, I’m searching , in the body of a object (sys.sql_module) , for the presence :

– of a table (tblXXX)

– to have the key word “insert”

– additional keys/word keyText1

– not having the missing field/word afield

– and particular user have rights on this object : and dp.lstUsers like ‘%aUserName%’

S

Help Index

There are a tone of scripts to display your indexes, in different type of form.

At some point, I decided to write my own version, and put it into SSMS -> Optins->Environment->Keyboard ->Query Shortcuts.

CREATE PROCEDURE dbo.sp_SQL_helpIndex
@objname nvarchar(128)
AS
SELECT OBJECT_NAME(ius.object_id) as tblName
,i.name
,STUFF(ca.IndexColumnsNames,1,1,”) as IndexColumns
,STUFF(ia.IncludedColumnsNames,1,1,”) as IncludedColumns
,STATS_DATE(ius.object_id, i.index_id) as Stat_Date
,ius.user_seeks
,ius.user_scans
,ius.user_lookups
,ius.last_user_seek
,ius.last_user_scan
,i.is_unique
,i.is_primary_key
,i.is_unique_constraint
,i.is_disabled
,i.has_filter
–,’ALTER INDEX ‘ + QUOTENAME(i.name) + ‘ ON ‘ + QUOTENAME(OBJECT_SCHEMA_NAME(i.object_id )) + ‘.’+ QUOTENAME(OBJECT_NAME(i.object_id)) + ‘ DISABLE;’ as DisableStmt
–,’DROP INDEX ‘ + QUOTENAME(i.name) + ‘ ON ‘ + QUOTENAME(OBJECT_SCHEMA_NAME(i.object_id )) + ‘.’+ QUOTENAME(OBJECT_NAME(i.object_id)) + ‘;’ as dropStmt
FROM sys.dm_db_index_usage_stats as ius
inner join sys.indexes as i
ON ius.object_id = i.object_id
AND ius.index_id = i.index_id
cross apply
(select ‘, ‘+ c.name +”
from sys.index_columns as ic
inner join sys.columns as c
on c.object_id = ic.object_id
and c.column_id = ic.column_id
where ic.object_id = i.object_id
and ic.index_id = i.index_id
and ic.is_included_column = 0
order by ic.key_ordinal
for xml path(”)
)ca(IndexColumnsNames)

outer apply
(select ‘, ‘+ c.name +”
from sys.index_columns as ic
inner join sys.columns as c
on c.object_id = ic.object_id
and c.column_id = ic.column_id
where ic.object_id = i.object_id
and ic.index_id = i.index_id
and ic.is_included_column = 1
order by ic.index_column_id ASC
for xml path(”)
)ia(IncludedColumnsNames)

WHERE
i.type > 1 — nonclustered index
and database_id = DB_ID(DB_Name()) — database in it
and OBJECT_NAME(ius.object_id) =@objname
ORDER BY OBJECT_NAME(ius.object_id),QUOTENAME(i.name)
go

And then mark it as a system stored procedure:

exec [sys].[sp_MS_marksystemobject] ‘sp_SQL_helpIndex’
GO

ps: please here is a condition on it:

and database_id = DB_ID(DB_Name()) — database in it

S

SET NOCOUNT ON

SET NOCOUNT ON and ASYNC_NETWORK_IO

SET NOCOUNT ON

From BOL , SET NOCOUNT ON the count is not returned. When is OFF, the count is returned.

Additional, prevents the sending of DONE_IN_PROC messages to the client . This is used within stored procedures and triggers to avoid showing the affected rows message. (XXX rows affected)

So , after every statement , in a stored procedure or trigger, sql server sends a message DONE_IN_PROC (rows affected) .With SET NOCOUNT ON, you can suppress these messages.

If you are not in a stored procedure , if you are in a batch , SQL Server sends DONE message/token for each statement.  This can lead (in case of client not consume it fast) to waits s ASYNC_NETWORK_IO.  To reduce it , you can use a temp stored procedure or sp_executeSQL .

When a statement is executed, the result ends with a DONE, DONEPROC, or DONEINPROC, depending of the where this statement is: stored procedure, batch…

  • DONE_IN_PROC, DONE_PROC are sent for statements executed in stored procedures , triggers
  • DONE are sent for statements executed in batch

Conclusion:

SET NOCOUNT ON , suppress only DONE_IN_PROC and not DONE messages.

S

Simple parameterization

I was checking my cache plan , and founded that there was a database with more then 44k+ plans , that are only one time used.

Digging up, I discovered a query and asked my self , way it wasn’t parameterized.

Here is a sample:

select *
from sales.SalesOrderHeader as soh
where soh.SalesOrderID = 43675

 

select TOP(1) *
from sales.SalesOrderHeader as soh
where soh.SalesOrderID = 43675

 

Here is the execution plan(part of it)

 

and xml representation:

 

SimpleParam_notParamXML

SimpleParam_ParamXML.jpg
Simple parameterization can happens , in the following situation :
– must be a trivial plan . if it’s not trivial, no simple parameterization
in xml plan is : StatementOptmLevel=”TRIVIAL”

– the query must not have JOIN, IN, TOP,GROUP BY, HAVING, Distinct , Subqueries

 

Conclusion:

QO decide to parameterize a query when it consider it safe to param , in a trivial plan , and not have : joins, In, Top, subqueries….

 

S

 

 

Check DB

How to find the last good known checkDB date?

Starting with SQL Server 2016 SP2, we have DATABASEPROPERTYEX (‘dbname’ , ‘LastGoodCheckDbTime’ )

In SQL Server 2017, CU14 ( I tested it) , this is also working.

Like so:

SELECT DATABASEPROPERTYEX (‘db_workspace’ , ‘LastGoodCheckDbTime’ )

or

SELECT DATABASEPROPERTYEX (d.name , ‘LastGoodCheckDbTime’ )

FROM sys.databases as d

 

Value of  `1900-01-01 00:00:00.000` means – no check db was  run.
Please, see here more info
S

New in 2019

Sql server 2019, in this moment, is still in CTP.

There is a new DMF – sys.dm_exec_query_plan_stats  , starting with 2.4CTP .

This DMF allow you to access the last execution plan, last ACTUAL execution plan for any query (with statistics). With out doing a thing(allmost).

You must have a trace flag activated – TF 2451 –  which allows you to access light weight profiling tech. (No need to restart the instance)

More details are here.

 

S

 

 

 

 

Parallel plan

A few days ago, I was call to see about a query that was some strange behavior.

The query was  composed by two queries with a UNION ALL .

Q1 UNION All Q2

and was taking around 15-20 sec to execute.

Q1,Q2 have like 15-20 tables and views joining together.  There were filtering for this month and year. Q1 has the result data, Q2 – hold the old data, fox data so, no result here.

Taking each on separately and execute them, they were finishing in less then 1 sec.

Q1 – was able to produce a parallel plan , Q2 – was unable to produce a parallel plan.

Reunion them, QO -was unable to produce a parallel plan because of Q2.

MaxDOP = 0  (this setting should be change , but I’m not in control) –

Threshold for Parallel  = 5 (this setting should also be change , but I’m not in control)

No  Resource Governor limitation in place…

From this point , they should trigger parallel plan. Looking at the execution plan , in xml output, here there was  NonParallelPlanReason = CouldNotGenerateValidParallelPlan .

Button line, in Q2 , was a view, that had an scalar function , this was disable parallel option. Rewrite it , and then all when well.

 

Conclusion:

Look at execution plan, also in xml version, you will find good information.

 

S

 

AT TIME Zone

New in sql server, starting with version 2016, it’s AT TIME ZONE, used to convert some date values to a specific zone.

 

Here is a sample :

declare @dt2_myDate as datetime2(0) = ‘20190216 08:10:12’ ;

select CONVERT(datetime2(0) , @dt2_myDate , 126)
AT TIME ZONE ‘Central European Standard Time’
AT TIME ZONE ‘UTC’ as newDateTime

 

newDateTime

16/02/2019 07:10:12 +00:00

 

S

Check database roles

If you have a new user/login and want to grant some permission base on an existing user. For this , I  use a script like this :

USE myDatabase
GO
set transaction isolation level read uncommitted;
;WITH RoleMembers (member_principal_id, role_principal_id)
AS
(
SELECT
rm1.member_principal_id,
rm1.role_principal_id
FROM sys.database_role_members rm1 (NOLOCK)
UNION ALL
SELECT
d.member_principal_id,
rm.role_principal_id
FROM sys.database_role_members rm (NOLOCK)
INNER JOIN RoleMembers AS d
ON rm.member_principal_id = d.role_principal_id
)
,lstUsers AS
(SELECT 5 as iIndex,’newUser’ as userFinal, ‘baseUser’ as userBase
)
select
CA.database_role as finalUserRoles
,u.userFinal as database_finalUser
,u.userBase as userBase
,OA.database_role as database_role_base__need_to_be_granted
,’IF IS_ROLEMEMBER(”’ + OA.database_role +”’ , ”’ + u.userFinal +”’) = 0 ‘ +
‘ BEGIN ‘ + CHAR(13) + CHAR(10) +
‘ ALTER ROLE [‘ + OA.database_role + ‘] ADD MEMBER ‘ + ‘[‘+ u.userFinal +’]’ + ‘;’ + + CHAR(13) + CHAR(10) +
‘ RAISERROR (” Rolul ‘ + OA.database_role + ‘ for user ‘ + u.userFinal + ‘ was granted;”,10,1) WITH NOWAIT;’ + CHAR(13) + CHAR(10) +
‘END ‘ + CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10)
as stmtToExec
from
lstUsers as u
OUTER APPLY
(
SELECT distinct
rp.name
FROM
RoleMembers as drm
inner join sys.database_principals as rp on drm.role_principal_id = rp.principal_id
inner join sys.database_principals as mp on drm.member_principal_id = mp.principal_id
WHERE
mp.name = u.userBase
)OA(database_role) –base Role
OUTER APPLY
(
SELECT DISTINCT
rp.name
FROM
RoleMembers as drm
inner join sys.database_principals as rp on drm.role_principal_id = rp.principal_id
inner join sys.database_principals as mp on drm.member_principal_id = mp.principal_id
WHERE
mp.name = u.userFinal
and OA.database_role = rp.name
)CA(database_role) — for finalUser
order by u.iIndex,u.userFinal

S