I have an SSIS package that is configured to log to SQL Server. With the package configured, a System Table is created under: MyDatabase > System Tables > dbo.sysssislog
This table has a schema that matches the identically named table held in msdb.
Within this table, each package execution has a unique executionid, which is defined as:
uniqueidentifier
The GUID of the execution instance of the executable that generated the logging entry
This is generated each time the SSIS package is run and held within the following system variable: System::ExecutionInstanceGUID
Sample Logs query:
SELECT [event] ,
[source] ,
[executionid] ,
[starttime] ,
[endtime]
FROM MyDatabase.[dbo].[sysssislog]
WHERE [event] IN ( 'PackageEnd', 'PackageStart' )
ORDER BY id desc, starttime
Produces:
event source starttime endtime executionid
PackageEnd Package 2017-04-10 11:12:01 2017-04-10 11:12:01 4EDBF979-5E99-44DB-AA08-839D5DCF3F2F
PackageStart Package 2017-04-10 11:12:01 2017-04-10 11:12:01 4EDBF979-5E99-44DB-AA08-839D5DCF3F2F
PackageEnd Package 2017-04-05 13:39:11 2017-04-05 13:39:11 9E212747-3CB7-44D8-8728-9E442082DB8B
PackageStart Package 2017-04-05 13:39:11 2017-04-05 13:39:11 9E212747-3CB7-44D8-8728-9E442082DB8B
Within my application I'm using various SQL Server Agent Job Stored Procedures to retrieve SSIS job information and history. For example:
EXEC msdb.dbo.sp_help_jobhistory
#job_name = N'MyJobName',
#step_id = null
GO
Produces (summary of columns, 1 execution = 3 rows):
job_id job_name run_date run_time run_duration
52916CFE-A652-4AAA-A052-738E4B349966 MyJobName 20170410 111145 16
52916CFE-A652-4AAA-A052-738E4B349966 MyJobName 20170410 111200 1
52916CFE-A652-4AAA-A052-738E4B349966 MyJobName 20170410 111145 15
52916CFE-A652-4AAA-A052-738E4B349966 MyJobName 20170405 133855 16
52916CFE-A652-4AAA-A052-738E4B349966 MyJobName 20170405 133910 1
52916CFE-A652-4AAA-A052-738E4B349966 MyJobName 20170405 133855 15
I'm building an ETL Admin page in my application that shows SSIS job history and a summary of the logs, but I cannot find a way to link logs, based on the unique executionid, to the Job History returned from the various Agent Job System Stored Procedures.
Is there a way to link the executionid from sysssislog to information held against the Agent Job execution history? The best I can come up with is using date/time matching to identify the logs that are closest in time to the agent job stats.
I've looked at using script tasks and firing custom events to log the System::ExecutionInstanceGUID to the agent job history, but I'm unable to use script tasks as it will not work when deployed to customers with later versions of SQL Server.
Please note, any solutions need to be compatible with 2008R2.
I ended up with the following solution, which isn't the prettiest and most performant, but it satisfied the requirement I had. I wanted the last n number of executions of my ssis package, which is specified by the 2 parameters. I hold this code in a stored procedure that is called by my application:
DECLARE #packageName NVARCHAR(250) = 'MySqlServerAgentJobName',
#rowsToTake INT = 15;
WITH cte
AS (SELECT TOP 15 msdb.dbo.agent_datetime(h.run_date, h.run_time) AS LastRunDate,
h.run_status RunStatus,
h.run_duration RunDurationSeconds,
(
SELECT TOP 1 executionid
FROM [dbo].[sysssislog]
WHERE starttime >= msdb.dbo.agent_datetime(h.run_date, h.run_time)
AND [event] = ('PackageStart')
) ExecutionId
FROM msdb.dbo.sysjobs j
INNER JOIN msdb.dbo.sysjobhistory h ON j.job_id = h.job_id
WHERE j.name = #packageName AND h.step_name = '(Job outcome)'
ORDER BY LastRunDate DESC
)
SELECT cte.LastRunDate,
cte.RunStatus,
cte.RunDurationSeconds,
cte.ExecutionId
FROM cte
It uses a built in function to parse a valid datetime from the agent job fields:
msdb.dbo.agent_datetime(h.run_date, h.run_time) AS LastRunDate
It retrieves the top 1 execution id from the sysssislogs table, filtering on [event] = ('PackageStart'), which exists for each time the job is executed and the starttime must be >= the LastRunDate that the agent job logged.
It produces results like this:
LastRunDate RunStatus RunDurationSecs ExecutionId
2017-07-12 12:32:18.000 1 210 99A8A715-890D-4115-975F-AC3DA660770D
2017-07-12 12:00:01.000 1 215 D152C1C6-530F-45D6-B962-41784EC7D9E5
2017-07-12 11:22:13.000 1 204 BBAC73C1-1600-477E-AFC9-1F66D7CFD07A
2017-07-12 10:25:44.000 1 213 A4C69B3C-462B-4000-B2BD-8C9EB47230DA
2017-07-12 10:16:19.000 1 230 3D4D8572-E4CC-4B70-875A-C5D3B513A480
2017-07-12 07:33:21.000 1 244 D8283955-B3EB-4E6D-BFDC-8D7C0D954574
2017-07-11 12:00:00.000 1 211 553FC580-54B2-490C-BB1D-6B1F9BB35C84
2017-07-10 16:21:16.000 1 212 B9A78077-A04C-49AC-A4C6-EF6AB636D862
2017-07-10 16:13:18.000 1 255 DFC853D8-67F5-43CE-B8A4-00B846425C6B
2017-07-10 14:04:00.000 1 217 8C6861B1-6CDE-44FD-8606-18F24F339C05
2017-07-10 13:58:27.000 1 224 5A05011C-9F14-4098-B579-0E75C1E76BDB
2017-07-10 13:53:14.000 1 231 9A2B52FB-2BD4-4FAA-A4A0-40AC48A41326
2017-07-10 13:42:06.000 1 210 9BDBB290-1C84-49ED-87E6-97522DB4AB81
2017-07-10 12:06:44.000 1 215 F4D0A847-F7E4-4F2B-B328-58C387D2C50E
2017-07-10 12:00:01.000 1 241 5EC52369-9832-4718-AF92-2C502D454A41
Related
I have a SSIS package deployed in SQL Server and there are 3 different SQL Server Agent jobs that runs this package in different steps and schedules.
My question is: if the package is showing as failed in the Integration Services Catalogs -> Reports in one of the execution, is there a way I can identify which is the job that run that execution which caused the package failed (not by cross checking time of failure from the history of the job and the package failed execution time)?
It is not very straight forward. Based on this stack exchange answer, you may try:
SELECT
history.*
,ex.*
,ex.status
, CASE ex.status
WHEN 1 THEN 'created'
WHEN 2 THEN 'running'
WHEN 3 then 'canceled'
WHEN 4 then 'failed'
WHEN 5 then 'pending'
WHEN 6 then 'ended unexpectedly'
WHEN 7 then 'succeeded'
WHEN 8 then 'stopping'
WHEN 9 then 'completed'
END as job_status
FROM (
SELECT
h.step_name,
-- h.message,
h.run_status,
h.run_date,
h.run_time,
SUBSTRING(h.message, NULLIF(CHARINDEX('Execution ID: ', h.message),0)+14 ,PATINDEX('%[^0-9]%',SUBSTRING(h.message, NULLIF(CHARINDEX('Execution ID: ', h.message),0)+14 ,20))-1) ExecutionId
FROM MSDB.DBO.SYSJOBHISTORY h) history
LEFT JOIN
SSISDB.CATALOG.EXECUTIONS ex on ex.execution_id = history.ExecutionId
WHERE project_name = '<ssisdb_project_name_here>'
It has many columns which you can ignore by replacing * in select. The important part is to join MSDB.DBO.SYSJOBHISTORY with MSDB.DBO.SYSJOBHISTORY.
Also, this works for project deployment mode and not package deployment mode of SSIS.
I have around 40 different sql server jobs in one instance. They all have different schedules. Some run once a day some every two mins some every five mins. If I have a need to stop sql server agent, how can I find the best time when no jobs are running so I won't interrupt any of my jobs?
how can I find the best time when no jobs are running so I won't interrupt any of my jobs?
You basically want to find a good window to perform some maintenance. #MaxVernon has blogged about it here with a handy script
/*
Shows gaps between agent jobs
-- http://www.sqlserver.science/tools/gaps-between-sql-server-agent-jobs/
-- requires SQL Server 2012+ since it uses the LAG aggregate.
Note: On SQL Server 2005, SQL Server 2008, and SQL Server 2008 R2, you could replace the LastEndDateTime column definition with:
LastEndDateTime = (SELECT TOP(1) s1a.EndDateTime FROM s1 s1a WHERE s1a.rn = s1.rn - 1)
*/
DECLARE #EarliestStartDate DATETIME;
DECLARE #LatestStopDate DATETIME;
SET #EarliestStartDate = DATEADD(DAY, -1, GETDATE());
SET #LatestStopDate = GETDATE();
;WITH s AS
(
SELECT StartDateTime = msdb.dbo.agent_datetime(sjh.run_date, sjh.run_time)
, MaxDuration = MAX(sjh.run_duration)
FROM msdb.dbo.sysjobs sj
INNER JOIN msdb.dbo.sysjobhistory sjh ON sj.job_id = sjh.job_id
WHERE sjh.step_id = 0
AND msdb.dbo.agent_datetime(sjh.run_date, sjh.run_time) >= #EarliestStartDate
AND msdb.dbo.agent_datetime(sjh.run_date, sjh.run_time) < = #LatestStopDate
GROUP BY msdb.dbo.agent_datetime(sjh.run_date, sjh.run_time)
UNION ALL
SELECT StartDate = DATEADD(SECOND, -1, #EarliestStartDate)
, MaxDuration = 1
UNION ALL
SELECT StartDate = #LatestStopDate
, MaxDuration = 1
)
, s1 AS
(
SELECT s.StartDateTime
, EndDateTime = DATEADD(SECOND, s.MaxDuration - ((s.MaxDuration / 100) * 100)
+ (((s.MaxDuration - ((s.MaxDuration / 10000) * 10000))
- (s.MaxDuration - ((s.MaxDuration / 100) * 100))) / 100) * 60
+ (((s.MaxDuration - ((s.MaxDuration / 1000000) * 1000000))
- (s.MaxDuration - ((s.MaxDuration / 10000) * 10000))) / 10000) * 3600, s.StartDateTime)
FROM s
)
, s2 AS
(
SELECT s1.StartDateTime
, s1.EndDateTime
, LastEndDateTime = LAG(s1.EndDateTime) OVER (ORDER BY s1.StartDateTime)
FROM s1
)
SELECT GapStart = CONVERT(DATETIME2(0), s2.LastEndDateTime)
, GapEnd = CONVERT(DATETIME2(0), s2.StartDateTime)
, GapLength = CONVERT(TIME(0), DATEADD(SECOND, DATEDIFF(SECOND, s2.LastEndDateTime, s2.StartDateTime), 0))
FROM s2
WHERE s2.StartDateTime > s2.LastEndDateTime
ORDER BY s2.StartDateTime;
The question title scared me a bit - I thought you wanted to programmatically shut the SQL Server agent down anytime there were no jobs running. My answer to that question would be "Why?" There is no need to.
But if you are just looking to do a planned restart or shut down and you don't have a third party tool like Sentry One's SQL Sentry Event Manager to have a visualization, I would just let the SQL Server Agent Job History and Job Activity Monitor help here. The Job Activity monitor can show you which jobs are running right now in the status column. You can also see the last execute and next execute dates and times.
In the object browser in SSMS, connect to your instance, then expand SQL Server Agent, then you'll see Jobs and under that you'll see "Job Activity Monitor" - this view should show you what you need.
Also - don't worry about shutting down before a job executes. If you do that, you will either have that job just missing its schedule and you can let it run when it is next due to (depending on the job and its purpose) or you can manually right click and execute the job.
For more on the activity monitor for jobs, see Monitor Job Activity in the product documentation.
I recommend creating a script that will disable your jobs. Disabled jobs still exist but will not be automatically launched by their schedules. Run this script (based on procedure sp_update_job in the msdb database) to disable jobs, wait for any currently running jobs to finish execution, then stop SQL agent. A similar script to re-enable disabled jobs would be useful. You might need to plan around jobs that are and should remain disabled.
A complete “SQL Agent shutdown” process could be fully scripted, but I question the wisdom of doing so. A bit of research implies that there is no 100% reliable way of programmatically telling if a given job is or is not running, and while there is an undocumented (where "undocumented" means "you really shouldn't be using this") system procedure for stopping and starting services, doing so from with SQL Server itself seems like a pretty bad idea.
You can query the system tables as shown by Dattatrey Sindol in the MSSQLTips.com article Querying SQL Server Agent Job Information:
SELECT
[sJOB].[job_id] AS [JobID]
, [sJOB].[name] AS [JobName]
, [sDBP].[name] AS [JobOwner]
, [sCAT].[name] AS [JobCategory]
, [sJOB].[description] AS [JobDescription]
, CASE [sJOB].[enabled]
WHEN 1 THEN 'Yes'
WHEN 0 THEN 'No'
END AS [IsEnabled]
, [sJOB].[date_created] AS [JobCreatedOn]
, [sJOB].[date_modified] AS [JobLastModifiedOn]
, [sSVR].[name] AS [OriginatingServerName]
, [sJSTP].[step_id] AS [JobStartStepNo]
, [sJSTP].[step_name] AS [JobStartStepName]
, CASE
WHEN [sSCH].[schedule_uid] IS NULL THEN 'No'
ELSE 'Yes'
END AS [IsScheduled]
, [sSCH].[schedule_uid] AS [JobScheduleID]
, [sSCH].[name] AS [JobScheduleName]
, CASE [sJOB].[delete_level]
WHEN 0 THEN 'Never'
WHEN 1 THEN 'On Success'
WHEN 2 THEN 'On Failure'
WHEN 3 THEN 'On Completion'
END AS [JobDeletionCriterion]
FROM
[msdb].[dbo].[sysjobs] AS [sJOB]
LEFT JOIN [msdb].[sys].[servers] AS [sSVR]
ON [sJOB].[originating_server_id] = [sSVR].[server_id]
LEFT JOIN [msdb].[dbo].[syscategories] AS [sCAT]
ON [sJOB].[category_id] = [sCAT].[category_id]
LEFT JOIN [msdb].[dbo].[sysjobsteps] AS [sJSTP]
ON [sJOB].[job_id] = [sJSTP].[job_id]
AND [sJOB].[start_step_id] = [sJSTP].[step_id]
LEFT JOIN [msdb].[sys].[database_principals] AS [sDBP]
ON [sJOB].[owner_sid] = [sDBP].[sid]
LEFT JOIN [msdb].[dbo].[sysjobschedules] AS [sJOBSCH]
ON [sJOB].[job_id] = [sJOBSCH].[job_id]
LEFT JOIN [msdb].[dbo].[sysschedules] AS [sSCH]
ON [sJOBSCH].[schedule_id] = [sSCH].[schedule_id]
ORDER BY [JobName]
I'm trying to find a better way of finding duplicates in SQL Server. This took over 20 minutes to run with just over 300 million records before results started showing in the results window within SSMS. Another 22 minutes elapsed before it crashed.
Then SSMS threw this error after displaying 16,777,216 records:
An error occurred while executing batch. Error message is: Exception of type 'System.OutOfMemoryException' was thrown.
Schema:
ENCOUNTER_NUM - numeric(22,0)
CONCEPT_CD - varchar(50)
PROVIDER_ID - varchar(50)
START_DATE - datetime
MODIFIER_CD - varchar(100)
INSTANCE_NUM - numeric(18,0)
SELECT
ROW_NUMBER() OVER (ORDER BY f1.[ENCOUNTER_NUM],f1.[CONCEPT_CD],f1.[PROVIDER_ID],f1.[START_DATE],f1.[MODIFIER_CD],f1.[INSTANCE_NUM]),
f1.[ENCOUNTER_NUM],
f1.[CONCEPT_CD],
f1.[PROVIDER_ID],
f1.[START_DATE],
f1.[MODIFIER_CD],
f1.[INSTANCE_NUM]
FROM
[dbo].[I2B2_OBSERVATION_FACT] f1
INNER JOIN [dbo].[I2B2_OBSERVATION_FACT] f2 ON
f1.[ENCOUNTER_NUM] = f2.[ENCOUNTER_NUM]
AND f1.[CONCEPT_CD] = f2.[CONCEPT_CD]
AND f1.[PROVIDER_ID] = f2.[PROVIDER_ID]
AND f1.[START_DATE] = f2.[START_DATE]
AND f1.[MODIFIER_CD] = f2.[MODIFIER_CD]
AND f1.[INSTANCE_NUM] = f2.[INSTANCE_NUM]
Not sure how much faster this is, but worth a try.
SELECT
COUNT(*) AS Dupes,
f1.[ENCOUNTER_NUM],
f1.[CONCEPT_CD],
f1.[PROVIDER_ID],
f1.[START_DATE],
f1.[MODIFIER_CD],
f1.[INSTANCE_NUM]
FROM
[dbo].[I2B2_OBSERVATION_FACT] f1
GROUP BY
f1.[ENCOUNTER_NUM],
f1.[CONCEPT_CD],
f1.[PROVIDER_ID],
f1.[START_DATE],
f1.[MODIFIER_CD],
f1.[INSTANCE_NUM]
HAVING
COUNT(*) > 1
This is running on SQL Server 2000
Background: when a new server is brought onto the network, it will pass through a series of status (stages) such as (loading, testing, configuring, production, etc) were production is the final step (not every server in the report will be in production so this bit of information may be a moot point).
I inherited this so if there are any questions, I'll answer them as best as I can. I'm trying to run a query to get the latest status of all the servers during a specific time frame. My query is currently returning every status for the server and I only need the current status and that is where I need your help.
The query I'm working with is as follows:
SELECT SD.ProjectName, SD.SystemName, SD.Status, H.history_id
FROM dbo.SI_SystemDetail AS SD
INNER JOIN dbo.SI_Projects AS P ON SD.ProjectName = P.ProjectName
INNER JOIN dbo.SI_StatusHistory AS H ON SD.SystemName = H.SystemName
WHERE
(P.Cancelled = 'N')
AND (P.Platform LIKE '%ibm%')
AND ('20110101' <= CONVERT(varchar(8), H.EffectiveDate, 112))
AND (CONVERT(varchar(8), H.EffectiveDate, 112) <= '20111111')
ORDER BY
H.history_id DESC, SD.SystemName, SD.ContactSBCuid, SD.ActualLiveDAte DESC,
SD.ProjectName, SD.SystemType, H.EffectiveDate`
This will return several duplicates for systemname but I only need one and that should correspond with the highest history_id number. For example, lets say there is a server named:
Server Name Status History_ID
Server01 Loading 1001
Server01 Configuring 1081
Server01 Testing 1101
Server01 Production 1451
Server02 Loading 1002
Server02 Configuring 1083
Server02 Testing 1104
Server02 Failed 1455
I would just need the following results returned:
Server Name Status History_ID
Server01 Production 1451
Server02 Failed 1455
Thanks in advance for the assistance.
UNTESTED:
SELECT SD.ProjectName, SD.SystemName, SD.Status, H.history_id
FROM dbo.SI_SystemDetail AS SD
INNER JOIN dbo.SI_Projects AS P
ON SD.ProjectName = P.ProjectName
INNER JOIN dbo.SI_StatusHistory AS H
ON SD.SystemName = H.SystemName
WHERE (P.Cancelled = 'N') AND (P.Platform LIKE '%ibm%') AND ('20110101' <= CONVERT(varchar(8), H.EffectiveDate, 112)) AND (CONVERT(varchar(8), H.EffectiveDate, 112) <= '20111111')
AND history_ID = (
Select max(history_ID)
FROM SI_Status_History iSH
where iSH.SystemName = SD.SystemName)
ORDER BY H.history_id DESC, SD.SystemName, SD.ContactSBCuid, SD.ActualLiveDAte DESC, SD.ProjectName, SD.SystemType, H.EffectiveDate
Is there a way to see what is writing to the transaction log?
I have a log file that has grown 15 Gigs in the last 20 minutes. Is there a way for me to track down what is causing this?
Activity monitor will show you what is executing.
DBCC OPENTRAN will show the oldest open transaction.
There is also the dynamic management view sys.dm_tran_active_transactions. For example, here's a query that shows you log file usage by process:
-- This query returns log file space used by all running transactions.
select
SessionTrans.session_id as [SPID],
enlist_count as [Active Requests],
ActiveTrans.transaction_id as [ID],
ActiveTrans.name as [Name],
ActiveTrans.transaction_begin_time as [Start Time],
case transaction_type
when 1 then 'Read/Write'
when 2 then 'Read-Only'
when 3 then 'System'
when 4 then 'Distributed'
else 'Unknown - ' + convert(varchar(20), transaction_type)
end as [Transaction Type],
case transaction_state
when 0 then 'Uninitialized'
when 1 then 'Not Yet Started'
when 2 then 'Active'
when 3 then 'Ended (Read-Only)'
when 4 then 'Committing'
when 5 then 'Prepared'
when 6 then 'Committed'
when 7 then 'Rolling Back'
when 8 then 'Rolled Back'
else 'Unknown - ' + convert(varchar(20), transaction_state)
end as 'State',
case dtc_state
when 0 then NULL
when 1 then 'Active'
when 2 then 'Prepared'
when 3 then 'Committed'
when 4 then 'Aborted'
when 5 then 'Recovered'
else 'Unknown - ' + convert(varchar(20), dtc_state)
end as 'Distributed State',
DB.Name as 'Database',
database_transaction_begin_time as [DB Begin Time],
case database_transaction_type
when 1 then 'Read/Write'
when 2 then 'Read-Only'
when 3 then 'System'
else 'Unknown - ' + convert(varchar(20), database_transaction_type)
end as 'DB Type',
case database_transaction_state
when 1 then 'Uninitialized'
when 3 then 'No Log Records'
when 4 then 'Log Records'
when 5 then 'Prepared'
when 10 then 'Committed'
when 11 then 'Rolled Back'
when 12 then 'Committing'
else 'Unknown - ' + convert(varchar(20), database_transaction_state)
end as 'DB State',
database_transaction_log_record_count as [Log Records],
database_transaction_log_bytes_used / 1024 as [Log KB Used],
database_transaction_log_bytes_reserved / 1024 as [Log KB Reserved],
database_transaction_log_bytes_used_system / 1024 as [Log KB Used (System)],
database_transaction_log_bytes_reserved_system / 1024 as [Log KB Reserved (System)],
database_transaction_replicate_record_count as [Replication Records],
command as [Command Type],
total_elapsed_time as [Elapsed Time],
cpu_time as [CPU Time],
wait_type as [Wait Type],
wait_time as [Wait Time],
wait_resource as [Wait Resource],
reads as [Reads],
logical_reads as [Logical Reads],
writes as [Writes],
SessionTrans.open_transaction_count as [Open Transactions(SessionTrans)],
ExecReqs.open_transaction_count as [Open Transactions(ExecReqs)],
open_resultset_count as [Open Result Sets],
row_count as [Rows Returned],
nest_level as [Nest Level],
granted_query_memory as [Query Memory],
SUBSTRING(SQLText.text,ExecReqs.statement_start_offset/2,(CASE WHEN ExecReqs.statement_end_offset = -1 then LEN(CONVERT(nvarchar(max), SQLText.text)) * 2 ELSE ExecReqs.statement_end_offset end - ExecReqs.statement_start_offset)/2) AS query_text
from
sys.dm_tran_active_transactions ActiveTrans (nolock)
inner join sys.dm_tran_database_transactions DBTrans (nolock)
on DBTrans.transaction_id = ActiveTrans.transaction_id
inner join sys.databases DB (nolock)
on DB.database_id = DBTrans.database_id
left join sys.dm_tran_session_transactions SessionTrans (nolock)
on SessionTrans.transaction_id = ActiveTrans.transaction_id
left join sys.dm_exec_requests ExecReqs (nolock)
on ExecReqs.session_id = SessionTrans.session_id
and ExecReqs.transaction_id = SessionTrans.transaction_id
outer apply sys.dm_exec_sql_text(ExecReqs.sql_handle) AS SQLText
where SessionTrans.session_id is not null -- comment this out to see SQL Server internal processes
If you transaction log has grown so much is such a short time this means that a lot of statements that make data or structure changes have been executed. If your database works with large blob records you can try looking there first.
Profiler won't help you much in finding out what happened previously but it can help you if this is still going on.
If you want to read into transaction log, you will need a 3rd party transaction log reader. The best solution on the market is ApexSQL Log which saved me couple times in similar situations.
However, if your database is running on sql server 2000, you can try to use SQL Log Rescue from Red Gate cause it's free. Thrid solution is to try and find Lumigent Log Explorer (product is discountinued but maybe you can find it somewhere online).
Try'em all and see which one works better for you.
You can use sql server profiler which show every transaction executed and it's start time and end time and many things and i think you can see what causing your problem.
I hope this help you.