Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

A melhor maneira de fragmentar dados XML em colunas de banco de dados SQL Server


Me deparei com essa pergunta enquanto tinha um problema muito semelhante, eu estava executando uma consulta processando um arquivo XML de 7,5 MB (aproximadamente 10.000 nós) por cerca de 3,5 a 4 horas antes de finalmente desistir.

No entanto, depois de um pouco mais de pesquisa, descobri que, digitando o XML usando um esquema e criando um índice XML (eu inseriria em massa em uma tabela), a mesma consulta foi concluída em ~ 0,04ms.

Como é isso para uma melhoria de desempenho!

Código para criar um esquema:
IF EXISTS ( SELECT * FROM sys.xml_schema_collections where [name] = 'MyXmlSchema')
DROP XML SCHEMA COLLECTION [MyXmlSchema]
GO

DECLARE @MySchema XML
SET @MySchema = 
(
    SELECT * FROM OPENROWSET
    (
        BULK 'C:\Path\To\Schema\MySchema.xsd', SINGLE_CLOB 
    ) AS xmlData
)

CREATE XML SCHEMA COLLECTION [MyXmlSchema] AS @MySchema 
GO

Código para criar a tabela com uma coluna XML digitada:
CREATE TABLE [dbo].[XmlFiles] (
    [Id] [uniqueidentifier] NOT NULL,

    -- Data from CV element 
    [Data] xml(CONTENT dbo.[MyXmlSchema]) NOT NULL,

CONSTRAINT [PK_XmlFiles] PRIMARY KEY NONCLUSTERED 
(
    [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]

Código para criar Índice
CREATE PRIMARY XML INDEX PXML_Data
ON [dbo].[XmlFiles] (Data)

Há algumas coisas a ter em mente embora. A implementação do Schema do SQL Server não oferece suporte a xsd:include. Isso significa que, se você tiver um esquema que faz referência a outro esquema, precisará copiar todos eles em um único esquema e adicioná-lo.

Também receberia um erro:
XQuery [dbo.XmlFiles.Data.value()]: Cannot implicitly atomize or apply 'fn:data()' to complex content elements, found type 'xs:anyType' within inferred type 'element({http://www.mynamespace.fake/schemas}:SequenceNumber,xs:anyType) ?'.

se eu tentasse navegar acima do nó que havia selecionado com a função nodes. Por exemplo.
SELECT
    ,C.value('CVElementId[1]', 'INT') AS [CVElementId]
    ,C.value('../SequenceNumber[1]', 'INT') AS [Level]
FROM 
    [dbo].[XmlFiles]
CROSS APPLY
    [Data].nodes('/CVSet/Level/CVElement') AS T(C)

Descobriu que a melhor maneira de lidar com isso era usar o OUTER APPLY para executar uma "junção externa" no XML.
SELECT
    ,C.value('CVElementId[1]', 'INT') AS [CVElementId]
    ,B.value('SequenceNumber[1]', 'INT') AS [Level]
FROM 
    [dbo].[XmlFiles]
CROSS APPLY
    [Data].nodes('/CVSet/Level') AS T(B)
OUTER APPLY
    B.nodes ('CVElement') AS S(C)

Espero que isso ajude alguém, pois esse tem sido o meu dia.