Perl XML::LibXML очень медленная обработка из-за наклона элементов

Я написал код для импорта XML-файла в базу данных и столкнулся с огромной проблемой производительности. Xml-файл имеет 150 Мб и содержит 170000 элементов (ART) с соответствующими подэлементами (REF_DATA, ...). Мне удалось выяснить причину проблемы, но я не знаю, как ее решить.

Каждый элемент ART имеет подэлементы (см. Рисунок). Проблема возникает в том случае, когда в АРТ есть несколько подэлементов ARTPRI, которые различаются между своими подэлементами PTYP. Я хотел бы извлечь все данные ARTPRI/VDAT и ARTPRI/PRICE и импортировать в переменные $v_dat_pexf, $v_dat_ppub,$v_dat_zurr, так далее.

Ниже приведен минимальный пример моего кода. Этому коду требуется 30 секунд, чтобы прочитать один элемент ART. Когда я удаляю деталь (узел START2 / узел END2), xml-файл обрабатывается очень быстро (< 1s/ART).

У кого-нибудь есть идея, почему эта часть кода замедляет процесс и как с этим справиться? Спасибо за помощь.

Вот XML-файл: XML-файл

И это код:

my $xml_article = "oddb_article.xml";
my $xpc = XML::LibXML::XPathContext->new();
$xpc->registerNs(sr => 'http://whatever');
my $doc = XML::LibXML->load_xml(location => $xml_article);

my @node1_art = $xpc->findnodes("/sr:ARTICLE/sr:ART", $doc);
my $i = 0;
foreach my $node1 ( @node1_art ) {
    $i++;
    my $ref_data           = $xpc->findvalue('./sr:REF_DATA',$node1);
    my @node1_art_artpri   = $xpc->findnodes("/sr:ARTICLE/sr:ART/sr:ARTPRI", $doc);
    my $v_dat_pexf;

    # -- search through each ARTPRI within ART
    # (This is the part which slows down processing)
    # -------- START node2 -----------------------
    foreach my $node2 ( @node1_art_artpri ) {
        my $ctrl1 = $xpc->findvalue('./sr:PTYP',$node2);
        if ( $ctrl1 eq 'PEXF' ) {
        $v_dat_pexf = $xpc->findvalue('./sr:VDAT',$node2);
        }
    # -------- END node2 -----------------------
    }     
print "Row $i\n";
}

Вот версия для копирования-вставки с 3 элементами АРТ:

<?xml version="1.0" encoding="utf-8"?>
<ARTICLE xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://whatever" CREATION_DATETIME="2015-11-17T05:44:14+0100" PROD_DATE="2015-11-17T05:44:14+0100" VALID_DATE="2015-11-17T05:44:14+0100">
  <ART DT="" SHA256="2744a856e9bdf226e68bd555f0695b37f6477c55fca3d9eec36a0740fe8146c2">
    <REF_DATA>1</REF_DATA>
    <PHAR>0000000</PHAR>
    <SALECD>I</SALECD>
    <CDBG>N</CDBG>
    <BG>N</BG>
    <DSCRD>Epimineral Paste</DSCRD>
    <DSCRF>Epimineral pâte</DSCRF>
    <SORTD>EPIMINERAL PASTE</SORTD>
    <SORTF>EPIMINERAL PâTE</SORTF>
    <ARTCOMP>
      <COMPNO>7601003300741</COMPNO>
    </ARTCOMP>
    <ARTBAR>
      <CDTYP>E13</CDTYP>
      <BC>0</BC>
      <BCSTAT>A</BCSTAT>
    </ARTBAR>
    <ARTPRI>
      <VDAT>01.10.2015</VDAT>
      <PTYP>PEXF</PTYP>
      <PRICE>305.83</PRICE>
    </ARTPRI>
    <ARTPRI>
      <VDAT>01.10.2015</VDAT>
      <PTYP>PPUB</PTYP>
      <PRICE>367.5</PRICE>
    </ARTPRI>
    <ARTINS>
      <NINCD>10</NINCD>
    </ARTINS>
  </ART>
  <ART DT="" SHA256="ac0eb1ad7c81f5476541ead533c48690a2c9cf3b1dd0ba8ae295145b6bcb1b40">
    <REF_DATA>0</REF_DATA>
    <PHAR>0021976</PHAR>
    <SALECD>I</SALECD>
    <CDBG>N</CDBG>
    <BG>N</BG>
    <DSCRD>DIOPARINE Gtt Opht 7500 E 5 ml</DSCRD>
    <DSCRF>DIOPARINE Gtt Opht 7500 E 5 ml</DSCRF>
    <SORTD>DIOPARINE GTT OPHT 7500 E 5 ML</SORTD>
    <SORTF>DIOPARINE GTT OPHT 7500 E 5 ML</SORTF>
    <ARTCOMP/>
    <ARTBAR>
      <CDTYP>E13</CDTYP>
      <BC>0</BC>
      <BCSTAT>A</BCSTAT>
    </ARTBAR>
    <ARTPRI>
      <VDAT>01.10.2015</VDAT>
      <PTYP>PEXF</PTYP>
      <PRICE>305.83</PRICE>
    </ARTPRI>
    <ARTPRI>
      <VDAT>01.10.2015</VDAT>
      <PTYP>PPUB</PTYP>
      <PRICE>367.5</PRICE>
    </ARTPRI>
  </ART>
  <ART DT="" SHA256="ecc62600e79183822abddb3af0d2a1f9dfb9f2c343c51a2cf135a45354ba7de1">
    <REF_DATA>0</REF_DATA>
    <PHAR>0027447</PHAR>
    <SALECD>I</SALECD>
    <CDBG>N</CDBG>
    <BG>N</BG>
    <DSCRD>ARTHROSENEX Salbe 100 g</DSCRD>
    <DSCRF>ARTHROSENEX Salbe 100 g</DSCRF>
    <SORTD>ARTHROSENEX SALBE 100 G</SORTD>
    <SORTF>ARTHROSENEX SALBE 100 G</SORTF>
    <ARTCOMP/>
    <ARTBAR>
      <CDTYP>E13</CDTYP>
      <BC>0</BC>
      <BCSTAT>A</BCSTAT>
    </ARTBAR>
    <ARTPRI>
      <VDAT>01.10.2015</VDAT>
      <PTYP>PEXF</PTYP>
      <PRICE>305.83</PRICE>
    </ARTPRI>
    <ARTPRI>
      <VDAT>01.10.2015</VDAT>
      <PTYP>PPUB</PTYP>
      <PRICE>367.5</PRICE>
    </ARTPRI>
  </ART>
  <RESULT>
    <OK_ERROR>OK</OK_ERROR>
    <NBR_RECORD>170673</NBR_RECORD>
    <ERROR_CODE/>
    <MESSAGE/>
  </RESULT>
</ARTICLE>

1 ответ

Решение

Я думаю, что корнем вашей проблемы будет сочетание этих двух строк:

my @node1_art = $xpc->findnodes("/sr:ARTICLE/sr:ART", $doc);


my @node1_art_artpri   = $xpc->findnodes("/sr:ARTICLE/sr:ART/sr:ARTPRI", $doc);

Потому что, похоже, ты находишь каждый ART узел во всем документе (сканирование всего) и затем для каждого такого узла - вы сканируете весь документ снова, чтобы найти каждый ARTPRI узел.

Который вы затем повторяете:

foreach my $node2 ( @node1_art_artpri ) {

... но только захватывая значение последнего, который вы найдете.

Похоже, что это может быть логическая ошибка (трудно сказать наверняка).

Но вы пробовали просто печатать каждый раз, когда цикл повторяется, и посмотреть, сколько раз это происходит?

Потому что, похоже, цель - пройти небольшое количество раз. У вас есть только два ARTPRI узлы под вашим примером ART но это будет делать гораздо больше.

Это должно быть исправлено с помощью:

my @node1_art_artpri   = $xpc->findnodes("./sr:ARTPRI", $node1); 

(или что-то подобное - важная часть - установить контекст для узла, а не документа).

Или, возможно, используя xpath вместо вашего логического состояния:

#!/usr/bin/env perl

use strict;
use warnings;

use XML::LibXML;

my $xpc = XML::LibXML::XPathContext->new();
$xpc->registerNs( sr => 'http://whatever' );
my $doc = XML::LibXML->load_xml( location => 'test4.xml' );

foreach my $node1 ( $xpc->findnodes( "/sr:ARTICLE/sr:ART", $doc ) ) {
    my $ref_data = $xpc->findvalue( './sr:REF_DATA', $node1 );
    my $v_dat_pexf =
        $xpc->findvalue( './sr:ARTPRI/sr:PTYP[text()="PEXF"]/../sr:VDAT', $node1 );
    print "$ref_data => $v_dat_pexf\n";
}

Но на самом деле для такого рода задач, я мог бы думать с точки зрения XML::Twig, который позволяет вам делать вещи через twig_handlers - и, таким образом, сохранить след памяти.

#!/usr/bin/env perl

use strict;
use warnings;

use XML::Twig;

sub extract_pexf {
   my ( $twig, $ART ) = @_; 
   #or stuff it in an array, whatever. 
   print $ART -> first_child_text('REF_DATA'), " => ";
   print $ART -> get_xpath('.//ARTPRI/PTYP[string()="PEXF"]/../VDAT',0) -> text,"\n";
   $twig -> purge; #clear processed data from memory. 
}

XML::Twig -> new ( twig_handlers => { 'ART' => \&extract_pexf } ) -> parsefile ( 'your_xml' );
Другие вопросы по тегам