Recentemente precisei criar um relatório no iReport que seria impresso em uma folha de etiquetas.


O PROBLEMA


Existe um campo na tabela que contém a quantidade de etiquetas que o usuário quer que seja impresso do item que ele buscou. 
Precisei criar uma query para repetir a linha de acordo com o valor do campo de quantidade para enviar para o iReport apenas imprimir.

DADOS

Tabela: etiquetas
Campos usados na etiqueta:  descricao e preco_item

Query:

with rep as
 (select level as rnk
    from dual
  connect by level <= (select max(qtd)
                         from etiquetas))
select v.descricao,
       v.preco_item
  from etiquetas v,
       rep r
 where r.rnk <= v.qtd
 order by v.qtd, r.rnk;


Tive alguns problemas relacionado a extração de blocos e valores dentro de um campo Clob que continha uma estrutura XML de nota fiscal eletrônica. Serei direto e listarei os problemas e como os solucionei usando algumas funções nativas do Oracle (minha release é a 11.2.0.4.0)


1 - Extração de blocos internos de um XML

O XML gravado no campo Clob tinha uma estrutura diferente da qual eu precisava exportar, o bloco que me interessava estava dentro dessa estrutura, mais precisamente no segundo nível do XML, precisava de tudo que havia dentro da tag <NFe>:

<?xml version="1.0" encoding="UTF-8" ?>
<nfeProc versao="2.00xmlns="http://www.portalfiscal.inf.br/nfe">
    <NFe xmlns="http://www.portalfiscal.inf.br/nfe">
        <infNFe Id="NFe000000000000000000000000000000000000versao="2.00">
            <ide>
                <cUF>52</cUF>
                <cNF>00010000</cNF>
                    (...)
            </ide>
        </infNFe>
    </NFe>
<protNFe versao="2.00"></protNFe>
</nfeProc>

O select que resolveu o problema da extração do bloco de dentro do XML do Clob foi esse:

select extract(xmltype(n.xml_autorizado),
               '/nfeProc/NFe',
               'xmlns="http://www.portalfiscal.inf.br/nfe"')
  from notas_fiscais_eletronicas n
 where n.nfe_sequencial = 42;


O primeiro parâmetro da função extract é um xmltype, então precisamos converter o campo Clob para xmltype e fazemos isso usando a função xmltype. O segundo parâmetro é a string com o XPath onde indicamos o nó do XML que queremos. O terceiro e último parâmetro é onde indicamos a string com o namespace dos nós que indicamos no XPath do segundo parâmetro.

É importante entender um pouco como o XPath funciona:

XPath  
Descrição
/
Indica a raiz da árvore em uma expressão XPath. Por exemplo,/nfeProc refere-se ao filho do nó raiz, cujo nome é nfeProc
/
Também é usado como um separador de caminho para identificar o nó filho de qualquer nó. Por exemplo, /nfeProc/NFe identifica o elementro de nome NFe do nfeProc, um filho do elemento raiz.
//
Usado para identificar todos os descendentes do nó atual. Por exemplo,/nfeProc/NFe/infNFe//ICMS corresponde a qualquer elemento ICMSsob o elemento infNFe.
*
Usado como um curinga para corresponder a qualquer nó filho. Por exemplo, /nfeProc/*/infNFe corresponde a qualquer elemento infNFe que é neto do elemento nfeProc.
[ ]
Usada para indicar expressões de predicado. XPath suporta uma rica lista de operadores binários, como orand, e not. Por exemplo,/nfeProc/NFe/infNFe/det/prod[CFOP = 5405 and uTrib = "PC"]/vProd seleciona o elemento Valor do Produto de todos os produtos cujo número do CFOP é 5405 e que a Unidade Tributável seja PC.

Colchetes também são utilizados para indicar uma posição (índice). Por exemplo, /nfeProc/NFe/infNFe/det[2] identifica o segundo item da nota sob o elemento pai infNFe.

Se precisar filtrar por valor de propriedade de uma tag, use arroba em frente, exemplo: /nfeProc/NFe/infNFe/det[@nItem = 5] buscará a o nó det cujo número é 5 <det nItem="5">.

Até agora só usamos exemplos com apenas um namespace. Quando o XML tem vários outros namespace precisamos indica-los no terceiro parâmetro da função extract dependendo do nó que estamos extraindo.

<?xml version="1.0" encoding="UTF-8" ?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Header>
    <nfeCabecMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NfeAutorizacao">
        <cUF>42</cUF>
        <versaoDados>3.10</versaoDados>
    </nfeCabecMsg>
</env:Header>
<env:Body>
    <nfeDadosMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NfeAutorizacao">
        <enviNFe versao="3.10xmlns="http://www.portalfiscal.inf.br/nfe">
            <idLote>10000000</idLote>
            <indSinc>1</indSinc>
            <NFe xmlns="http://www.portalfiscal.inf.br/nfe">
                <infNFe Id="NFe0000000000000000000000versao="3.10">
                    <ide>
                        <cUF>42</cUF>
                        <cNF>00080000</cNF>
                (...)
                </infNFe>
            </NFe>
        </enviNFe>
    </nfeDadosMsg>
</env:Body>
</env:Envelope>


Assim como o primeiro exemplo, quero obter os dados a partir do nó<NFe> , porém existem três namespace da tag raiz até a tag <NFe>. A query fica assim:

 select extract(xmltype(n.xml_autorizado),
       '/env:Envelope/env:Body/nfeDadosMsg/nota:enviNFe/nota:NFe',
       'xmlns:env="http://www.w3.org/2003/05/soap-envelope"
        xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NfeAutorizacao"
        xmlns:nota="http://www.portalfiscal.inf.br/nfe"')
  from notas_fiscais_eletronicas n
 where n.nfe_sequencial = 42;


A estrutura do select é a mesma usada no exemplo anterior, porém percebemos que o terceiro parâmetro da função extract que compõe os namespace não necessita de caractere separador, é necessário apenas separar cada namespace com espaço ou quebra-de-linha. O XPath funciona da mesma forma que no exemplo anterior.


2 – Converter o XMLTYPE para Clob novamente

Podemos usar as funções getClobVal(), getStringVal(), getNumberVal() ou getBlobVal(csid) para converter XML em CLOB, VARCHAR, NUMBER, ou BLOB, respectivamente. Usando o select do primeiro exemplo para retornar um Clob ao invés de XMLTYPE ficá assim:

select extract(xmltype(n.xml_autorizado),
               '/nfeProc/NFe',
               'xmlns="http://www.portalfiscal.inf.br/nfe"')
       .getClobVal()
  from notas_fiscais_eletronicas n
 where n.nfe_sequencial = 42;


3 – Converter data UTC para tipo Date no XML do Clob

 Antigamente existia uma tag para data e outra para hora na estrutura do XML de nota fical eletrônica. Agora existe apenas uma tag em formato UTC (AAAA-MM-DDThh:mm:ssTZD). Então precisei converter essa string em formato UTC para um tipo Date do Oracle:

select CAST(TO_TIMESTAMP_TZ(
          extractvalue(xmltype(n.xml_autorizado),
                       '/nfeProc/protNFe/infProt/dhRecbto',
                       'xmlns="http://www.portalfiscal.inf.br/nfe"'),
         'YYYY-MM-DD"T"HH24:MI:SS TZH:TZM')
       AS DATE)
  from notas_fiscais_eletronicas n
 where n.nfe_sequencial = 42;


Usei três funções para a conversão:

· extractvalue - A estrutura funciona como a função extractcitado acima, porém ele retorna o valor da tag especificada. Nesse exemplo ele retornará uma string com o valor da tag dhRecbto que contém a data no formato UTC.

·    TO_TIMESTAMP_TZ - converte tipos CHAR, VARCHAR2, NCHAR, ou NVARCHAR2 em um valor do tipo TIMESTAMP COM TIME ZONE. Podemos usar o parâmetro do formato da data usado na tag de data UTC (TZH significa a hora da Timezone e TZM o minuto da timezone).

·    CAST(… AS DATE) – Converterá o tipo TIMESTAMP COM TIME ZONE para o tipo DATE. Existe uma tabela na documentação do Oracle que contém os tipos compatíveis para conversão. Deixarei nos links de referência.


Referências: