3

我正在使用 SQL Server 2012

我有一个长时间运行的扩展事件(运行数天以捕获事件)保存到 .xel 文件。

我有一项定期运行以将数据导入临时表的工作。我只是event_data从文件中导入 XML 列,因此我可以解析出我需要的 XML 字段并保存到表中以进行报告。

我知道我上次运行导入的时间是什么时候,所以我想看看我是否只能从文件中选择自上次运行导入过程以来添加的记录。

我现在可以使用它,但是它将文件中的所有记录导入临时表,解析出我需要的字段(包括时间戳),然后仅导入自上次运行作业以来具有时间戳的记录。

自上次作业运行以来,我的流程仅插入新的,因此一切正常,但它为文件中的所有记录导入和解析 XML 做了很多工作,包括我上次运行作业时已经导入的那些.

所以我想找到一种方法,如果文件已经导入,则根本不从文件中导入,或者至少不必为已导入的记录解析 XML(尽管我现在必须解析它以获取时间戳排除已经处理的)。

下面是我所拥有的,正如我所说,它可以工作,但是如果我能找到一种方法来跳过我已经导入的那些,它会做很多额外的工作。

我只包括了我需要帮助的流程步骤:

-- pull data from file path and insert into staging table
INSERT INTO #CaptureObjectUsageFileData (event_data)
SELECT cast(event_data as XML) as event_data
FROM sys.fn_xe_file_target_read_file(@FilePathNameToImport, null, null, null)


-- parse out the data needed (only columns using) and insert into temp table for parsed data
INSERT INTO #CaptureObjectUsageEventData (EventTime, EventObjectType, EventObjectName)
SELECT n.value('(@timestamp)[1]', 'datetime') AS [utc_timestamp],
n.value('(data[@name="object_type"]/text)[1]', 'varchar(500)') AS ObjectType,
n.value('(data[@name="object_name"]/value)[1]', 'varchar(500)') as ObjectName
from (
    SELECT event_data
    FROM #CaptureObjectUsageFileData (NOLOCK)
) ed
CROSS apply ed.event_data.nodes('event') as q(n)


-- select from temp table as another step for speed/conversion
--  converting the timestamp to smalldatetime so it doesnt get miliseconds so when we select distinct it wont have lots of dupes
INSERT INTO DBALocal.dbo.DBObjectUsageTracking(DatabaseID, ObjectType, ObjectName, ObjectUsageDateTime)
SELECT DISTINCT @DBID, EventObjectType, EventObjectName, CAST(EventTime AS SMALLDATETIME)
FROM #CaptureObjectUsageEventData
WHERE EventTime > @LastRunDateTime
4

2 回答 2

1

好的,我已经发表了评论,但是 - 在深入思考并查看您的代码之后 - 这可能相当简单:

您可以存储上次导入的时间并使用谓词in .nodes()(就像您这样做.value()以获得正确的<data>元素)。

尝试这样的事情:

DECLARE @LastImport DATETIME=GETDATE(); --put the last import's time here

and then

CROSS apply ed.event_data.nodes('event[@timestamp cast as xs:dateTime? > sql:variable("@LastImport")]') as q(n)

这样做,.nodes()应该只返回<event>满足条件的元素。如果这没有帮助,请展示一些简化的 XML 示例以及您想要获得的内容。

于 2019-01-25T19:01:46.420 回答
1

上面接受的答案,但发布了我有问题的部分的代码,其中包含我所做的评论/修复的更新(同样不是整个代码),但重要的部分。使用@Shnugo帮助我能够从我的流程中完全删除一个临时表,我需要在插入我的永久表之前进行日期过滤,他的回答我可以直接插入到永久表中。在我测试的小数据集中,更新和删除额外代码将运行时间减少了 1/3。随着我获得的数据越多,这种改进将产生更大的影响。

这旨在长时间运行扩展事件会话。它会告诉我正在使用哪些对象(稍后查询系统表)来告诉我哪些对象没有被使用。请参阅下面的扩展事件生成代码:我正在获取有关以下内容的信息:sp_statement_starting 并且仅获取 SP 和函数事件并且仅保存对象名称、类型和时间戳我没有保存 SQL 文本,因为我的目的不需要它。

sp_statement_starting 在存储过程中提取每个语句,因此当 SP 运行时,它可能有 1-100 个语句启动事件,并将那么多记录插入到文件中(这比我的目的所需的数据多得多)。

在我将文件导入临时表后的代码中,我将时间戳缩短为 shortdatetime 并从文件中的所有记录中选择不同的值

我这样做是因为它为 SP 中的每个语句插入一条记录,将数据缩短为 shortdatetime 并选择 distinct 大大减少了插入记录的麻烦。

我知道我可以只保留对象名称,只插入唯一值并完全忽略时间,但我想大致了解它们被调用的频率。

CREATE EVENT SESSION [CaptureObjectUsage_SubmissionEngine] ON SERVER 
ADD EVENT sqlserver.sp_statement_starting(
    -- collect object name but NOT statement, thats not needed
    SET collect_object_name=(1),
    collect_statement=(0)
    WHERE (
    -- this is for functions or SP's
        (
            -- functions
            [object_type]=(8272) 
            -- SProcs
            OR [object_type]=(20038)
        ) 
        AND [sqlserver].[database_name]=N'DBNAMEHERE' 
        AND [sqlserver].[is_system]=(0))
    ) 
ADD TARGET package0.event_file( 
    SET filename=N'c:\Path\CaptureObjectUsage.xel'  -- mine that was default UI gave me
)
WITH (MAX_MEMORY=4096 KB,EVENT_RETENTION_MODE=ALLOW_SINGLE_EVENT_LOSS,MAX_DISPATCH_LATENCY=30 SECONDS,MAX_EVENT_SIZE=0 KB,MEMORY_PARTITION_MODE=NONE,TRACK_CAUSALITY=OFF,STARTUP_STATE=OFF)
GO




-- ***************************************************************************
--      code for importing
-- ***************************************************************************

-- pull data from file path and insert into staging table
INSERT INTO #CaptureObjectUsageFileData (event_data)
SELECT cast(event_data as XML) as event_data
FROM sys.fn_xe_file_target_read_file(@FilePathNameToImport, null, null, null)


-- with the XML.nodes parsing I can insert directly into my final table because it does the logic here
INSERT INTO DBALocal.dbo.DBObjectUsageTracking(DatabaseID, ObjectType, ObjectName, ObjectUsageDateTime)
SELECT DISTINCT @DBID, -- @DBID is variable I set above so I dont need to use DBNAME and take up a ton more space
n.value('(data[@name="object_type"]/text)[1]', 'varchar(500)') AS ObjectType,
n.value('(data[@name="object_name"]/value)[1]', 'varchar(500)') as ObjectName,
CAST(n.value('(@timestamp)[1]', 'datetime') AS SMALLDATETIME) AS [utc_timestamp]
from (
    SELECT event_data
    FROM #CaptureObjectUsageFileData (NOLOCK)
) ed
-- original  before adding the .node logic
--CROSS apply ed.event_data.nodes('event') as q(n)
-- updated to reduce amount of data to import
CROSS apply ed.event_data.nodes('event[@timestamp cast as xs:dateTime? > sql:variable("@LastRunDateTime")]') as q(n)
于 2019-01-25T20:02:23.660 回答