在这个答案中,我总结了我最终实施的解决方案。
它是专门为解决我的特定问题而设置的,所以我建议
您还可以查看 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 中)需要分配搜索和测试。尽管不完美,但如果有人想做类似的事情,我希望以下 sn-ps 可能有用。
创建 SQLite 数据库并将数据从 csv 导入数据库:
使用 sqlite3 创建数据库的第一个 .bat 文件结构如下:
sqlite3 数据库名称.db
第一个文本文件 (.sql) 称为 DatabaseNameStructure.sql,其结构如下:
开始;创建表 Table1Name (Column1Name real, Column2Name real, Column2Name real);创建表 Table2Name (Column1Name real, Column2Name real, Column2Name real);提交;
让 sqlite3 将 csv 文件上传到数据库的第二个 .bat 文件的结构如下:
sqlite3 DatabaseName.db
第二个文本文件 (.sql) 称为 uploadCsvToDatabaseName.sql,其结构如下:
.分隔符","
.import Table1Data.csv Table1Name
.import Table2Data.csv Table2Name
.退出
为此,您需要在系统路径中包含 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 在其数据库工具箱 (cursor.fetch) documentation 中提供了如何执行此操作的建议。但是,使用 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