Monitoring SQL transactions and their elapsed time in seconds - sql

i'm currently challenged with the task to monitor an mssql server and i'd like to get an overview of pending/running transactions in tempDB.
I use following query to get a table of transactions and their elapsed_time_seconds
SELECT
a.session_id
, a.transaction_id
, a.transaction_sequence_num
, a.elapsed_time_seconds
, b.program_name
, b.open_tran
, b.STATUS
FROM sys.dm_tran_active_snapshot_database_transactions a
JOIN sys.sysprocesses b ON a.session_id = b.spid
ORDER BY elapsed_time_seconds DESC
The problem:
This query does not return anything if the table is empty.
Not even NULL. Additonally I dont speak SQL.
I've tried to place COALESCE and ISNULL in the query at different rows but this didn't help.
Can i somehow extend the query so that it returns 0 0 0 0 0 in the table row if nothing else is returned?
Thanks and best regards
Manuel

It's unclear why you would want this, but you can start off with a dummy VALUES constructor and left-join everything else.
Note that sysprocesses is deprecated
Note also that dm_tran_active_snapshot_database_transactions only shows transactions in the snapshot isolation level. You probably want dm_tran_active_transactions instead.
SELECT
ISNULL(t.session_id, 0)
, ISNULL(t.transaction_id, 0)
, ISNULL(t.transaction_sequence_num, 0)
, ISNULL(t.elapsed_time_seconds, 0)
, s.program_name
, ISNULL(s.open_transaction_count, 0)
, s.STATUS
FROM (VALUES(0)) v(dummy)
LEFT JOIN
sys.dm_tran_active_snapshot_database_transactions t
JOIN sys.dm_exec_sessions s ON s.session_id = t.spid
ON 1 = 1
ORDER BY elapsed_time_seconds DESC

Related

SQL View slow when filtered. Is there a clean way to improve performance?

Let me open with:
SHOWPLAN permission denied in database 'MyDatabase'.
With that out of the way, I'll layout my situation.
So, The database I work with has a view that executes fairly quickly.
SELECT * FROM MyView
returns 32 rows in 1 second and includes a non-indexed column of values (IDs) I need to filter on.
If I filter on these IDs directly in the view:
SELECT * FROM MyView WHERE MyView.SomeId = 18
Things slow immensely and it takes 21 seconds to return the 20 rows with that ID.
As an experiment I pushed the unfiltered results into a temporary table and executed the filtered query on the the temporary table:
IF OBJECT_ID('tempdb..#TEMP_TABLE') IS NOT NULL
BEGIN
DROP TABLE #TEMP_TABLE
END
SELECT * INTO #TEMP_TABLE
FROM MyView;
SELECT *
FROM #TEMP_TABLE
WHERE #TEMP_TABLE.SomeId = 18
DROP TABLE #TEMP_TABLE
And found that it returns the filtered results far faster (roughly 1 second)
Is there a cleaner syntax or pattern that can be implemented to achieve the same performance?
UPDATE: View Definition and Description
Manually obfuscated, but I was careful so hopefully there aren't many errors. Still waiting on SHOWPLAN permissions, so Execution Plan is still pending.
The view's purpose is to provide a count of all the records that belong to a specific component (CMP.COMPONENT_ID = '100') grouped by location.
"Belonging" is determined by the record's PROC_CODE (mapped through PROC_ID) being within the CMP's inclusion range (CMP_INCs) and not in the CMP's exclusion range (CMP_EXCs).
In practice, exclusion ranges are created for individual codes (the bounds are always equal) making it sufficient to check that the code is not equal a bound.
PROC_CODES can (and don't always) have an alphabetic prefix or suffix, which makes the ISNUMERIC() comparison necessary.
Records store PROC_IDs for their PROC_CODEs, so it's necessary to convert the CMP's PROC_CODE ranges into a set of PROC_IDs for identifying which records belong to that component
The performance issue occurs when trying to filter by DEPARTMENT_ID or LOCATION_ID
[CO_RECORDS] is also a view, but if it's that deep I'm going turf this to someone with less red tape to fight through.
CREATE VIEW [ViewsSchema].[MyView] AS
WITH
CMP_INCs AS (SELECT RNG.*, COALESCE(RNG.RANGE_END, RNG.RANGE_BEG) [SAFE_END] FROM DBEngine.DBO.DB_CMP_RANGE [RNG] WHERE [RNG].COMPONENT_ID = '100'),
CMP_EXCs AS (SELECT CER.* FROM DBEngine.DBO.DB_CMP_EXC_RANGE CER WHERE CER.COMPONENT_ID = '100'),
CMP_PROC_IDs AS (
SELECT
DBEngine_ProcTable.PROC_ID [CMP_PROC_ID],
DBEngine_ProcTable.PROC_CODE [CMP_PROC_CODE],
DB_CmpTable.COMPONENT_ID [CMP_ID],
MAX(DB_CmpTable.COMPONENT_NAME) [CMP_NAME]
FROM [DBEngine].DBO.DBEngine_ProcTable DBEngine_ProcTable
LEFT JOIN CMP_INCs ON ISNUMERIC(DBEngine_ProcTable.PROC_CODE) = ISNUMERIC(CMP_INCs.RANGE_BEG)
AND(DBEngine_ProcTable.PROC_CODE = CMP_INCs.RANGE_BEG
OR DBEngine_ProcTable.PROC_CODE BETWEEN CMP_INCs.RANGE_BEG AND CMP_INCs.SAFE_END)
INNER JOIN DBEngine.DBO.DB_CmpTable ON CMP_INCs.COMPONENT_ID = DB_CmpTable.COMPONENT_ID
LEFT JOIN CMP_EXCs EXCS ON EXCS.COMPONENT_ID = DB_CmpTable.COMPONENT_ID AND EXCS.EXCL_RANGE_END = DBEngine_ProcTable.PROC_CODE
WHERE EXCS.EXCL_RANGE_BEG IS NULL
GROUP BY
DBEngine_ProcTable.PROC_ID,
DBEngine_ProcTable.PROC_CODE,
DBEngine_ProcTable.BILL_DESC,
DBEngine_ProcTable.PROC_NAME,
DB_CmpTable.COMPONENT_ID
)
SELECT
RECORD.LOCATION_NAME [LOCATION_NAME]
, RECORD.LOCATION_ID [LOCATION_ID]
, MAX(RECORD.[Department]) [DEPARTMENT]
, RECORD.[Department ID] [DEPARTMENT_ID]
, SUM(RECORD.PROCEDURE_QUANTITY) [PROCEDURE_COUNT]
FROM DBEngineCUSTOMRPT.ViewsSchema.CO_RECORDS [RECORDS]
INNER JOIN CMP_PROC_IDs [CV] ON [CV].CMP_PROC_ID = [RECORDS].PROC_ID
CROSS JOIN (SELECT DATEADD(M, DATEDIFF(M, 0,GETDATE()), 0) [FIRSTOFTHEMONTH]) VARS
WHERE [RECORDS].TYPE = 1
AND ([RECORDS].VOID_DATE IS NULL OR [RECORDS].VOID_DATE >= VARS.[FIRSTOFTHEMONTH] )
AND [RECORDS].POST_DATE < VARS.[FIRSTOFTHEMONTH]
AND [RECORDS].DOS_MONTHS_BACK = 2
GROUP BY [RECORDS].LOCATION_NAME, [RECORDS].[Department ID]
GO
Based on the swift down votes, the answer to my question is
'No, there is not a clean syntax based solution for the improved
performance, and asking for one is ignorant of the declarative nature
of SQL you simple dirty plebeian'.
From the requests for the view's definition, it's clear that performance issues in simple queries should be addressed by fixing the structure of the objects being queried ('MyView' in this case) rather than syntactical gymnastics.
For interested parties the issue was resolved by adding a Row_Number() column to the final select in the view definition, wrapping it in a CTE, and using the new column in an always true filter while selecting the original columns.
I have no idea if this is the optimal solution. It doesn't feel good to me, but it appears to be working.
CREATE VIEW [ViewsSchema].[MyView] AS
WITH
CMP_INCs AS (SELECT RNG.*, COALESCE(RNG.RANGE_END, RNG.RANGE_BEG) [SAFE_END] FROM DBEngine.DBO.DB_CMP_RANGE [RNG] WHERE [RNG].COMPONENT_ID = '100'),
CMP_EXCs AS (SELECT CER.* FROM DBEngine.DBO.DB_CMP_EXC_RANGE CER WHERE CER.COMPONENT_ID = '100'),
CMP_PROC_IDs AS (
SELECT
DBEngine_ProcTable.PROC_ID [CMP_PROC_ID],
DBEngine_ProcTable.PROC_CODE [CMP_PROC_CODE],
DB_CmpTable.COMPONENT_ID [CMP_ID],
MAX(DB_CmpTable.COMPONENT_NAME) [CMP_NAME]
FROM [DBEngine].DBO.DBEngine_ProcTable DBEngine_ProcTable
LEFT JOIN CMP_INCs ON ISNUMERIC(DBEngine_ProcTable.PROC_CODE) = ISNUMERIC(CMP_INCs.RANGE_BEG)
AND(DBEngine_ProcTable.PROC_CODE = CMP_INCs.RANGE_BEG
OR DBEngine_ProcTable.PROC_CODE BETWEEN CMP_INCs.RANGE_BEG AND CMP_INCs.SAFE_END)
INNER JOIN DBEngine.DBO.DB_CmpTable ON CMP_INCs.COMPONENT_ID = DB_CmpTable.COMPONENT_ID
LEFT JOIN CMP_EXCs EXCS ON EXCS.COMPONENT_ID = DB_CmpTable.COMPONENT_ID AND EXCS.EXCL_RANGE_END = DBEngine_ProcTable.PROC_CODE
WHERE EXCS.EXCL_RANGE_BEG IS NULL
GROUP BY
DBEngine_ProcTable.PROC_ID,
DBEngine_ProcTable.PROC_CODE,
DBEngine_ProcTable.BILL_DESC,
DBEngine_ProcTable.PROC_NAME,
DB_CmpTable.COMPONENT_ID
),
RESULTS as (
SELECT
RECORD.LOCATION_NAME [LOCATION_NAME]
, RECORD.LOCATION_ID [LOCATION_ID]
, MAX(RECORD.[Department]) [DEPARTMENT]
, RECORD.[Department ID] [DEPARTMENT_ID]
, SUM(RECORD.PROCEDURE_QUANTITY) [PROCEDURE_COUNT]
, ROW_NUMBER() OVER (ORDER BY TDL.[Medical Department ID], TDL.[BILL_AREA_ID], TDL.JP_POS_NAME) [ROW]
FROM DBEngineCUSTOMRPT.ViewsSchema.CO_RECORDS [RECORDS]
INNER JOIN CMP_PROC_IDs [CV] ON [CV].CMP_PROC_ID = [RECORDS].PROC_ID
CROSS JOIN (SELECT DATEADD(M, DATEDIFF(M, 0,GETDATE()), 0) [FIRSTOFTHEMONTH]) VARS
WHERE [RECORDS].TYPE = 1
AND ([RECORDS].VOID_DATE IS NULL OR [RECORDS].VOID_DATE >= VARS.[FIRSTOFTHEMONTH] )
AND [RECORDS].POST_DATE < VARS.[FIRSTOFTHEMONTH]
AND [RECORDS].DOS_MONTHS_BACK = 2
GROUP BY [RECORDS].LOCATION_NAME, [RECORDS].[Department ID]
)
SELECT
[LOCATION_NAME]
, [LOCATION_ID]
, [DEPARTMENT]
, [DEPARTMENT_ID]
, [PROCEDURE_COUNT]
FROM RESULTS
WHERE [ROW] > 0
GO

query get records lesser than SQL

This query which get records of all clubs, i check record per records. The NULL columns doesn't play any role. Howerver i filled NULL's. This mean any NULL isn't existing.
This query get 202 recordcount using cfquery and on MSSQL 2014 get 204 records.
Could you please tell what could happen that cfquery can't get some records. The exact query work on MSSQL 2014 perfectly.
SELECT tblDistrict.IDDist,
tblClubs.IDDiv,
tblClubs.ClubArtStart,
tblClubs.IDClub,
tblClubs.ClubName,
tblDivisionen.DivBezeichnung,
tblDivisionen.Region,
tblClubs.OrgDatum,
tblClubs.ChartDatum,
tblClubs.ClubStatus,
tblClubs.ClubArt,
tblClubs.Clubort
FROM (tblDistrict
INNER JOIN tblDivisionen ON tblDistrict.IDDist = tblDivisionen.IDDist)
INNER JOIN tblClubs ON tblDivisionen.IDDiv = tblClubs.IDDiv
WHERE (((tblDistrict.IDDist)=1)) AND Clubstatus<>'E' AND Clubstatus<>'I'
I would start with some formatting and aliasing. Your query would look something like this. This would be a lot more maintainable.
SELECT d.IDDist
, c.IDDiv
, c.ClubArtStart
, c.IDClub
, c.ClubName
, div.DivBezeichnung
, div.Region
, c.OrgDatum
, c.ChartDatum
, c.ClubStatus
, c.ClubArt
, c.Clubort
FROM tblDistrict d
INNER JOIN tblDivisionen div ON d.IDDist = div.IDDist
INNER JOIN tblClubs c ON div.IDDiv = c.IDDiv
WHERE d.IDDist = 1
AND c.Clubstatus not in ('E', 'I')

Generating missing report submissions via reference table

I have a SQL query i'm currently working on which i would greatly appreciate some help with.
Here is a simplified version of the view I've been given to work on:
SELECT a.Organisation_Name
,a.Org_Id
,b.Activity_month
,SUM(b.Activity_Plan) 'Plan_Activity'
,SUM(b.Activity_Actual) 'Actual_Activity'
,SUM(b.Price_Actual) 'Actual_Price'
,SUM(b.Price_Plan) 'Plan_Price'
,COUNT(b.Instances) AS 'Record_Count'
,CASE WHEN COUNT(b.Instances) > 0 THEN 'Yes' ELSE 'No' END AS Submitted
FROM [ExampleDatabase].[dbo].[Organisation_Reference] a
LEFT JOIN [ExampleDatabase].[dbo].[Report_Submissions] b
ON a.Org_Id = b.Org_Id
AND ([Exmaple_Code] LIKE ('X') or [Example_Code] = 'X')
WHERE a.Category_Flag = 1
AND a.Example_Code in ('X','X','X','X','X')
GROUP BY
a.Organisation_Name
,a.Org_Id
,b.Activity_month
--
The Activity Month field is an Integer rather than a date, currently ranging from 1-8.
The problem i am facing is that within the [Report_Submissions] table, it only contains organisations which have actually submitted the reports, whereas the
[Organisation_Reference] table lists all the organisations which should be submitting.
Where the organisations have submitted the reports, the data is perfect and gives me a run down of all the details i need for each individual month.
Obviously if an organisation hasn't submitted then this detail wouldn't be available, but i do need to have a complete list of all organisations listed from the reference table for each individual month and whether they have submitted the reports or not.
At the moment where the 'Submitted' field = 'No' it's only bringing back one record for each organisation that has never submitted (With Activity_month coming through as null) and if an organisation has only submitted once or twice then it will include those submissions but still be missing the rest of the months from the result set.
I've tried various different joins etc. but I seem to be drawing a blank for a solution. Is there a way of generating this information within the script? Any advice would be great!
Kind Regards,
Mark
Since you just need numbers 1-8, using a subquery in your join to cross apply(values ()) to your Organisation_Reference table works well and does not make the query much more compliCated to read.
select
a.Organisation_Name
, a.Org_Id
, a.Activity_Month
, sum(b.Activity_Plan) 'Plan_Activity'
, sum(b.Activity_Actual) 'Actual_Activity'
, sum(b.Price_Actual) 'Actual_Price'
, sum(b.Price_Plan) 'Plan_Price'
, count(b.Instances) as 'record_count'
, case when count(b.Instances) > 0 then 'yes' else 'no' end as Submitted
from (
select o.*, t.Activity_Month
from [ExampleDatabase].[dbo].[Organisation_Reference] as o
cross apply (values (1),(2),(3),(4),(5),(6),(7),(8)) t(Activity_Month)
) as a
left join [ExampleDatabase].[dbo].[Report_Submissions] b
on a.Org_Id = b.Org_Id
and a.Activity_Month = b.Activity_Month
and ([exmaple_Code] like ('X') or [Example_Code] = 'X')
where a.Category_Flag = 1
and a.Example_Code in ('X','X','X','X','X')
group by
a.Organisation_Name
, a.Org_Id
, b.Activity_Month
You could also cross join with a numbers/tally table, or use a common table expression to generate the range of numbers you need. I would recommend either of those options as well, especially if your logic was more compliCated.
If Report_Submissions contains all of the months you want in your query, you could cross join the distinct Activity_Months from that table to your Organisation_Reference table.
select
a.Organisation_Name
, a.Org_Id
, a.Activity_Month
, sum(b.Activity_Plan) 'Plan_Activity'
, sum(b.Activity_Actual) 'Actual_Activity'
, sum(b.Price_Actual) 'Actual_Price'
, sum(b.Price_Plan) 'Plan_Price'
, count(b.Instances) as 'record_count'
, case when count(b.Instances) > 0 then 'yes' else 'no' end as Submitted
from (
select o.*, t.Activity_Month
from [ExampleDatabase].[dbo].[Organisation_Reference] as o
cross join (select distinct Activity_Month from Report_Submissions) t
) as a
left join [ExampleDatabase].[dbo].[Report_Submissions] b
on a.Org_Id = b.Org_Id
and a.Activity_Month = b.Activity_Month
and ([exmaple_Code] like ('X') or [Example_Code] = 'X')
where a.Category_Flag = 1
and a.Example_Code in ('X','X','X','X','X')
group by
a.Organisation_Name
, a.Org_Id
, b.Activity_Month

Case Statement using Dates

I have a data set of clients. Each have a Start date and an End Date. A client can have multiple lines with different Start dates and End dates. I have another data set with Claims info and i want to know if they had claims during the time frame there state and end date.
how can i write this?
SELECT
M.[ID]
,EN.StartDate
,EN.EndDate
,[Has Cliams History] (Column to identify if yes or no)
FROM [Test].[dbo].[tblClients] M
left join [test].[dbo].[tblCliams] EN on EN.ID = M.ID
To illustrate the use of a case statement, the ClaimDate field represents the date of a claim. The query would look something like this.
SELECT
M.[ID]
,EN.StartDate
,EN.EndDate
,CASE
WHEN ClaimDate between EN.StartDate AND EN.EndDate THEN 'yes'
ELSE 'no'
END [Has Cliams History]
FROM [Test].[dbo].[tblClients] M
left join [test].[dbo].[tblClaims] EN on EN.ID = M.ID
Your question needs to provide more clarity on the definition of both tables and the relationship between the two. I am guessing you aren't actually joining a column called ID from both (at least I hope not). Here is a stab at it based on the assumption you have a ClientID on the tblClaims and also that you support ongoing clients via a NULL EndDate. Also assuming there are Start and End dates on tblClaims.
SELECT M.[ID]
, CASE WHEN MAX(claim.ID) IS NOT NULL THEN 1 ELSE 0 END AS HasClaimsHistory
FROM [Test].[dbo].[tblClients] client
LEFT JOIN [Test].[dbo].[tblCliams] claim on client.ID = claim.ClientID
AND claim.StartDate >= client.StartDate
AND (
client.EndDate IS NULL /* ongoing support? */
OR
claim.EndDate <= client.EndDate
)
GROUP BY M.[ID]
, CASE WHEN MAX(claim.ID) IS NOT NULL THEN 1 ELSE 0 END

Like SQL Statement SQL Server 2008R2

I have a simple query but I have mutiple records I need to filter out. I'm using the like statment with wild cards. Is there a better way do do this then writing out each one? Can I create a udf, table that it refrences? How? If I can. Thanks :)
SELECT a.SalesOrderNo ,
a.ShipExpireDate ,
a.CustomerNo ,
b.ItemCode ,
b.LineKey ,
b.QuantityOrdered ,
b.QuantityShipped ,
b.ItemCodeDesc ,
b.ExplodedKitItem
FROM dbo.SO_SalesOrderHeader a
LEFT JOIN dbo.SO_SalesOrderDetail b
ON a.SalesOrderNo = b.SalesOrderNo
WHERE b.ItemType = '1'
AND b.ItemCodeDesc NOT LIKE '%Cert%'
AND b.ItemCodeDesc NOT LIKE '%Fee%'
AND b.ItemCodeDesc NOT LIKE '%Tag%'
AND b.ItemCode NOT LIKE 'GF%'
AND b.ItemCode NOT LIKE 'PXDIALPREP'
AND b.ItemCode NOT LIKE '/C%'
AND a.ShipExpireDate = CONVERT(DATE, GETDATE(), 101)
Here's a different design that lets you put ItemCodeDesc in a seperate table (this could also be a TVF). I can't comment on performance though.
On a different note, be aware that because you are outer joining to sales order detail, this table can have NULL records. In turn your b.ItemType = '1' will always be FALSE when ItemType is NULL. So you may as well make it an inner join (and you might find your query plan is doing that anyway)
SELECT a.SalesOrderNo ,
a.ShipExpireDate ,
a.CustomerNo ,
b.ItemCode ,
b.LineKey ,
b.QuantityOrdered ,
b.QuantityShipped ,
b.ItemCodeDesc ,
b.ExplodedKitItem
FROM dbo.SO_SalesOrderHeader a
LEFT JOIN dbo.SO_SalesOrderDetail b
ON a.SalesOrderNo = b.SalesOrderNo
WHERE b.ItemType = '1'
AND b.ItemCode NOT LIKE 'GF%'
AND b.ItemCode NOT LIKE 'PXDIALPREP'
AND b.ItemCode NOT LIKE '/C%'
AND a.ShipExpireDate = CONVERT(DATE, GETDATE(), 101)
AND NOT EXISTS (
SELECT 1 FROM dbo.MappingTable MT
WHERE b.ItemCodeDesc LIKE MT.ItemCodeDesc
)
Note: I am guessing that your criteria is meant to filter out item types that can't be shipped (like Fees), adjust as per your requirements.
The problem you are encountering is a result of discrete values being stored in an ID. Looks like you should have a column IsShippable, or better yet a code table for ItemCodeType with rows of Cert, Fee, Tag, etc. and the IsShippable column there. if you had a code table then you'd be able to do
inner join ItemCodeTypes ict on ict.ItemCodeTypeId = b.ItemCodeTypeId and ict.IsShippable = 1
Cert, Fee, Tag, rows in the ItemCodeTypes table would have IsShippable = 0:
Id | Name | IsShippable
1 Cert 0
2 Fee 0
3 Tag 0
4 Product 1
5 Book 1
Edit: To more directly answer your question, you could make a view like this, and then when you query from it easily filter on Where IsShippable = 1:
Select CASE
When b.ItemCodeDesc LIKE '%Cert%' Then 0
When b.ItemCodeDesc LIKE '%Fee%' Then 0
--etc.
Else 1
END as IsShippable
,*
From dbo.SO_SalesOrderDetail