使用 datatool 的文档编译速度很慢

使用 datatool 的文档编译速度很慢

以下文档使用 datatool 包,编译速度很慢,尽管生成的 PDF 文件只有 3 页长。在我的计算机上,它需要大约 17 秒。我希望有加快速度的建议。

显然,大部分时间都花在了 datatool 的数据库操作上。一行

\dtlsort{RowID}{docDB}{\dtlletterindexcompare}

目前大约需要编译时间的一半,但我不清楚为什么。

更新:到目前为止有两条评论,基本上是说 - 对于数据处理步骤(排序和合并),使用更适合这项工作的工具。但是,我不清楚为什么使用快速排序算法(如果这是问题的一部分)是一个问题。我发现 创建数组以说明使用 tikz 进行快速排序的最简单/最干净的方法是什么? 其中讨论了 TeX 中的快速排序。

此外,我会根据@egreg 的回答,自动从文本中的引用中创建相关表格: 使用文本中的引用自动从数​​据工具创建表格,所以如果我想继续这样做,我无法轻松地将数据处理步骤与 LaTeX 文件分开。我想我可以在将数据传递给 datatool 之前合并和排序数据...

我用 编译了这个pdflatex filename

######################
dto.tex
######################
\documentclass{letter}
\usepackage{datatool}
\usepackage{datagidx}
\usepackage{longtable}
\usepackage{lipsum}
\usepackage{array}
\usepackage{etoolbox}
\usepackage{url}
\setlength{\oddsidemargin}{-0.10in}
\setlength{\evensidemargin}{-0.10in}
\setlength{\topmargin}{-0.3in}
\setlength{\headsep}{0.2in}
\textheight=8.8in
\textwidth=6.7in
\newcounter{tabenum}\setcounter{tabenum}{0}
\newcommand{\colhead}[1]{\multicolumn{1}{>{\bfseries}l}{#1}}
\newcommand{\nextnuml}[1]{\refstepcounter{tabenum}\thetabenum.\label{#1}}
\makeatletter
\let\oldref\ref
\def\ref#1{%
  \immediate\write\@auxout{%
    \string\gappto\string\ReferencedIDs{#1,}%
  }%
  \oldref{#1}%
}
\def\ReferencedIDs{}
\makeatother

\usepackage{fouriernc}
\address{Some Address\\ Some Place\\Email: [email protected]}
\signature{(Somebody)}
\newcommand*{\checkmissing}[1]{\DTLifnull{#1}{}{#1}}

\newcommand{\PrintDocTableParekh}[3][]{%
 % #1 = list of rowIDs
 % #2 = database to search
 % #3 =caption
  \begin{longtable}{r l p{1.5in} c c p{2.5in}}
    \caption{#3}\\
   & \colhead{Date} & \colhead{Filename} & \colhead{From} & \colhead{To} & \colhead{Subject}\\\hline\endhead
    \DTLforeach
    [%
    \(%
    \equal{#1}{}\AND\DTLisSubString{\ReferencedIDs}{\RowID}%
    \)%
    \OR\(
    \DTLisSubString{#1}{\RowID}\AND\DTLisSubString{\ReferencedIDs}{\RowID}%
    \)%
    ]
    {#2}{%
      \RowID=RowID,%
      \Date=Date,%
      \Filename=Filename,%
      \From=From,%
      \To=To,%
      \Subject=Subject%
    }{%
      \nextnuml{\RowID} & \Date & {\bfseries\expandafter\url\expandafter{\Filename} } & \checkmissing{\From} & \checkmissing{\To} & \Subject \\
    }%
  \end{longtable}
}%

\makeatletter
\newcommand{\AddToTable}[2]{%
\DTLforeach*{#2}{\newDBRowID=RowID,\newDBDate=Date,\newDBFilename=Filename,\newDBFrom=From,\newDBTo=To,\newDBSubject=Subject}{
  \DTLnewrow{#1}
  {\let\DTLnewdbentry\relax % Avoid expansion of \DTLnewdbentry when using \protected@xdef below
   \protected@xdef\insertnewdbentry{%
     \DTLnewdbentry{#1}{RowID}{\newDBRowID}%
     \DTLnewdbentry{#1}{Date}{\newDBDate}%
     \DTLnewdbentry{#1}{Filename}{\newDBFilename}%
     \DTLnewdbentry{#1}{From}{\newDBFrom}%
     \DTLnewdbentry{#1}{To}{\newDBTo}%
     \DTLnewdbentry{#1}{Subject}{\newDBSubject}%
  }}\insertnewdbentry
}}
\makeatother

\begin{filecontents*}{documents.csv}
1952.02.19,19 Feb 1952,something.txt,subject
1994.03.26,26 Mar 1994,something.txt,subject
2010.11.04,04 Nov 2010,something.txt,subject
smyt.bs.ie,Mar 2004-2013 (incl.),something.txt,subject
ct.bs.ie,Mar 2004-2013 (incl.),something.txt,subject
cf.bs.ie,Mar 2004-2013 (incl.),something.txt,subject
ismail.bs.ie,Mar 2004-2013 (incl.),something.txt,subject
mh.bs.ie,Mar 2004-2013 (incl.),something.txt,subject
smyt.it,2007-2013,something.txt,subject
ismail.it,2008-2013,something.txt,subject
2013.10.05.powai,05 Oct 2013,something.txt,subject
2013.10.05.kanjur,05 Oct 2013,something.txt,subject
2013.10.28.zhm,28 Oct 2013,something.txt,subject
2014.02.26,26 Feb 2014,something.txt,subject
familytree,26 Feb 2014,something.txt,subject
trusts,26 Feb 2014,something.txt,subject
\end{filecontents*}

\begin{filecontents*}{correspondence.csv}
1998.09.14,14 Sep 1998,something.txt,FROM,TO,,subject
1998.09.16,16 Sep 1998,something.txt,FROM,TO,,subject
2001.05.23,23 May 2001,something.txt,FROM,TO,,subject
2007.06.10,10 Jun 2007,something.txt,FROM,TO,,subject
2011.05.09,09 May 2011,something.txt,FROM,TO,Y,subject
2011.09.09.slm,09 Sep 2011,something.txt,FROM,TO,Y,subject
2011.09.26.fm,26 Sep 2011,something.txt,FROM,TO,Y,subject
2011.09.26.slm,26 Sep 2011,something.txt,FROM,TO,Y,subject
2011.09.26.zhm,26 Sep 2011,something.txt,FROM,TO,,subject
2011.11.21.slm,21 Nov 2011,something.txt,FROM,TO,Y,subject
2011.11.21.fm,21 Nov 2011,something.txt,FROM,TO,Y,subject
2011.11.22,22 Nov 2011,something.txt,FROM,TO,Y,subject
2011.11.23,23 Nov 2011,something.txt,FROM,TO,Y,subject
2012.01.25,25 Jan 2012 (?),something.txt,FROM,TO,,subject
2012.12.07,07 Dec 2012,something.txt,FROM,TO,,subject
2013.01.14,14 Jan 2013,something.txt,FROM,TO,,subject
2013.08.29,29 Aug 2013,something.txt,FROM,TO,Y,subject
2013.09.09.fm,09 Sep 2013,something.txt,FROM,TO,Y,subject
2013.09.09.rm,09 Sep 2013,something.txt,FROM,TO,Y,subject
2013.09.10.slm,10 Sep 2013,something.txt,FROM,TO,Y,subject
2013.09.10.fm,10 Sep 2013,something.txt,FROM,TO,Y,subject
2013.09.10.slm2,10 Sep 2013,something.txt,FROM,TO,,subject
2013.09.11.slm1,11 Sep 2013,something.txt,FROM,TO,Y,subject
2013.09.11.fm,11 Sep 2013,something.txt,FROM,TO,Y,subject
2013.09.11.slm2,11 Sep 2013,something.txt,FROM,TO,Y,subject
2013.09.16,16 Sep 2013,something.txt,FROM,TO,,subject
2013.09.17,17 Sep 2013,something.txt,FROM,TO,,subject
2013.09.23,23 Sep 2013,something.txt,FROM,TO,,subject
2013.10.06,06 Oct 2013,something.txt,FROM,TO,,subject
2013.10.14.kma,14 Oct 2013,something.txt,FROM,TO,,subject
2013.10.14.ks,14 Oct 2013,something.txt,FROM,TO,,subject
2013.10.16,16 Oct 2013,something.txt,FROM,TO,,subject
2013.10.17,17 Oct 2013,something.txt,FROM,TO,,subject
2013.10.18,18 Oct 2013,something.txt,FROM,TO,,subject
2013.10.22,22 Oct 2013,something.txt,FROM,TO,,subject
2013.10.23,23 Oct 2013,something.txt,FROM,TO,,subject
2013.10.24,24 Oct 2013,something.txt,FROM,TO,,subject
2013.10.25.zhm,25 Oct 2013,something.txt,FROM,TO,,subject
2013.10.25.fm1,25 Oct 2013,something.txt,FROM,TO,Y,subject
2013.10.25.rm1,25 Oct 2013,something.txt,FROM,TO,Y,subject
2013.10.25.fm2,25 Oct 2013,something.txt,FROM,TO,Y,subject
2013.10.25.rm2,25 Oct 2013,something.txt,FROM,TO,Y,subject
2013.10.28,28 Oct 2013,something.txt,FROM,TO,,subject
2013.10.30,30 Oct 2013,something.txt,FROM,TO,,subject
2013.11.11,11 Nov 2013,something.txt,FROM,TO,,subject
2013.11.12,12 Nov 2013,something.txt,FROM,TO,,subject
2013.11.13,13 Nov 2013,something.txt,FROM,TO,,subject
2013.11.25,25 Nov 2013,something.txt,FROM,TO,,subject
2013.12.05.ta,05 Dec 2013,something.txt,FROM,TO,,subject
2013.12.05.slm,05 Dec 2013,something.txt,FROM,TO,,subject
2014.01.15,15 Jan 2014,something.txt,FROM,TO,,subject
2014.02.24,24 Feb 2014,something.txt,FROM,TO,,subject
2014.04.02,02 Apr 2014,something.txt,FROM,TO,,subject
2014.04.10,10 Apr 2014,something.txt,FROM,TO,,subject
2014.04.15,15 Apr 2014,something.txt,FROM,TO,,subject
2014.07.18.tm,18 Jul 2014,something.txt,FROM,TO,,subject
2014.07.18.ta,18 Jul 2014,something.txt,FROM,TO,,subject
2014.07.24,24 Jul 2014,something.txt,FROM,TO,,subject
2014.09.05.tm,05 Sep 2014,something.txt,FROM,TO,,subject
2014.09.05.ta,05 Sep 2014,something.txt,FROM,TO,,subject
2014.09.05.slm,05 Sep 2014,something.txt,FROM,TO,,subject
2014.10.17,17 Oct 2014,something.txt,FROM,TO,,subject
2014.11.12.tm,12 Nov 2014,something.txt,FROM,TO,,subject
2014.11.14,14 Nov 2014,something.txt,FROM,TO,,subject
2014.11.17,17 Nov 2014,something.txt,FROM,TO,,subject
2015.01.06,06 Jan 2015,something.txt,FROM,TO,,subject
\end{filecontents*}

\begin{document}

\def\today{9th January, 2014}
\begin{letter}{
    Someone\\
    Somewhere\\
    Subject: Some stuff
}

  \opening{Dear Someone}

  \lipsum[1]. Here is a ref - [\ref{2014.11.14}].

  \lipsum[2]. Here is a ref - [\ref{2014.11.14}].

  \lipsum[3] Here is a ref - [\ref{2014.11.17}].

  \lipsum[4]


  \lipsum[5] Here is a ref - [\ref{2014.11.14}].

  \lipsum[6] Here is a ref - [\ref{2014.07.18.tm}].

  \lipsum[7]

  \lipsum[8] Here is a ref - [\ref{2014.11.12.tm}].

  \lipsum[9]. See references
    ~[\ref{2013.10.05.kanjur}], [\ref{2013.10.05.powai}].
    [\ref{2013.10.06}], [\ref{2013.10.28.zhm}],
    [\ref{2013.10.17}], [\ref{2013.10.22}],

    \lipsum[10]. See references
    [\ref{2013.10.14.ks}], [\ref{2013.10.06}]
    [\ref{2013.10.17}], [\ref{2013.10.22}], [\ref{2013.10.24}],

    \lipsum[10]. See references
    [\ref{2013.10.28}], [\ref{2013.10.30}]

    \lipsum[10]. See references [\ref{2013.11.11}]

    \lipsum[11]. See references [\ref{2014.09.05.tm}]

    \lipsum[12]. See references
    [\ref{2014.07.18.tm}] [\ref{2014.11.12.tm}]

   \lipsum[13]. See references
    [\ref{2014.11.17}], [\ref{2015.01.06}], [\ref{2015.01.06}]

\closing{Yours Sincerely,}

\DTLloaddb[noheader,keys={RowID,Date,Filename,Subject}]{docDB}{documents.csv}
\newtermaddfield[docDB]{From}{From}{}
\newtermaddfield[docDB]{To}{To}{}
\DTLloaddb[noheader,keys={RowID,Date,Filename,From,To,Email,Subject}]{corrDB}{correspondence.csv}
\AddToTable{docDB}{corrDB}
\dtlsort{RowID}{docDB}{\dtlletterindexcompare}
\PrintDocTableParekh{docDB}{Documents}

\end{letter}
\end{document}

答案1

datatool包使用插入排序算法。对于任何感兴趣的人,请参阅第 4.10 节中的详细信息。datatool 记录代码。不过,我同意这些评论。TeX 是一个排版工具(而且非常好),但它有很多缺点,这就是为什么它datatool有一个附带的辅助应用程序叫做datatooltk。它可以加载自己的本机.dbtex格式,也datatool可以使用加载(加载速度比或\DTLloaddbtex快得多)。还可以加载 CSV、XLS、ODS 并可以从 MySQL 数据库获取数据,它可以对数据库进行排序并将两个数据库合并在一起。(它还可以对数据库进行打乱并过滤掉行。)\DTLloaddb\DTLloadrawdbdatatooltk

为了测试目的,我生成了一个包含一列和 101 行的 CSV 文件。第一行是标题行,其余 100 行每行包含从字典中随机选择的一个单词或短语。CSV 文件的开头(entries.csv)如下所示:

Word
minnow
running board
dispirit
gravelly
sharp-tongued
penitentiary
vice
witness box
hydrochloric acid

这是一个测试文件(test-texsort.tex):

% arara: pdflatex
\documentclass{article}

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{datatool}

\DTLloaddb{test}{entries.csv}

\DTLsort{Word}{test}

\begin{document}

\DTLforeach*{test}{\Word=Word}{\Word. }

\end{document}

这只需要

pdflatex test-texsort

进行编译。下一个示例文件(test-csv.tex)需要datatooltk导入并排序 CSV 文件中的数据,然后将其转换为entries.dbtex

% arara: datatooltk: {csv: entries.csv, sort: Word, output: entries.dbtex}
% arara: pdflatex
\documentclass{article}

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{datatool}

\DTLloaddbtex{\testdb}{entries.dbtex}

\begin{document}

\DTLforeach*{\testdb}{\Word=Word}{\Word. }

\end{document}

这需要:

datatooltk --csv entries.csv --sort Word --output entries.dbtex
pdflatex test-csv

我将 CSV 文件转换为 XLS 和 ODS。测试文件完全相同,尽管datatooltk调用需要不同的开关。以下是test-ods.tex(修改了 arara 指令):

% arara: datatooltk: {ods: entries.ods, sort: Word, output: entries.dbtex}
% arara: pdflatex
\documentclass{article}

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{datatool}

\DTLloaddbtex{\testdb}{entries.dbtex}

\begin{document}

\DTLforeach*{\testdb}{\Word=Word}{\Word. }

\end{document}

现在构建文档所需的命令行调用是:

datatooltk --ods entries.ods --sort Word --output entries.dbtex
pdflatex test-ods

这里是test-xls.tex

% arara: datatooltk: {xls: entries.xls, sort: Word, output: entries.dbtex}
% arara: pdflatex
\documentclass{article}

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{datatool}

\DTLloaddbtex{\testdb}{entries.dbtex}

\begin{document}

\DTLforeach*{\testdb}{\Word=Word}{\Word. }

\end{document}

现在构建文档所需的命令行调用是:

datatooltk --xls entries.xls --sort Word --output entries.dbtex
pdflatex test-xls

我使用arara4.0 版本构建了所有这些文档,这给出了总共花费的时间。结果如下:

  • 测试-texsort:1.12 秒
  • 测试-csv:0.42秒
  • 测试时间:0.63 秒
  • 测试-xls:0.57秒

我还将 CSV 文件转换为 MySQL 数据库。以下是test-sql.tex

% arara: datatooltk: {sql: "SELECT * FROM words ORDER BY Word",
% arara: --> sqluser: sampleuser,
% arara: --> sqldb: samples, output: entries.dbtex}
% arara: pdflatex
\documentclass{article}

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{datatool}

\DTLloaddbtex{\testdb}{entries.dbtex}

\begin{document}

\DTLforeach*{\testdb}{\Word=Word}{\Word. }

\end{document}

命令行调用是:

datatooltk --sql "SELECT * FROM words ORDER BY Word" --sqluser sampleuser --sqldb samples --output entries.dbtex
pdflatex test-sql

总时间为 4.23 秒,看起来很长,但大部分时间都是因为我在提示符下输入 SQL 密码所花费的时间。如果我将密码添加到命令行调用中(出于显而易见的原因,我不建议这样做),则所用时间会减少到 0.58 秒。

可以使用--merge类似的开关之一来合并另一个数据库,这些开关记录在datatooltk 用户指南。该datatool包有几个命令允许您以 .dbtex 格式保存数据库:\DTLsaverawdb和。您可以创建另一个数据库,使用该数据库来存储参考资料,并在文档末尾写出数据库,\DTLprotectedsaverawdb而不是将命令写入文件。然后使用合并和过滤。.auxdatatooltk

例如:

% arara: datatooltk: {output: entries.dbtex, csv: entries.csv,
% arara: --> sort: Word, options: [--merge, Word, refs.dbtex],
% arara: --> filters: [[Used, eq, 1]] }
% arara: pdflatex
\documentclass{article}

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}

\usepackage{datatool}

\DTLloaddbtex{\testdb}{entries.dbtex}

\DTLnewdb{refs}
\DTLaddcolumn{refs}{Word}
\DTLaddcolumn{refs}{Used}

\newcommand{\useword}[1]{% assumes no expansion required on #1
  \dtlgetrowindex{\myrowidx}{refs}{1}{#1}%
  \ifx\myrowidx\dtlnovalue
   \DTLnewrow{refs}%
   \DTLnewdbentry{refs}{Word}{#1}%
   \DTLnewdbentry{refs}{Used}{1}%
  \fi
}

\begin{document}

\DTLforeach*{\testdb}{\Word=Word}{\Word. }

Let's use the words  ``minnow''\useword{minnow}, 
``android''\useword{android} and ``felcity''\useword{felicity}.

\DTLsaverawdb{refs}{refs.dbtex}

\end{document}

现在的调用datatooltk是:

datatooltk --sort Word --filter Used eq 1 --csv entries.csv --merge Word refs.dbtex --output entries.dbtex

我第一次运行arara(当refs.dbtex不存在时)arara花了 0.47 秒,生成的文档如下所示:

列出 100 个单词的文档图像

第二次运行arara耗时 0.45 秒。生成的文档如下所示:

列出三个单词的文档图像

entries.dbtex文件现在仅包含上次运行文档中使用的三个单词。

至于为什么我不能在 中实现更高效的排序算法datatool.sty,答案相当平凡:我独自经营一家企业,还要照顾家庭。我根本没有时间开发我的 LaTeX 软件包,而且编写快速排序算法的 TeX 实现将非常耗时、令人疲惫,而且我看不出这样做有什么意义,因为您可以使用 来datatooltk排序。

最后,快速回顾一下第 i 页datatool 用户手册

有句老话说,“用合适的工具做合适的工作”。木工的细凿子是进行精细雕刻的合适工具,但如果你试图用它来砍断树枝,那将需要很长时间。这并不意味着凿子有问题。这只是意味着你使用的工具不对。

提供数据工具包来帮助执行重复命令,例如邮件合并,但由于 TeX 被设计为排版语言,因此不要指望该工具包能够像自定义数据库系统或专用数学或脚本语言那样高效地运行。 如果提供的软件包需要很长时间来编译您的文档,请使用另一种语言来执行计算或数据处理,并将结果保存在可以输入到您的文档中的文件中。对于需要排序、过滤或连接的大量数据,请考虑将数据存储在 SQL 数据库中并使用 datatooltk 导入数据,使用 SQL 语法对值进行过滤、排序和以其他方式操作。

相关内容