在这个答案中,我总结了我最终实施的解决方案。它是专门为解决我的特定问题而设置的,因此我建议您还可以查看 Luca Geretti 和 bdecaf 的答案以获取替代选项。
我实施了什么
我选择SQLite作为数据库,因为它将数据存储在一个文件中,并且易于设置和处理。(使用 sqlite3 和 sqlite-jdbc-3.7.2 驱动程序不需要安装太多,数据库都在一个简单的文件中。)
事实证明, Matlab数据库工具箱太慢了,无法从我的模拟过程中创建的 Matlab 中导出大量数据。因此,我编写了一个“DataOutputManager”类,将数据快照转储到 csv 文件中。
模拟之后,DataOutputManager 创建两个批处理文件和两个带有 SQL 命令的 sql 文本文件并执行批处理文件。第一个批处理文件通过运行 sqlite3.exe ( www.sqlite.org ) 并在第一个文本文件中为其提供 SQL 命令来创建一个 SQLite 数据库。
创建数据库后,sqlite3 被告知使用第二个批处理和文本文件将数据从 csv 文件导入数据库。这不是一个“漂亮”的解决方案,但将数据写入 csv 然后使用 sqlite3 将这些文件导入数据库比使用 Database Toolbox 快得多。(我听说有些人使用 xml 文件)
模拟数据上传到数据库后,我使用 Database Toolbox 和 jdbc 驱动程序 ( sqlite-jdbc-3.7.2 ) 将 SQL 查询发送到数据库。由于这些查询只返回很少的数据,因此 Database Toolbox 不是这里的瓶颈。
设置所有这些(在 Windows 7 中)需要分配搜索和测试。即使不完美,我希望如果有人想做类似的事情,下面的代码片段可能会有用。
创建 SQLite 数据库并将数据从 csv 导入数据库:
使用 sqlite3 创建数据库的第一个 .bat 文件的结构如下:
sqlite3 数据库名称.db < 数据库名称结构.sql
第一个文本文件 (.sql) 称为 DatabaseNameStructure.sql,其结构如下:
开始; 创建表 Table1Name (Column1Name real, Column2Name real, Column2Name real); 创建表 Table2Name (Column1Name real, Column2Name real, Column2Name real); 犯罪;
让 sqlite3 将 csv 文件上传到数据库的第二个 .bat 文件的结构如下:
sqlite3 数据库名称.db < uploadCsvToDatabaseName.sql
第二个文本文件 (.sql) 称为 uploadCsvToDatabaseName.sql,其结构如下:
。分隔器 ”,”
.import Table1Data.csv 表1名称
.import Table2Data.csv 表2名称
。出口
为此,您需要在系统路径中包含 sqlite3.exe,例如保存在 C:\Windows\System32 下。您根据数据/csv 设置在 Matlab 中创建字符串,然后使用 fprintf() 将它们写入上述格式的文件中。然后使用 winopen() 从 Matlab 执行 bat 文件。
使用 jdbc 驱动将 Matlab 连接到 SQLite 数据库:
Bryan Downing 的以下视频对我的开发很有帮助:http ://www.youtube.com/watch?v=5QNyOe79l-s
我创建了一个 Matlab 类(“DataAnalyser”),它连接到数据库并对模拟结果运行所有分析。这是用于设置与数据库通信的类构造函数和连接函数。(我删掉了一些不那么重要的实现部分)
function Analyser=DataAnalyser()
% add SQLite JDBC Driver to java path
Analyser.JdbcDriverFileName='sqlite-jdbc-3.7.2.jar';
% Ask User for Driver Path
[Analyser.JdbcDriverFileName, Analyser.JdbcDriverFilePath] = uigetfile( {'*.jar','*.jar'},['Select the JDBC Driver file (',Analyser.JdbcDriverFileName,')']);
Analyser.JdbcDriverFilePath=[Analyser.JdbcDriverFilePath,Analyser.JdbcDriverFileName];
JavaDynamicPath=javaclasspath('-dynamic'); % read everything from the dynamic path
if~any(strcmp(Analyser.JdbcDriverFilePath,JavaDynamicPath))
disp(['Adding Path of ',Analyser.JdbcDriverFileName,' to java dynamic class path'])
javaaddpath(Analyser.JdbcDriverFilePath);
else
disp(['Path of ',Analyser.JdbcDriverFileName,' is already part of the java dynamic class and does not need to be added']);
end
Analyser.JdbcDriver='org.sqlite.JDBC';
% Ask User for Database File
[Analyser.DbFileName, Analyser.DbFilePath] = uigetfile( '*.db','Select the SQLite DataBase File ');
Analyser.DbFilePath=[Analyser.DbFilePath,Analyser.DbFileName];
Analyser.DbURL=sprintf('jdbc:sqlite:%s',Analyser.DbFilePath);
% Set Timeout of trying to connect with Database to 5 seconds
logintimeout(Analyser.JdbcDriver,5);
end
function [conn,isConnected]=connect(Analyser)
% Creates connection to database.
Analyser.Connection=database(Analyser.DbFilePath,'','',Analyser.JdbcDriver,Analyser.DbURL);
conn=Analyser.Connection;
isConnected=isconnection(Analyser.Connection);
end
从连接的 SQLite 数据库中获取数据到 Matlab
我还为 DataAnalyser 编写了一个函数,该函数在给定 sql 查询时从数据库中获取数据。我在这里发布它的主要部分有两个原因。
不是一次导入所有数据,而是像此函数那样分部分导入数据,这样可以更快地导入数据。
Mathworks 在其 Database Toolbox (cursor.fetch)文档中提供了如何执行此操作的建议。但是,使用 jdbc 和 SQLite 会因为错误而导致错误。
引用来自 Mathworks 支持:
我们之前已经看到,如果您位于记录集的末尾,SQLite JDBC 驱动程序不允许查询有关记录集的某些元数据;根据 JDBC 规范,这应该是允许的。
这个函数可以解决这个问题:
function OutputData=getData(Analyser,SqlQuery,varargin)
% getData(Analyser,SqlQuery)
% getData(Analyser,SqlQuery, setdbprefsString)
% getData(Analyser,SqlQuery,RowLimitPerImportCycle)
% getData(Analyser,SqlQuery,RowLimitPerImportCycle,setdbprefsArg1String,setdbprefsArg2String)
% getData(Analyser,SqlQuery,[],setdbprefsArg1String,setdbprefsArg2String)
%
% RowLimitPerImportCycle sets the Limit on howmany Data rows
% are imported per cycle.
% Default is RowLimitPerImportCycle = 6000
%
% setdbprefsArg1String Default 'datareturnformat'
% setdbprefsArg2String Default 'numeric'
% Hence setdbprefs('datareturnformat','numeric') is the Default
%
% function is partially based on cursor.fetch Documentation for
% Matlab R2012b:
% http://www.mathworks.de/de/help/database/ug/cursor.fetch.html
% Example #6 as of 10.Oct.2012
% The Mathworks' cursor.fetch Documentation mentioned above had
% some errors. These errors were (among other changes)
% corrected and a bug report was send to Mathworks on 10.Oct.2012
if isempty(Analyser.Connection)
disp('No open connection to Database found.')
disp(['Trying to connect to: ',Analyser.DbFileName])
Analyser.connect
end
% Get Setting
if nargin>2
RowLimitPerImportCycle=varargin{1};
else
RowLimitPerImportCycle=[];
end
if ~isnumeric(RowLimitPerImportCycle) || isempty(RowLimitPerImportCycle)
%Default
RowLimitPerImportCycle=5000;
end
if nargin>4
setdbprefsArg1String=varargin{2};
setdbprefsArg2String=varargin{3};
else
setdbprefsArg1String='';
setdbprefsArg2String='';
end
if ischar(setdbprefsArg1String) && ~isempty(setdbprefsArg1String) && ischar(setdbprefsArg2String) && ~isempty(setdbprefsArg2String)
setdbprefs(setdbprefsArg1String,setdbprefsArg2String)
else
%Default
setdbprefs('datareturnformat','numeric');
end
% get Curser
curs=exec(Analyser.Connection,SqlQuery);
if ~isempty(curs.Message)
warning('Model:SQLMessageGetData',[curs.Message, '/n while executing SqlQuery: ',SqlQuery])
end
% import Data
FirstRow = 1;
LastRow = RowLimitPerImportCycle;
firstLoop=true;
while true
curs = fetch(curs,RowLimitPerImportCycle);
if rows(curs)==0
if firstLoop == true
OutputData=[];
end
break
end
AuxData = curs.Data;
numImportedRows = size(AuxData,1);
if numImportedRows < RowLimitPerImportCycle
OutputData(FirstRow:LastRow-(RowLimitPerImportCycle-numImportedRows), :) = AuxData;
else
OutputData(FirstRow:LastRow, :) = AuxData;
end
FirstRow = FirstRow + RowLimitPerImportCycle;
LastRow = LastRow + RowLimitPerImportCycle;
firstLoop=false;
if rows(curs)<RowLimitPerImportCycle
break
end
end
end