我目前正在探索SQL服务器XML列和选择性索引以满足我们的需求。为此,我创建了一个名为Incidents
的表,并创建了Selective Index
和Secondary selective Indexes
(下面的脚本)。
当我运行以下查询时,它确实使用了选择性索引,但查询计划对Severity列数据执行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)')
答案 0 :(得分:1)
由于XPATH查询中的[1],需要SORT TOP N.要摆脱这种情况,您需要确保SQL Server所需的xml元素仅在事件元素中出现一次。为此,您需要使用XSD文档强烈键入XML。你可以这样创建一个:
CREATE XML SCHEMA COLLECTION Incident_XSD AS
N'<?xml version="1.0" encoding="UTF-16"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Incident">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:int" name="_id" />
<xs:element type="xs:int" name="Severity" />
<xs:element type="xs:string" name="OwningTeamId" />
<xs:element type="xs:string" name="OwningTenantId" />
<xs:element type="xs:string" name="IncidentStatus"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>' ;
GO
在表定义中使用它,如此
[Data] [xml](Incident_XSD) NULL
现在以下查询有效
select TOP 100 Data.value('/Incident[1]/Severity', 'int') AS Severity
FROM XmlTable
WHERE Data.value('/Incident[1]/Severity', 'int') = 1
ORDER BY Data.value('/Incident[1]/OwningTenantId', 'NVARCHAR(800)')
在一秒钟内返回,或在表格中以毫秒行返回。
PS:您可能想重新考虑使用GUID作为主键