Partilhar via


Promoção de propriedade

Se as consultas forem feitas principalmente em um pequeno número de valores de elementos e atributos, você poderá desejar promover essas quantidades em colunas relacionais. Isso é útil quando consultas são emitidas em uma pequena parte dos dados XML enquanto toda a instância XML é recuperada. A criação de um índice XML na coluna XML não é necessária. Em vez disso, a coluna promovida pode ser indexada. As consultas devem ser escritas para usar a coluna promovida. Isto é, o otimizador de consultas não destina consultas novamente na coluna XML para a coluna promovida.

A coluna promovida pode ser uma coluna computada na mesma tabela ou ser uma coluna separada, mantida pelo usuário em uma tabela. Isso é suficiente quando valores singleton são promovidos de cada instância XML. No entanto para propriedades com vários valores, é necessário criar uma tabela separada para a propriedade, conforme descrito na seção a seguir.

Coluna computada com base no tipo de dados xml

Uma coluna computada pode ser criada usando uma função definida pelo usuário que invoca métodos de tipo de dados xml. O tipo da coluna computada pode ser qualquer tipo SQL, inclusive XML. Isso é ilustrado no exemplo a seguir.

Exemplo: Coluna computada com base no método do tipo de dados xml

Crie a função definida pelo usuário para um número ISBN de livro:

CREATE FUNCTION udf_get_book_ISBN (@xData xml)
RETURNS varchar(20)
BEGIN
   DECLARE @ISBN   varchar(20)
   SELECT @ISBN = @xData.value('/book[1]/@ISBN', 'varchar(20)')
   RETURN @ISBN 
END

Adicione uma coluna computada à tabela para o ISBN:

ALTER TABLE      T
ADD   ISBN AS dbo.udf_get_book_ISBN(xCol)

A coluna computada pode ser indexada de maneira normal.

Exemplo: Consultas em uma coluna computada com base em métodos de tipo de dados xml

Para obter o <book> cujo ISBN é 0-7356-1588-2:

SELECT xCol
FROM   T
WHERE  xCol.exist('/book/@ISBN[. = "0-7356-1588-2"]') = 1

A consulta na coluna XML pode ser reescrita para usar a coluna computada da seguinte maneira:

SELECT xCol
FROM   T
WHERE  ISBN = '0-7356-1588-2'

É possível criar uma função definida pelo usuário para retornar o tipo de dados xml e uma coluna computada usando a função definida pelo usuário. No entanto não é possível criar um índice XML na coluna XML computada.

Criando tabelas de propriedades

Você pode desejar promover algumas das propriedades com vários valores de seus dados XML em uma ou mais tabelas, criar índices nessas tabelas e destinar suas consultas novamente para usá-las. Um cenário típico é aquele em que um pequeno número propriedades cobre a maior parte da carga de trabalho das consultas. É possível fazer o seguinte:

  • Crie uma ou mais tabelas para manter as propriedades com vários valores. Você pode descobrir que é conveniente armazenar uma propriedade por tabela e duplicar a chave primária da tabela base nas tabelas de propriedades para junção retroativa com a tabela base.

  • Para manter a ordem relativa das propriedades, você precisa introduzir uma coluna separada para a ordem relativa.

  • Crie gatilhos na coluna XML para manter as tabelas de propriedades. Dentro dos gatilhos, proceda de uma das seguintes maneiras:

    • Use métodos de tipo de dados xml, como nodes() e value(), para inserir e excluir linhas das tabelas de propriedades.

    • Crie funções com valor de tabela de streaming no CLR (Common Language Runtime) para inserir e excluir linhas das tabelas de propriedades.

    • Escreva consultas para acesso do SQL às tabelas de propriedades e para acesso do XML à coluna XML na tabela base, com junções entre as tabelas usando suas chaves primárias.

Exemplo: Crie uma tabela de propriedades

Para ilustração, assuma que você quer promover o nome dos autores. Os livros têm um ou mais autores, de forma que nome é uma propriedade com vários valores. Cada nome é armazenado em uma linha separada de uma tabela de propriedades. A chave primária da tabela base é duplicada na tabela de propriedades para junção retroativa.

create table tblPropAuthor (propPK int, propAuthor varchar(max))

Exemplo: Crie uma função definida pelo usuário para gerar um conjunto de linhas de uma instância XML

A função com valor de tabela a seguir, udf_XML2Table, aceita um valor de chave primária e uma instância XML. Ela recupera o nome de todos os autores dos elementos de <book> e retorna um conjunto de linhas de chave primária, primeiros pares de nomes.

create function udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (propPK int, propAuthor varchar(max))
with schemabinding
as
begin
      insert into @ret_Table 
      select @pk, nref.value('.', 'varchar(max)')
      from   @xCol.nodes('/book/author/first-name') R(nref)
      return
end

O gatilho de inserção insere linhas na tabela de propriedades:

create trigger trg_docs_INS on T for insert
as
      declare @wantedXML xml
      declare @FK int
      select @wantedXML = xCol from inserted
      select @FK = PK from inserted

   insert into tblPropAuthor
   select * from dbo.udf_XML2Table(@FK, @wantedXML)

O gatilho de exclusão exclui as linhas da tabela de propriedades com base no valor da chave primária das linhas excluídas:

create trigger trg_docs_DEL on T for delete
as
   declare @FK int
   select @FK = PK from deleted
   delete tblPropAuthor where propPK = @FK

O gatilho de atualização exclui as linhas existentes da tabela de propriedades correspondentes à instância XML atualizada e insere novas linhas na tabela de propriedades:

create trigger trg_docs_UPD
on T
for update
as
if update(xCol) or update(pk)
begin
      declare @FK int
      declare @wantedXML xml
      select @FK = PK from deleted
      delete tblPropAuthor where propPK = @FK

   select @wantedXML = xCol from inserted
   select @FK = pk from inserted

   insert into tblPropAuthor 
      select * from dbo.udf_XML2Table(@FK, @wantedXML)
end

Exemplo: Localize instâncias XML cujos autores têm o mesmo nome

A consulta pode ser formada na coluna XML. Como alternativa, é possível pesquisar o nome "David" na tabela de propriedades e executar uma junção retroativa com a tabela base para retornar a instância XML. Por exemplo:

SELECT xCol 
FROM     T JOIN tblPropAuthor ON T.pk = tblPropAuthor.propPK
WHERE    tblPropAuthor.propAuthor = 'David'

Exemplo: Solução usando a função com valor de tabela de streaming de CLR

Esta solução é composta das seguintes etapas:

  1. Definir uma classe CLR, SqlReaderBase, que implementa ISqlReader e gera um streaming, saída com valor de tabela, aplicando uma expressão de caminho em uma instância XML.

  2. Crie um assembly e uma função definida pelo usuário de Transact-SQL para iniciar a classe CLR.

  3. Defina os gatilhos de inserção, atualização e exclusão usando a função definida pelo usuário para manter uma tabela de propriedades.

Para fazer isso, primeiro crie a função CLR de streaming. O tipo de dados xml é exposto como um SqlXml de classe gerenciada no ADO.NET e oferece suporte ao método CreateReader() que retorna um XmlReader.

ObservaçãoObservação

O código de exemplo nesta seção usa XPathDocument e XPathNavigator. Isso força você a carregar todos os documentos XML na memória. Se estiver usando código semelhante em seu aplicativo para processar vários documentos XML grandes, esse código não será escalável. Em vez disso, mantenha as alocações de memória pequenas e use interfaces de streaming sempre que possível. Para obter mais informações sobre desempenho, consulte Arquitetura da integração CLR.

public class c_streaming_xml_tvf {
   public static ISqlReader streaming_xml_tvf 
(SqlXml xmlDoc, string pathExpression) {
      return (new TestSqlReaderBase (xmlDoc, pathExpression));
   }
}

// Class that implements ISqlReader
public class TestSqlReaderBase : ISqlReader {
XPathNodeIterator m_iterator;         
   public SqlChars FirstName;
// Metadata for current resultset
private SqlMetaData[] m_rgSqlMetaData;      

   public TestSqlReaderBase (SqlXml xmlDoc, string pathExpression) {   
      // Variables for XPath navigation
      XPathDocument xDoc;
      XPathNavigator xNav;
      XPathExpression xPath;
   
      // Set sql metadata
      m_rgSqlMetaData = new SqlMetaData[1];
      m_rgSqlMetaData[0] = new SqlMetaData ("FirstName",  
SqlDbType.NVarChar,50);   
   
      //Set up the Navigator
      if (!xmlDoc.IsNull)
          xDoc = new XPathDocument (xmlDoc.CreateReader());
      else
          xDoc = new XPathDocument ();
      xNav = xDoc.CreateNavigator();
      xPath = xNav.Compile (pathExpression);
      m_iterator = xNav.Select(xPath);
   }
   public bool Read() {
      bool moreRows = true;
      if (moreRows = m_iterator.MoveNext())
         FirstName = new SqlChars (m_iterator.Current.Value);
      return moreRows;
   }
}

Em seguida, crie um assembly e uma função definida pelo usuário Transact-SQL, SQL_streaming_xml_tvf (não mostrada), que corresponda à função CLR, streaming_xml_tvf. A função definida pelo usuário é usada para definir a função com valor de tabela, CLR_udf_XML2Table, para geração do conjunto de linhas:

create function CLR_udf_XML2Table (@pk int, @xCol xml)
returns @ret_Table table (FK int, FirstName varchar(max))
with schemabinding
as
begin
      insert into @ret_Table 
   select @pk, FirstName 
   FROM   SQL_streaming_xml_tvf (@xCol, '/book/author/first-name')
      return
end

Finalmente, defina gatilhos conforme mostrado no exemplo "Crie gatilhos para popular uma tabela de propriedades", mas substitua a função udf_XML2Table pela CLR_udf_XML2Table. O gatilho de inserção é mostrado no exemplo a seguir:

create trigger CLR_trg_docs_INS on T for insert
as
   declare @wantedXML xml
   declare @FK int
   select @wantedXML = xCol from inserted
   select @FK = PK from inserted

   insert into tblPropAuthor
      select *
   from    dbo.CLR_udf_XML2Table(@FK, @wantedXML)

O gatilho de inserção é idêntico à versão de não-CLR. No entanto o gatilho de atualização apenas substitui a função udf_XML2Table() pela CLR_udf_XML2Table().

Consulte também

Conceitos