【发布时间】:2015-01-18 17:09:06
【问题描述】:
我目前正在探索 SQL Server XML 列和选择性索引以满足我们的需要。为此,我创建了名为Incidents 的表并创建了Selective Index 和Secondary selective Indexes(下面的脚本)。
当我运行以下查询时,它确实使用了选择性索引,但查询计划对严重性列数据执行IS NOT NULL 谓词,然后对其进行排序。当表中的数据很大时,这会显着降低查询的性能。我已经看到表中有 400 万行,完成以下查询需要大约 20 秒。
我在这里遗漏了什么吗?
select TOP 100 Data.value('(/Incident/Severity)[1]', 'int') AS Severity,
Data.value('(/Incident/OwningTenantId)[1]', 'VARCHAR(800)') AS OwningTenantId,
Data.value('(/Incident/OwningTeamId)[1]', 'NVARCHAR(800)') AS OwningTeamId
FROM Incidents
WHERE Data.value('(/Incident/Severity)[1]', 'int') = 1
ORDER BY Data.value('(/Incident/OwningTenantId)[1]', 'NVARCHAR(800)')
索引:
CREATE TABLE [dbo].[Incidents](
[id] [uniqueidentifier] NOT NULL,
[Data] [xml] NOT NULL,
CONSTRAINT [PK_Incidents] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE SELECTIVE XML INDEX sxi_Incident_Data ON Incidents(Data)
FOR
(
Severity = '/Incident/Severity' AS SQL int SINGLETON,
OwningTeamId = '/Incident/OwningTeamId' AS SQL NVARCHAR(400) SINGLETON,
OwningTenantId = '/Incident/OwningTenantId' AS SQL NVARCHAR(400) SINGLETON,
id = '/Incident/_id' AS SQL BIGINT SINGLETON
)
GO
create xml index sxi_secondary_severity on Incidents(Data)
using xml index sxi_Incident_Data
for (Severity);
GO
create xml index sxi_secondary_OwningTeamId on Incidents(Data)
using xml index sxi_Incident_Data
for (OwningTeamId);
GO
create xml index sxi_secondary_OwningTenantId on Incidents(Data)
using xml index sxi_Incident_Data
for (OwningTenantId);
GO
create xml index sxi_secondary_Id on Incidents(Data)
using xml index sxi_Incident_Data
for (id);
GO
示例 XML:
<Incident>
<_id>123</_id>
<Severity>3</Severity>
<IncidentStatus>RESOLVED</IncidentStatus>
<CreateDate>2014-05-04 05:43:58.317</CreateDate>
<LastUpdateDate>2014-05-06 18:47:39.037</LastUpdateDate>
<AlertSourceLocalId>20070</AlertSourceLocalId>
<SourceIncidentId>35d0bfe4-ccb9-491f-a30c-ea7685ffe8c0</SourceIncidentId>
<SourceCreateDate>2014-05-04 02:51:14.000</SourceCreateDate>
<SourceCreatedBy>Someone</SourceCreatedBy>
<SourceModifiedDate>2014-05-04 05:43:57.797</SourceModifiedDate>
<SourceOrigin>Some Origin</SourceOrigin>
<CorrelationId>correlatioid</CorrelationId>
<RoutingId>Route123</RoutingId>
<Datacenter>Unknown</Datacenter>
<Environment>INT</Environment>
<DeviceGroup>Devicegroup</DeviceGroup>
<DeviceName>DeviceName</DeviceName>
<RaisingEnvironment>PROD</RaisingEnvironment>
<RaisingDatacenter>Unknown</RaisingDatacenter>
<RaisingDeviceGroup>DEviceGroup</RaisingDeviceGroup>
<RaisingDeviceName>FakeDevice</RaisingDeviceName>
<PrimaryIncidentId>1234</PrimaryIncidentId>
<RelatedLinksCount>0</RelatedLinksCount>
<ExternalLinksCount>0</ExternalLinksCount>
<HitCount>0</HitCount>
<ChildCount>0</ChildCount>
<Title>Some Title</Title>
<ReproSteps>�</ReproSteps>
<OwningTenantId>564</OwningTenantId>
<OwningTeamId>123</OwningTeamId>
<ResolveDate>2014-05-06 18:47:39.037</ResolveDate>
<ResolvedBy>SomeOne</ResolvedBy>
<MitigateDate>2014-05-06 18:45:55.403</MitigateDate>
<MitigatedBy>Someone</MitigatedBy>
<Mitigation>N/A</Mitigation>
<IsNoise>0</IsNoise>
<IsSecurityRisk>0</IsSecurityRisk>
<IsCustomerImpacting>0</IsCustomerImpacting>
<OriginatingTenantId>10066</OriginatingTenantId>
<ImpactStartDate>2014-05-01 23:31:22.000</ImpactStartDate>
<RootCauseNeedsInvestigation>0</RootCauseNeedsInvestigation>
<ConnectorTenantId>10066</ConnectorTenantId>
<RelationshipId>1852546</RelationshipId>
<SuppressAutoUpdate>0</SuppressAutoUpdate>
</Incident>
复制: 创建表索引
-- Create Table
IF(EXISTS(SELECT * FROM sys.tables WHERE [Name] = 'XmlTable' AND [Type] = 'U'))
BEGIN
DROP TABLE XmlTable
END
CREATE TABLE [dbo].[XmlTable](
[id] [uniqueidentifier] NOT NULL,
[Data] [xml] NULL
PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
-- Populate Data
DECLARE @i INT = 0
DECLARE @XML NVARCHAR(MAX),
@Severity INT,
@OwningTeamId VARCHAR(400),
@OwningTenantId VARCHAR(400),
@IncidentStatus varchar(100),
@Mod SMALLINT
WHILE @i < 500
BEGIN
SET @i = @i + 1
SET @Mod = @i % 3
SELECT @Severity = @Mod + 1,
@OwningTeamId = 'OwningTeam' + CAST(@Mod AS VARCHAR),
@OwningTenantId = 'OwningTenantId' + CAST(@Mod AS VARCHAR),
@IncidentStatus = CASE @Mod
WHEN 0 THEN 'Active'
WHEN 1 THEN 'Resolved'
WHEN 2 THEN 'Closed'
END
SET @XML =
'<Incident>' +
'<_id>' + CAST(@i AS VARCHAR) + '</_id>' +
'<Severity>' + CAST(@Severity AS VARCHAR) + '</Severity>' +
'<OwningTeamId>' + @OwningTeamId + '</OwningTeamId>' +
'<OwningTenantId>' + @OwningTenantId + '</OwningTenantId>' +
'<IncidentStatus>' + @IncidentStatus + '</IncidentStatus>' +
'</Incident>'
INSERT INTO XmlTable
SELECT NEWID(), @XML
END
-- Creat Indices
CREATE SELECTIVE XML INDEX [sxi_Data] ON [dbo].[XmlTable]
(
[Data]
)
FOR
(
[Severity] = '/Incident/Severity' as SQL [int] SINGLETON ,
[OwningTeamId] = '/Incident/OwningTeamId' as SQL [nvarchar](400) SINGLETON ,
[OwningTenantId] = '/Incident/OwningTenantId' as SQL [nvarchar](400) SINGLETON ,
[id] = '/Incident/_id' as SQL [bigint] SINGLETON ,
[TicketStatus] = '/Incident/IncidentStatus' as SQL [nvarchar](100) SINGLETON
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
CREATE XML INDEX [sxi_secondary_Id] ON [dbo].[XmlTable]
(
[Data]
)USING XML INDEX [sxi_Data] FOR (
[id]
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
CREATE XML INDEX [sxi_secondary_OwningTeamId] ON [dbo].[XmlTable]
(
[Data]
)USING XML INDEX [sxi_Data] FOR (
[OwningTeamId]
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
USE [XMLDocuemntStore]
GO
CREATE XML INDEX [sxi_secondary_OwningTenantId] ON [dbo].[XmlTable]
(
[Data]
)USING XML INDEX [sxi_Data] FOR (
[OwningTenantId]
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
USE [XMLDocuemntStore]
GO
CREATE XML INDEX [sxi_secondary_severity] ON [dbo].[XmlTable]
(
[Data]
)USING XML INDEX [sxi_Data] FOR (
[Severity]
)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
GO
示例查询:检查右侧的查询计划。
select TOP 100 Data.value('(/Incident/Severity)[1]', 'int') AS Severity
FROM XmlTable
WHERE Data.value('(/Incident/Severity)[1]', 'int') = 1
ORDER BY Data.value('(/Incident/OwningTenantId)[1]', 'NVARCHAR(800)')
【问题讨论】:
-
这是什么版本的 SQL Server? 2012 年还是 2014 年?
-
有人吗?我需要了解这种行为以及为什么查询计划如此不影响大量记录的性能?
-
首先,目前还没有很多关于 XML 选择性索引的经验。其次,我认为我们需要查看两个查询计划来尝试理解这一点:一种是它现在在 20 秒超过 400 万行的情况下工作的方式,另一种方式是它没有进行这种比较并按您的预期执行。或者,如果您可以向我们提供一些代码或 SQL Fiddle 来重现该行为,我们或许可以提供帮助。
-
我已经用脚本编辑了原始问题来解决问题
-
HI RBarryYoung,代码 sn-p 是否有助于重现问题...
标签: sql-server tsql sql-server-2012 sql-execution-plan