Synopse Open Source - Tag - FirebirdmORMot MVC / SOA / ORM and friends2024-02-02T17:08:25+00:00urn:md5:cc547126eb580a9adbec2349d7c65274DotclearHTTP remote access for SynDB SQL executionurn:md5:8ac51ed0a6e45f22746c8ba9e6d3e63c2014-11-18T01:08:00+01:002014-11-18T01:11:52+01:00AB4327-GANDIOpen Source librariesarray bindingblogcompressionDatabaseDelphiFirebirdFireDACHTTPhttp.sysHTTPSmORMotMSSQLMySQLOpenSourceOracleORMperformancesecuritySQLSynDBSynDBExplorerTDataSetwebWinHTTPWinINetZEOS<p>For <em>mORMot</em>, we developed a fully feature direct access layer to any
RDBMS, implemented <a href="http://synopse.info/files/html/Synopse%20mORMot%20Framework%20SAD%201.18.html#TITL_126">
in the SynDB.pas unit</a>.</p>
<p>You can use those <code>SynDB</code> classes to execute any SQL statement,
without any link to the framework ORM.<br />
At reading, the resulting performance is much higher than using the standard
<code>TDataSet</code> component, which is in fact a true performance
bottleneck.<br />
It has genuine features, like <a href="http://synopse.info/files/html/Synopse%20mORMot%20Framework%20SAD%201.18.html#TITL_136">
column access via late-binding</a>, <a href="http://synopse.info/files/html/Synopse%20mORMot%20Framework%20SAD%201.18.html#TITL_137">
an innovative <code>ISQLDBRows</code> interface, and ability to directly access
the low-level binary buffers of the database clients.</a></p>
<p><img src="http://vpn-services.bestreviews.net/files/vpn-remote-access.jpg" alt="" /></p>
<p>We just added a nice feature to those classes: the ability to access
remotely, via plain HTTP, to any <code>SynDB</code> supported database!</p> <p>To define a HTTP server, you may write:</p>
<pre>
<strong>uses</strong> SynDB, <em>// RDBMS core</em>
SynDBSQLite3, SynSQLite3Static, <em>// static SQLite3 engine</em>
SynDBRemote; <em>// for HTTP server</em>
...
<strong>var</strong> Props: TSQLDBConnectionProperties;
HttpServer: TSQLDBServerAbstract;
...
Props := TSQLDBSQLite3ConnectionProperties.Create('data.db3','','','');
<span style="background-color:yellow;">HttpServer := TSQLDBServerHttpApi.Create(Props,'remote','8092','user','pass');</span>
</pre>
<p>The above code will initialize a connection to a local <code>data.db3</code>
<em>SQlite3</em> database (in the <code>Props</code> variable), and then
publish it using the <code>http.sys</code> kernel mode HTTP server to the
<code>http://1.2.3.4:8092/remote</code> URI - if the server's IP is
<code>1.2.3.4</code>.</p>
<p>On the client side, you can then write:</p>
<pre>
<strong>uses</strong> SynDB, <em>// RDBMS core</em>
SynDBRemote; <em>// for HTTP server</em>
...
<strong>var</strong> Props: TSQLDBConnectionProperties;
...
Props := TSQLDBWinHTTPConnectionProperties.Create('1.2.3.4:8092','root','user','pass');
</pre>
<p>As you can see, there is no link to <code>SynDBSQLite3.pas</code> nor
<code>SynSQLite3Static.pas</code> on the client side.<br />
Just the HTTP link is needed.<br />
No need to deploy the RDBMS client libraries with your application, nor setup
the local network firewall.</p>
<p>We defined here a single user, with 'user' / 'pass' credentials, but you may
manage more users on the server side, using the <code>Authenticate</code>
property of <code>TSQLDBServerAbstract</code>.<br />
Note that in our remote access, user management does not match the RDBMS user
rights: you should better have your own set of users at application level, for
higher security, and a better integration with your business logic. If creating
a new user on a RDBMS could be painful, it is pretty easy on our
<code>SynDBRemote.pas</code> side.</p>
<p>Then, you execute your favorite SQL using the connection just as usual:</p>
<pre>
<strong>procedure</strong> Test(Props: TSQLDBConnectionProperties);
<strong>var</strong> Stmt: ISQLDBRows;
<strong>begin</strong>
Stmt := Props.Execute('select * from People where YearOfDeath=?',[1519]);
<strong>while</strong> Stmt.Step <strong>do begin</strong>
assert(Stmt.ColumnInt('ID')>0);
assert(Stmt.ColumnInt('YearOfDeath')=1519);
<strong>end</strong>;
<strong>end</strong>;
</pre>
<p>You may even use this remote connection e.g. using a stand-alone shared
<em>SQLite3</em> database as high performance but low maintenance client-server
database engine.</p>
<p>The transmission protocol uses an optimized binary format, which is
compressed and digitally signed on both ends, and the remote user
authentication will be performed via a challenge validation scheme.<br />
You could publish your server over HTTPS, if needed.</p>
<p>Even if you may be tempted to use such remote access to implement a
<em>n-Tier architecture</em>, you should rather use <em>mORMot</em>'s
Client-Server ORM instead. Our little <em>mORMot</em> is not an ORM on
which we added a data transmission layer: it is a full RESTful system, with a
true SOA design.<br />
But for integrating some legacy SQL code into a new architecture,
<code>SynDBRemote.pas</code> may have its benefits, used in conjunction with
<em>mORMot</em>'s higher level features.</p>
<p>Feel free to <a href="http://synopse.info/forum/viewtopic.php?id=2178">post
your feedback on our forum</a>, as usual, or <a href="http://synopse.info/files/html/Synopse%20mORMot%20Framework%20SAD%201.18.html#TITL_131">
browse the updated documentation</a>!</p>Legacy code, mORMot, and database sharingurn:md5:8855247aa1091834083ba2a25e2c16802014-09-12T09:56:00+02:002014-09-12T09:56:00+02:00AB4327-GANDImORMot FrameworkAJAXblogCrossPlatformDatabaseDocumentationFirebirdGoodPracticelegacyORMRADSOASQL<p>It is pretty much possible that you would have to maintain and evolve a
legacy project, based on an existing database, with a lot of already written
SQL statements - see <em><a href="https://blog.synopse.info?post/post/2012/12/31/Enhance-existing-projects-with-mORMot">Legacy code and
existing projects</a></em>.</p>
<p><img src="http://thumbs.dreamstime.com/t/old-stamp-marmot-black-fox-7894949.jpg" /></p>
<p>For instance, you would like to use <em>mORMot</em> for new features, and/or
add mobile or HTML clients - see <em><a href="https://blog.synopse.info?post/post/2014/08/11/CrossPlatform-Clients/Units-Platforms">Cross-Platform
clients</a></em>.<br />
In this case, the ORM advanced features - like <em><a href="https://blog.synopse.info?post/post/2012/02/14/ORM-cache">ORM Cache</a></em> or BATCH process, see
<em><a href="https://blog.synopse.info?post/post/2011/06/03/BATCH-sequences-for-adding/updating/deleting-records">BATCH
sequences for adding/updating/deleting records</a></em> - may conflict with the
legacy code, for the tables which may have to be shared.<br />
Here are some guidelines when working on such a project.</p>
<p>To be exhaustive about your question, we need to consider each ORM CRUD
operation.<br />
We may have to divide them in three kinds: read queries, insertions, and
modifications of existing data.</p> <p>About ORM <code>Retrieve()</code> methods, the ORM cache can be tuned per
table, and you will definitively lack of some cache, but remember :</p>
<ul>
<li>That you can set a "time out" period for this cache, so that you may still
benefit of it in most cases;</li>
<li>That you have a cache at server level and another at client level, so you
can tune it to be less aggressive on the client, for instance;</li>
<li>That you can tune the ORM cache <em>per ID</em>, so some items which are
not likely to change can still be cached.</li>
</ul>
<p>About ORM <code>Add()</code> or <code>BatchAdd()</code> methods, when using
the external engine, if any external process is likely to INSERT new rows,
ensure you set the <code>TSQLRestStorageExternal EngineAddUseSelectMaxID</code>
property to TRUE, so that it will compute the next maximum ID by hand.<br />
But it still may be an issue, since the external process may do an INSERT
<em>during</em> the ORM insertion.<br />
So the best is perhaps to NOT use the ORM <code>Add()</code> or
<code>BatchAdd()</code> methods, but rely on dedicated INSERT SQL statement,
e.g. hosted in an interface-based service on the server side.</p>
<p>About ORM <code>Update() Delete() BatchUpdate() BatchDelete()</code>
methods, they sound safe to be used in conjunction with external process
modifying the DB, as soon as you use transactions to let the modifications be
atomic, and won't conflict any concurrent modifications in the legacy code.</p>
<p>Perhaps the safer pattern, when working with external tables which are to be
modified in the background by some legacy code, may be to use server-side
<em>interface-based services</em> - see <em>Client-Server services via
interfaces</em> - for any process involving external tables which may be
modified by another process, with manual SQL, instead of using the ORM "magic".
But it will depend on your business logic, and you will fail to benefit from
the ORM features of the framework.<br />
Nevertheless, introducing <em>Service-Oriented Architecture (SOA)</em> into
your application would be very beneficial: ORM is not mandatory, especially if
you are "fluent" in SQL queries, know how to make them as standard as possible,
and have a lot of legacy code, perhaps with already tuned SQL statements.</p>
<p>Introducing SOA is mandatory to introduce new kind of clients to your
applications, like mobile apps or AJAX modern sites: you could not access
directly the database any more, as you did with your legacy Delphi application,
and RAD DB components.<br />
All new features, involving new tables to store new data, would still benefit
of the <em>mORMot</em>'s ORM, and could still be hosted in the very same
external database, shared by your existing code.<br />
Then, you will be able to identify <em>seams</em> - see <em><a href="https://blog.synopse.info?post/post/2012/12/31/Enhance-existing-projects-with-mORMot">Legacy code and
existing projects</a></em> - in your legacy code, and move them to your new
<em>mORMot</em> services, then let your application evolve into a newer
<a href="https://blog.synopse.info?post/post/2014/05/30/Software-Design%2C-Brook%2C-mORMot%2C-RAD%2C-SOLID-and-OOP">
SOA/MVC architecture</a>, without breaking anything, nor starting from
scratch.</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?id=2008">welcome on our forum</a>, as
usual.</p>Support of MySQL, DB2 and PostgreSQLurn:md5:7be2ca38e9a4c0d2c2548de8ed6a46c32014-03-07T10:29:00+01:002014-03-07T10:31:24+01:00AB4327-GANDImORMot FrameworkblogDatabaseDB2DelphiDocumentationFirebirdFireDACmORMotMSSQLmultithreadMySQLNexusDBODBCOleDBOracleORMperformancePostgreSQLRestSQLSQLite3SynDBTDataSetUnicodeUniDACZEOS<p>We just tested, benchmarked and validated <a href="https://dev.mysql.com">Oracle MySQL</a>, <a href="http://www.ibm.com/software/data/db2/express-c/">IBM DB2</a> and <a href="http://www.postgresql.org/">PostgreSQL</a> support for our <code>SynDB</code>
database classes and the <em>mORMot</em>'s ORM core.<br />
This article will also show all updated results, including our <a href="https://blog.synopse.info?post/post/2014/03/03/ORM-enhanced-for-BATCH-insert">newly introduced multi-value
INSERT statement generations</a>, which speed up a lot BATCH insertion.</p>
<p>Stay tuned!</p>
<p><img src="http://fc04.deviantart.net/fs70/f/2012/206/0/7/marmot_can_run_by_jaffa_tamarin-d58lk2i.jpg" alt="" width="450" height="251" /></p>
<p><strong>Purpose here is not to say that one library or database is better or
faster than another, but publish a snapshot of <em>mORMot</em> persistence
layer abilities, depending on each access library.</strong></p>
<p>In this timing, we do not benchmark only the "pure" SQL/DB layer access
(<code>SynDB</code> units), but the whole Client-Server ORM of our
framework.</p>
<p>Process below includes all aspects of our ORM:</p>
<ul>
<li>Access via high level CRUD methods (<em>Add/Update/Delete/Retrieve</em>,
either per-object or in BATCH mode);</li>
<li>Read and write access of <code>TSQLRecord</code> instances, via optimized
RTTI;</li>
<li>JSON marshaling of all values (ready to be transmitted over a
network);</li>
<li>REST routing, with security, logging and statistic;</li>
<li>Virtual cross-database layer using its <em>SQLite3</em> kernel;</li>
<li>SQL on-the-fly generation and translation (in <em>virtual</em> mode);</li>
<li>Access to the database engines via several libraries or providers.</li>
</ul>
<p>In those tests, we just bypassed the communication layer, since
<code>TSQLRestClient</code> and <code>TSQLRestServer</code> are run in-process,
in the same thread - as a <code>TSQLRestServerDB</code> instance. So you have
here some raw performance testimony of our framework's ORM and RESTful core,
and may expect good scaling abilities when running on high-end hardware, over a
network.</p>
<p>On a recent notebook computer (<em>Core i7</em> and SSD drive), depending on
the back-end database interfaced, <em>mORMot</em> excels in speed, as will show
the following benchmark:</p>
<ul>
<li>You can persist up to 570,000 objects per second, or retrieve 870,000
objects per second (for our pure Delphi in-memory engine);</li>
<li>When data is retrieved from server or client 38, you can read more than
900,000 objects per second, whatever the database back-end is;</li>
<li>With a high-performance database like Oracle, and our direct access
classes, you can write 70,000 (via array binding) and read 160,000 objects per
second, over a 100 MB network;</li>
<li>When using alternate database access libraries (e.g. Zeos, or
<code>DB.pas</code> based classes), speed is lower (even if comparable for DB2,
MS SQL, PostgreSQL, MySQL) but still enough for most work, due to some
optimizations in the <em>mORMot</em> code (e.g. caching of prepared statements,
SQL multi-values insertion, direct export to/from JSON, <em>SQlite3</em>
virtual mode design, avoid most temporary memory allocation...).</li>
</ul>
<p>Difficult to find a faster ORM, I suspect.</p> <h3>Software and hardware configuration</h3>
<p>The following tables try to sum up all available possibilities, and give
some benchmark (average objects/second for writing or reading).</p>
<p>In these tables:<br />
- 'SQLite3 (file full/off/exc)' indicates use of the internal <em>SQLite3</em>
engine, with or without <code>Synchronous := smOff</code> and/or
<code>DB.LockingMode := lmExclusive</code>;<br />
- 'SQLite3 (mem)' stands for the internal <em>SQLite3</em> engine running in
memory;<br />
- 'SQLite3 (ext ...)' is about access to a <em>SQLite3</em> engine as external
database , either as file or memory;<br />
- '<code>TObjectList</code>' indicates a
<code>TSQLRestServerStaticInMemory</code> instance, either static (with no SQL
support) or virtual (i.e. SQL featured via <em>SQLite3</em> virtual table
mechanism) which may persist the data on disk as JSON or compressed
binary;<br />
- 'NexusDB' is the free embedded edition, available from official site;<br />
- 'Jet' stands for a <em>MSAccess</em> database engine, accessed via
OleDB.<br />
- 'Oracle' shows the results of our direct OCI access layer
(<code>SynDBOracle.pas</code>);<br />
- 'Zeos *' indicates that the database was accessed directly via the ZDBC
layer;<br />
- 'FireDAC *' stands for <em>FireDAC</em> library;<br />
- 'UniDAC *' stands for <em>UniDAC</em> library;<br />
- 'BDE *' when using a <em>BDE</em> connection;<br />
- 'ODBC *' for a direct access to ODBC.</p>
<p>This list of database providers is to be extended in the future. Any
feedback is welcome!</p>
<p>Numbers are expressed in rows/second (or objects/second). This benchmark was
compiled with Delphi XE4, since newer compilers tends to give better results,
mainly thanks to function in-lining (which was not existing e.g. in Delphi
6-7).</p>
<p>Note that these tests are not about the relative speed of each database
engine, but reflect the current status of the integration of several DB
libraries within the <em>mORMot</em> database access.</p>
<p>Benchmark was run on a <em>Core i7</em> notebook, running <em>Windows
7</em>, with a standard SSD, including anti-virus and background
applications:<br />
- Linked to a shared <em>Oracle</em> 11.2.0.1 database over 100 Mb
Ethernet;<br />
- <em>MS SQL Express 2008 R2</em> running locally in 64 bit mode;<br />
- <em>IBM DB2 Express-C</em> edition 10.5 running locally in 64 bit mode;<br />
- <em>PostgreSQL</em> 9.2.7 running locally in 64 bit mode;<br />
- <em>MySQL</em> 5.6.16 running locally in 64 bit mode;<br />
- <em>Firebird</em> embedded in revision 2.5.2;<br />
- <em>NexusDB</em> 3.11 in Free Embedded Version.</p>
<p>So it was a development environment, very similar to low-cost production
site, not dedicated to give best performance. During the process, CPU was
noticeable used only for <em>SQLite3</em> in-memory and <em>TObjectList</em> -
most of the time, the bottleneck is not the CPU, but the storage or network. As
a result, rates and timing may vary depending on network and server load, but
you get results similar to what could be expected on customer side, with an
average hardware configuration. When using high-head servers and storage,
running on a tuned <em>Linux</em> configuration, you can expect even better
numbers.</p>
<p>Tests were compiled with the Delphi XE4 32 bit mode target platform. Most of
the tests do pass when compiled as a 64 bit executable, with the exception of
some providers (like Jet), not available on this platform. Speed results are
almost the same, only slightly slower; so we won't show them here.</p>
<p>You can compile the "<code>15 - External DB performance</code>" supplied
sample code, and run the very same benchmark on your own configuration.<br />
Feedback is welcome!</p>
<p>From our tests, the UniDAC version we were using had huge stability issues
when used with DB2: the tests did not pass, and the DB2 server just hang
processing the queries, whereas there was no problem with other libraries. It
may have been fixed since, but you won't find any "UniDAC DB2" results in the
benchmark below in the meanwhile.</p>
<h3>Insertion speed</h3>
<p>Here we insert 5,000 rows of data, with diverse scenarios:<br />
- 'Direct' stands for a individual <code>Client.Add()</code> insertion;<br />
- 'Batch' mode will be described 28;<br />
- 'Trans' indicates that all insertion is nested within a transaction - which
makes a great difference, e.g. with a <em>SQlite3</em> database.</p>
<table>
<tbody>
<tr>
<td> </td>
<td><strong>Direct</strong></td>
<td><strong>Batch</strong></td>
<td><strong>Trans</strong></td>
<td><strong>Batch Trans</strong></td>
</tr>
<tr>
<td><strong>SQLite3 (file full)</strong></td>
<td>462</td>
<td>356</td>
<td>95377</td>
<td>130086</td>
</tr>
<tr>
<td><strong>SQLite3 (file off)</strong></td>
<td>844</td>
<td>821</td>
<td>100389</td>
<td>136675</td>
</tr>
<tr>
<td><strong>SQLite3 (file off exc)</strong></td>
<td>28847</td>
<td>35316</td>
<td>102599</td>
<td>144258</td>
</tr>
<tr>
<td><strong>SQLite3 (mem)</strong></td>
<td>89456</td>
<td>120513</td>
<td>104249</td>
<td>146933</td>
</tr>
<tr>
<td><strong>TObjectList (static)</strong></td>
<td>314465</td>
<td>543892</td>
<td>326370</td>
<td>542652</td>
</tr>
<tr>
<td><strong>TObjectList (virtual)</strong></td>
<td>325393</td>
<td>545672</td>
<td>298846</td>
<td>545018</td>
</tr>
<tr>
<td><strong>SQLite3 (ext full)</strong></td>
<td>424</td>
<td>11297</td>
<td>102049</td>
<td>164636</td>
</tr>
<tr>
<td><strong>SQLite3 (ext off)</strong></td>
<td>830</td>
<td>21406</td>
<td>109706</td>
<td>189250</td>
</tr>
<tr>
<td><strong>SQLite3 (ext off exc)</strong></td>
<td>41589</td>
<td>180759</td>
<td>108481</td>
<td>192071</td>
</tr>
<tr>
<td><strong>SQLite3 (ext mem)</strong></td>
<td>101440</td>
<td>234576</td>
<td>113530</td>
<td>190142</td>
</tr>
<tr>
<td><strong>ODBC SQLite3</strong></td>
<td>492</td>
<td>11746</td>
<td>35367</td>
<td>82425</td>
</tr>
<tr>
<td><strong>ZEOS SQlite3</strong></td>
<td>494</td>
<td>11851</td>
<td>56206</td>
<td>85705</td>
</tr>
<tr>
<td><strong>FireDAC SQlite3</strong></td>
<td>26369</td>
<td>50306</td>
<td>49755</td>
<td>155115</td>
</tr>
<tr>
<td><strong>UniDAC SQlite3</strong></td>
<td>477</td>
<td>8725</td>
<td>26552</td>
<td>38756</td>
</tr>
<tr>
<td><strong>ODBC Firebird</strong></td>
<td>1495</td>
<td>18056</td>
<td>13485</td>
<td>17731</td>
</tr>
<tr>
<td><strong>ZEOS Firebird</strong></td>
<td>9733</td>
<td>13429</td>
<td>26348</td>
<td>30616</td>
</tr>
<tr>
<td><strong>FireDAC Firebird</strong></td>
<td>24233</td>
<td>52021</td>
<td>24791</td>
<td>52111</td>
</tr>
<tr>
<td><strong>UniDAC Firebird</strong></td>
<td>5986</td>
<td>14809</td>
<td>6522</td>
<td>14948</td>
</tr>
<tr>
<td><strong>Jet</strong></td>
<td>4235</td>
<td>4424</td>
<td>4954</td>
<td>5094</td>
</tr>
<tr>
<td><strong>NexusDB</strong></td>
<td>5998</td>
<td>15494</td>
<td>7687</td>
<td>18619</td>
</tr>
<tr>
<td><strong>Oracle</strong></td>
<td>226</td>
<td>56112</td>
<td>1133</td>
<td>52367</td>
</tr>
<tr>
<td><strong>ODBC Oracle</strong></td>
<td>236</td>
<td>1664</td>
<td>1515</td>
<td>7709</td>
</tr>
<tr>
<td><strong>FireDAC Oracle</strong></td>
<td>118</td>
<td>48575</td>
<td>1519</td>
<td>12566</td>
</tr>
<tr>
<td><strong>UniDAC Oracle</strong></td>
<td>164</td>
<td>5701</td>
<td>1215</td>
<td>2884</td>
</tr>
<tr>
<td><strong>BDE Oracle</strong></td>
<td>489</td>
<td>927</td>
<td>839</td>
<td>1022</td>
</tr>
<tr>
<td><strong>MSSQL local</strong></td>
<td>5246</td>
<td>54360</td>
<td>12988</td>
<td>62453</td>
</tr>
<tr>
<td><strong>ODBC MSSQL</strong></td>
<td>4911</td>
<td>18652</td>
<td>11541</td>
<td>20976</td>
</tr>
<tr>
<td><strong>FireDAC MSSQL</strong></td>
<td>5016</td>
<td>7341</td>
<td>11686</td>
<td>51242</td>
</tr>
<tr>
<td><strong>UniDAC MSSQL</strong></td>
<td>4392</td>
<td>29768</td>
<td>8649</td>
<td>33464</td>
</tr>
<tr>
<td><strong>ODBC DB2</strong></td>
<td>4792</td>
<td>48387</td>
<td>14085</td>
<td>70104</td>
</tr>
<tr>
<td><strong>FireDAC DB2</strong></td>
<td>4452</td>
<td>48635</td>
<td>11014</td>
<td>52781</td>
</tr>
<tr>
<td><strong>ZEOS PostgreSQL</strong></td>
<td>4196</td>
<td>26663</td>
<td>9689</td>
<td>38735</td>
</tr>
<tr>
<td><strong>ODBC PostgreSQL</strong></td>
<td>4068</td>
<td>19515</td>
<td>5130</td>
<td>27843</td>
</tr>
<tr>
<td><strong>FireDAC PostgreSQL</strong></td>
<td>4181</td>
<td>37000</td>
<td>10111</td>
<td>36483</td>
</tr>
<tr>
<td><strong>UniDAC PostgreSQL</strong></td>
<td>2705</td>
<td>18563</td>
<td>4442</td>
<td>22317</td>
</tr>
<tr>
<td><strong>ODBC MySQL</strong></td>
<td>3160</td>
<td>38309</td>
<td>10856</td>
<td>47630</td>
</tr>
<tr>
<td><strong>ZEOS MySQL</strong></td>
<td>3426</td>
<td>34037</td>
<td>12217</td>
<td>40186</td>
</tr>
<tr>
<td><strong>FireDAC MySQL</strong></td>
<td>3078</td>
<td>43053</td>
<td>10955</td>
<td>45781</td>
</tr>
<tr>
<td><strong>UniDAC MySQL</strong></td>
<td>3119</td>
<td>27772</td>
<td>11246</td>
<td>33288</td>
</tr>
</tbody>
</table>
<p><img src="http://chart.apis.google.com/chart?chtt=Insertion+speed+%28rows%2Fsecond%29&chxl=1:|Batch+Trans|Trans|Batch|Direct&chxt=x,y&chbh=a&chs=600x500&cht=bhg&chco=3D7930,3D8930,309F30,40C355&chxr=0,0,545672&chds=0,545672,0,545672,0,545672,0,545672,0,545672&chd=t:462,356,95377,130086|844,821,100389,136675|28847,35316,102599,144258|89456,120513,104249,146933|314465,543892,326370,542652|325393,545672,298846,545018|424,11297,102049,164636|830,21406,109706,189250|41589,180759,108481,192071|101440,234576,113530,190142|492,11746,35367,82425|494,11851,56206,85705|26369,50306,49755,155115|477,8725,26552,38756|1495,18056,13485,17731|9733,13429,26348,30616|24233,52021,24791,52111|5986,14809,6522,14948|4235,4424,4954,5094|226,56112,1133,52367|236,1664,1515,7709|118,48575,1519,12566|164,5701,1215,2884|5246,54360,12988,62453|4911,18652,11541,20976|5016,7341,11686,51242|4392,29768,8649,33464|4792,48387,14085,70104|4452,48635,11014,52781|4196,26663,9689,38735|4068,19515,5130,27843|4181,37000,10111,36483|2705,18563,4442,22317|3160,38309,10856,47630|3426,34037,12217,40186|3078,43053,10955,45781|3119,27772,11246,33288&chdl=SQLite3+%28file+full%29|SQLite3+%28file+off%29|SQLite3+%28file+off+exc%29|SQLite3+%28mem%29|TObjectList+%28static%29|TObjectList+%28virtual%29|SQLite3+%28ext+full%29|SQLite3+%28ext+off%29|SQLite3+%28ext+off+exc%29|SQLite3+%28ext+mem%29|ODBC+SQLite3|ZEOS+SQlite3|FireDAC+SQlite3|UniDAC+SQlite3|ODBC+Firebird|ZEOS+Firebird|FireDAC+Firebird|UniDAC+Firebird|Jet|Oracle|ODBC+Oracle|FireDAC+Oracle|UniDAC+Oracle|MSSQL+local|ODBC+MSSQL|FireDAC+MSSQL|UniDAC+MSSQL|ODBC+DB2|FireDAC+DB2|ZEOS+PostgreSQL|ODBC+PostgreSQL|FireDAC+PostgreSQL|UniDAC+PostgreSQL|ODBC+MySQL|ZEOS+MySQL|FireDAC+MySQL|UniDAC+MySQL" /></p>
<p><img src="http://chart.apis.google.com/chart?chtt=Insertion+speed+%28rows%2Fsecond%29&chxl=1:|UniDAC+MySQL|FireDAC+MySQL|ZEOS+MySQL|ODBC+MySQL|UniDAC+PostgreSQL|FireDAC+PostgreSQL|ODBC+PostgreSQL|ZEOS+PostgreSQL|FireDAC+DB2|ODBC+DB2|UniDAC+MSSQL|FireDAC+MSSQL|ODBC+MSSQL|MSSQL+local|UniDAC+Oracle|FireDAC+Oracle|ODBC+Oracle|Oracle|Jet|UniDAC+Firebird|FireDAC+Firebird|ZEOS+Firebird|ODBC+Firebird|UniDAC+SQlite3|FireDAC+SQlite3|ZEOS+SQlite3|ODBC+SQLite3|SQLite3+%28ext+mem%29|SQLite3+%28ext+off+exc%29|SQLite3+%28ext+off%29|SQLite3+%28ext+full%29|TObjectList+%28virtual%29|TObjectList+%28static%29|SQLite3+%28mem%29|SQLite3+%28file+off+exc%29|SQLite3+%28file+off%29|SQLite3+%28file+full%29&chxt=x,y&chbh=a&chs=600x500&cht=bhg&chco=3D7930,3D8930,309F30,40C355&chxr=0,0,545672&chds=0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672,0,545672&chd=t:462,844,28847,89456,314465,325393,424,830,41589,101440,492,494,26369,477,1495,9733,24233,5986,4235,226,236,118,164,5246,4911,5016,4392,4792,4452,4196,4068,4181,2705,3160,3426,3078,3119|356,821,35316,120513,543892,545672,11297,21406,180759,234576,11746,11851,50306,8725,18056,13429,52021,14809,4424,56112,1664,48575,5701,54360,18652,7341,29768,48387,48635,26663,19515,37000,18563,38309,34037,43053,27772|95377,100389,102599,104249,326370,298846,102049,109706,108481,113530,35367,56206,49755,26552,13485,26348,24791,6522,4954,1133,1515,1519,1215,12988,11541,11686,8649,14085,11014,9689,5130,10111,4442,10856,12217,10955,11246|130086,136675,144258,146933,542652,545018,164636,189250,192071,190142,82425,85705,155115,38756,17731,30616,52111,14948,5094,52367,7709,12566,2884,62453,20976,51242,33464,70104,52781,38735,27843,36483,22317,47630,40186,45781,33288&chdl=Direct|Batch|Trans|Batch+Trans" /></p>
<p>Due to its ACID implementation, <em>SQLite3</em> process on file waits for
the hard-disk to have finished flushing its data, therefore it is the reason
why it is slower than other engines at individual row insertion (less than 10
objects per second with a mechanical hardrive instead of a SDD) outside the
scope of a transaction.</p>
<p>So if you want to reach the best writing performance in your application
with the default engine, you should better use transactions and regroup all
writing into services or a BATCH process. Another possibility could be to
execute <code>DB.Synchronous := smOff</code> and/or <code>DB.LockingMode :=
lmExclusive</code> at <em>SQLite3</em> engine level before process: in case of
power loss at wrong time it may corrupt the database file, but it will increase
the rate by a factor of 50 (with hard drive), <a href="https://blog.synopse.info?post/post/2013/06/14/SQLite3-performance-in-Exclusive-file-locking-mode">as stated
by the "<em>off</em>" and "<em>off exc</em>" rows of the table</a>. Note that
by default, the <em>FireDAC</em> library set both options, so results above are
to be compared with "<em>SQLite3 off exc</em>" rows.</p>
<p>For both our direct <em>Oracle</em> access <code>SynDBOracle.pas</code> unit
and <em>FireDAC</em> library , BATCH process benefits of the array binding
feature a lot (known as <em>Array DML</em> in <em>FireDAC/AnyDAC</em>).</p>
<p>For most engines, our ORM kernel is able to generate the appropriate SQL
statement for speeding up bulk insertion. For instance:<br />
- <em>SQlite3, MySQL, PostgreSQL, MSSQL 2008, DB2, MySQL</em> or
<em>NexusDB</em> handle <code>INSERT</code> statements with multiple
<code>INSERT INTO .. VALUES (..),(..),(..)..</code>;<br />
- <em>Oracle</em> handles <code>INSERT INTO .. INTO .. SELECT 1 FROM
DUAL</code> (weird syntax, isn't it?);<br />
- <em>Firebird</em> implements <code>EXECUTE BLOCK</code>.</p>
<p>As a result, some engines show a nice speed boost when
<code>BatchAdd()</code> is used. Even <em>SQLite3</em> is faster when used as
external engine, in respect to direct execution! This feature is at ORM/SQL
level, so it benefits to any external database library. Of course, if a given
library has a better implementation pattern (e.g. our direct <em>Oracle</em> or
<em>FireDAC</em> with native array binding), it is used instead.</p>
<h3>Reading speed</h3>
<p>Now the same data is retrieved via the ORM layer:<br />
- 'By one' states that one object is read per call (ORM generates a
<code>SELECT * FROM table WHERE ID=?</code> for <code>Client.Retrieve()</code>
method);<br />
- 'All *' is when all 5000 objects are read in a single call (i.e. running
<code>SELECT * FROM table</code> from a <code>FillPrepare()</code> method
call), either forced to use the virtual table layer, or with direct static
call.</p>
<p>Here are some reading speed values, in objects/second:</p>
<table>
<tbody>
<tr>
<td> </td>
<td><strong>By one</strong></td>
<td><strong>All Virtual</strong></td>
<td><strong>All Direct</strong></td>
</tr>
<tr>
<td><strong>SQLite3 (file full)</strong></td>
<td>27284</td>
<td>558721</td>
<td>550842</td>
</tr>
<tr>
<td><strong>SQLite3 (file off)</strong></td>
<td>26896</td>
<td>549450</td>
<td>526149</td>
</tr>
<tr>
<td><strong>SQLite3 (file off exc)</strong></td>
<td>128077</td>
<td>557537</td>
<td>535905</td>
</tr>
<tr>
<td><strong>SQLite3 (mem)</strong></td>
<td>127106</td>
<td>557537</td>
<td>563316</td>
</tr>
<tr>
<td><strong>TObjectList (static)</strong></td>
<td>300012</td>
<td>912408</td>
<td>913742</td>
</tr>
<tr>
<td><strong>TObjectList (virtual)</strong></td>
<td>303287</td>
<td>402706</td>
<td>866551</td>
</tr>
<tr>
<td><strong>SQLite3 (ext full)</strong></td>
<td>135380</td>
<td>267436</td>
<td>553158</td>
</tr>
<tr>
<td><strong>SQLite3 (ext off)</strong></td>
<td>133696</td>
<td>262977</td>
<td>543065</td>
</tr>
<tr>
<td><strong>SQLite3 (ext off exc)</strong></td>
<td>134698</td>
<td>264186</td>
<td>558596</td>
</tr>
<tr>
<td><strong>SQLite3 (ext mem)</strong></td>
<td>137487</td>
<td>259713</td>
<td>557475</td>
</tr>
<tr>
<td><strong>ODBC SQLite3</strong></td>
<td>19461</td>
<td>136600</td>
<td>201280</td>
</tr>
<tr>
<td><strong>ZEOS SQlite3</strong></td>
<td>33541</td>
<td>200835</td>
<td>306955</td>
</tr>
<tr>
<td><strong>FireDAC SQlite3</strong></td>
<td>7683</td>
<td>83532</td>
<td>112470</td>
</tr>
<tr>
<td><strong>UniDAC SQlite3</strong></td>
<td>2522</td>
<td>74030</td>
<td>96420</td>
</tr>
<tr>
<td><strong>ODBC Firebird</strong></td>
<td>3446</td>
<td>69607</td>
<td>97585</td>
</tr>
<tr>
<td><strong>ZEOS Firebird</strong></td>
<td>20296</td>
<td>91974</td>
<td>107229</td>
</tr>
<tr>
<td><strong>FireDAC Firebird</strong></td>
<td>2376</td>
<td>46276</td>
<td>56269</td>
</tr>
<tr>
<td><strong>UniDAC Firebird</strong></td>
<td>2189</td>
<td>66886</td>
<td>88102</td>
</tr>
<tr>
<td><strong>Jet</strong></td>
<td>2640</td>
<td>166112</td>
<td>258277</td>
</tr>
<tr>
<td><strong>NexusDB</strong></td>
<td>1413</td>
<td>120845</td>
<td>208246</td>
</tr>
<tr>
<td><strong>Oracle</strong></td>
<td>1558</td>
<td>120977</td>
<td>159861</td>
</tr>
<tr>
<td><strong>ODBC Oracle</strong></td>
<td>1620</td>
<td>43441</td>
<td>45764</td>
</tr>
<tr>
<td><strong>FireDAC Oracle</strong></td>
<td>1231</td>
<td>42149</td>
<td>54795</td>
</tr>
<tr>
<td><strong>UniDAC Oracle</strong></td>
<td>688</td>
<td>27083</td>
<td>30093</td>
</tr>
<tr>
<td><strong>BDE Oracle</strong></td>
<td>860</td>
<td>3870</td>
<td>4036</td>
</tr>
<tr>
<td><strong>MSSQL local</strong></td>
<td>10135</td>
<td>210837</td>
<td>437905</td>
</tr>
<tr>
<td><strong>ODBC MSSQL</strong></td>
<td>12458</td>
<td>147544</td>
<td>256502</td>
</tr>
<tr>
<td><strong>FireDAC MSSQL</strong></td>
<td>3776</td>
<td>72123</td>
<td>94091</td>
</tr>
<tr>
<td><strong>UniDAC MSSQL</strong></td>
<td>2505</td>
<td>93231</td>
<td>135932</td>
</tr>
<tr>
<td><strong>ODBC DB2</strong></td>
<td>7649</td>
<td>84880</td>
<td>124486</td>
</tr>
<tr>
<td><strong>FireDAC DB2</strong></td>
<td>3155</td>
<td>71456</td>
<td>88264</td>
</tr>
<tr>
<td><strong>ZEOS PostgreSQL</strong></td>
<td>8833</td>
<td>158760</td>
<td>223583</td>
</tr>
<tr>
<td><strong>ODBC PostgreSQL</strong></td>
<td>10361</td>
<td>85680</td>
<td>120913</td>
</tr>
<tr>
<td><strong>FireDAC PostgreSQL</strong></td>
<td>2261</td>
<td>58252</td>
<td>79002</td>
</tr>
<tr>
<td><strong>UniDAC PostgreSQL</strong></td>
<td>864</td>
<td>86900</td>
<td>122856</td>
</tr>
<tr>
<td><strong>ODBC MySQL</strong></td>
<td>10143</td>
<td>65538</td>
<td>82447</td>
</tr>
<tr>
<td><strong>ZEOS MySQL</strong></td>
<td>2052</td>
<td>171803</td>
<td>245772</td>
</tr>
<tr>
<td><strong>FireDAC MySQL</strong></td>
<td>3636</td>
<td>75081</td>
<td>105028</td>
</tr>
<tr>
<td><strong>UniDAC MySQL</strong></td>
<td>4798</td>
<td>99940</td>
<td>146968</td>
</tr>
</tbody>
</table>
<p><img src="http://chart.apis.google.com/chart?chtt=Read+speed+%28rows%2Fsecond%29&chxl=1:|All+Direct|All+Virtual|By+one&chxt=x,y&chbh=a&chs=600x500&cht=bhg&chco=3D7930,3D8930,309F30,40C355&chxr=0,0,913742&chds=0,913742,0,913742,0,913742&chd=t:27284,558721,550842|26896,549450,526149|128077,557537,535905|127106,557537,563316|300012,912408,913742|303287,402706,866551|135380,267436,553158|133696,262977,543065|134698,264186,558596|137487,259713,557475|19461,136600,201280|33541,200835,306955|7683,83532,112470|2522,74030,96420|3446,69607,97585|20296,91974,107229|2376,46276,56269|2189,66886,88102|2640,166112,258277|1558,120977,159861|1620,43441,45764|1231,42149,54795|688,27083,30093|10135,210837,437905|12458,147544,256502|3776,72123,94091|2505,93231,135932|7649,84880,124486|3155,71456,88264|8833,158760,223583|10361,85680,120913|2261,58252,79002|864,86900,122856|10143,65538,82447|2052,171803,245772|3636,75081,105028|4798,99940,146968&chdl=SQLite3+%28file+full%29|SQLite3+%28file+off%29|SQLite3+%28file+off+exc%29|SQLite3+%28mem%29|TObjectList+%28static%29|TObjectList+%28virtual%29|SQLite3+%28ext+full%29|SQLite3+%28ext+off%29|SQLite3+%28ext+off+exc%29|SQLite3+%28ext+mem%29|ODBC+SQLite3|ZEOS+SQlite3|FireDAC+SQlite3|UniDAC+SQlite3|ODBC+Firebird|ZEOS+Firebird|FireDAC+Firebird|UniDAC+Firebird|Jet|Oracle|ODBC+Oracle|FireDAC+Oracle|UniDAC+Oracle|MSSQL+local|ODBC+MSSQL|FireDAC+MSSQL|UniDAC+MSSQL|ODBC+DB2|FireDAC+DB2|ZEOS+PostgreSQL|ODBC+PostgreSQL|FireDAC+PostgreSQL|UniDAC+PostgreSQL|ODBC+MySQL|ZEOS+MySQL|FireDAC+MySQL|UniDAC+MySQL" /></p>
<p><img src="http://chart.apis.google.com/chart?chtt=Read+speed+%28rows%2Fsecond%29&chxl=1:|UniDAC+MySQL|FireDAC+MySQL|ZEOS+MySQL|ODBC+MySQL|UniDAC+PostgreSQL|FireDAC+PostgreSQL|ODBC+PostgreSQL|ZEOS+PostgreSQL|FireDAC+DB2|ODBC+DB2|UniDAC+MSSQL|FireDAC+MSSQL|ODBC+MSSQL|MSSQL+local|UniDAC+Oracle|FireDAC+Oracle|ODBC+Oracle|Oracle|Jet|UniDAC+Firebird|FireDAC+Firebird|ZEOS+Firebird|ODBC+Firebird|UniDAC+SQlite3|FireDAC+SQlite3|ZEOS+SQlite3|ODBC+SQLite3|SQLite3+%28ext+mem%29|SQLite3+%28ext+off+exc%29|SQLite3+%28ext+off%29|SQLite3+%28ext+full%29|TObjectList+%28virtual%29|TObjectList+%28static%29|SQLite3+%28mem%29|SQLite3+%28file+off+exc%29|SQLite3+%28file+off%29|SQLite3+%28file+full%29&chxt=x,y&chbh=a&chs=600x500&cht=bhg&chco=3D7930,3D8930,309F30,40C355&chxr=0,0,913742&chds=0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742,0,913742&chd=t:27284,26896,128077,127106,300012,303287,135380,133696,134698,137487,19461,33541,7683,2522,3446,20296,2376,2189,2640,1558,1620,1231,688,10135,12458,3776,2505,7649,3155,8833,10361,2261,864,10143,2052,3636,4798|558721,549450,557537,557537,912408,402706,267436,262977,264186,259713,136600,200835,83532,74030,69607,91974,46276,66886,166112,120977,43441,42149,27083,210837,147544,72123,93231,84880,71456,158760,85680,58252,86900,65538,171803,75081,99940|550842,526149,535905,563316,913742,866551,553158,543065,558596,557475,201280,306955,112470,96420,97585,107229,56269,88102,258277,159861,45764,54795,30093,437905,256502,94091,135932,124486,88264,223583,120913,79002,122856,82447,245772,105028,146968&chdl=By+one|All+Virtual|All+Direct" /></p>
<p>The <em>SQLite3</em> layer gives amazing reading results, which makes it a
perfect fit for most typical ORM use. When running with <code>DB.LockingMode :=
lmExclusive</code> defined (i.e. "off exc" rows), reading speed is very high,
and benefits from exclusive access to the database file. External database
access is only required when data is expected to be shared with other
processes.</p>
<p>In the above table, it appears that all libraries based on
<code>DB.pas</code> are slower than the others for reading speed. In fact,
<code>TDataSet</code> sounds to be a real bottleneck, due to its internal data
marshalling. Even <em>FireDAC</em>, which is known to be very optimized for
speed, is limited by the <code>TDataSet</code> structure. Our direct classes,
or even ZEOS/ZDBC performs better, since they are able to output JSON content
with no additional marshalling.</p>
<p>For both writing and reading, <code>TObjectList</code> /
<code>TSQLRestServerStaticInMemory</code> engine gives impressive results, but
has the weakness of being in-memory, so it is not ACID by design, and the data
has to fit in memory. Note that indexes are available for IDs and <code>stored
AS_UNIQUE</code> properties.</p>
<p>As a consequence, search of non-unique values may be slow: the engine has to
loop through all rows of data. But for unique values (defined as <code>stored
AS_UNIQUE</code>), both insertion and search speed is awesome, due to its
optimized O(1) hash algorithm - see the following benchmark, especially the
"<em>By name</em>" row for "<em>TObjectList</em>" columns, which correspond to
a search of an unique <code>RawUTF8</code> property value via this hashing
method.</p>
<table>
<tbody>
<tr>
<td><strong>SQLite3 (file full)</strong></td>
<td><strong>SQLite3 (file off)</strong></td>
<td><strong>SQLite3 (mem)</strong></td>
<td><strong>TObjectList (static)</strong></td>
<td><strong>TObjectList (virt.)</strong></td>
<td><strong>SQLite3 (ext file full)</strong></td>
<td><strong>SQLite3 (ext file off)</strong></td>
<td><strong>SQLite3 (ext mem)</strong></td>
<td><strong>Oracle</strong></td>
<td><strong>Jet</strong></td>
</tr>
<tr>
<td><strong>By one</strong></td>
<td>10461</td>
<td>10549</td>
<td>44737</td>
<td>103577</td>
<td>103553</td>
<td>43367</td>
<td>44099</td>
<td>45220</td>
<td>901</td>
<td>1074</td>
</tr>
<tr>
<td><strong>By name</strong></td>
<td>9694</td>
<td>9651</td>
<td>32350</td>
<td>70534</td>
<td>60153</td>
<td>22785</td>
<td>22240</td>
<td>23055</td>
<td>889</td>
<td>1071</td>
</tr>
<tr>
<td><strong>All Virt.</strong></td>
<td>167095</td>
<td>162956</td>
<td>168651</td>
<td>253292</td>
<td>118203</td>
<td>97083</td>
<td>90592</td>
<td>94688</td>
<td>56639</td>
<td>52764</td>
</tr>
<tr>
<td><strong>All Direct</strong></td>
<td>167123</td>
<td>144250</td>
<td>168577</td>
<td>254284</td>
<td>256383</td>
<td>170794</td>
<td>165601</td>
<td>168856</td>
<td>88342</td>
<td>75999</td>
</tr>
</tbody>
</table>
<p>Above table results were run on a Core 2 duo laptop, so numbers are lower
than with the previous tables.</p>
<p>During the tests, <a href="https://blog.synopse.info?post/post/2012/02/14/ORM-cache">internal caching</a>
was disabled, so you may expect speed enhancements for real applications, when
data is more read than written: for instance, when an object is retrieved from
the cache, you achieve more than 1,00,000 read requests per second, whatever
database is used.</p>
<h3>Analysis and use case proposal</h3>
<p>When declared as virtual table (via a <code>VirtualTableRegister</code>
call), you have the full power of SQL (including JOINs) at hand, with
incredibly fast CRUD operations: 100,000 requests per second for objects read
and write, including serialization and Client-Server communication!</p>
<p>Some providers are first-class citizens to <em>mORMot</em>, like
<em>SQLite3</em>, <em>Oracle</em>, <em>MS SQL</em>, PostgreSQL, MySQL or IBM
DB2. You can connect to them without the bottleneck of the <code>DB.pas</code>
unit, nor any restriction of your Delphi license (a <em>Starter edition</em> is
enough).</p>
<p>First of all, <em>SQLite3</em> is still to be considered, even for a
production server. Thanks to <em>mORMot</em>'s architecture and design, this
"embedded" database could be used as main database engine for a client-server
application with heavy concurrent access - if you have doubts about its scaling
abilities, <a href="https://blog.synopse.info?post/post/2013/09/10/Thread-safety-of-mORMot">see this blog
article</a>. Here, "embedded" is not restricted to "mobile", but sounds like a
self-contained, zero-configuration proven engine.</p>
<p>Most recognized <em>closed source</em> databases are available:<br />
- Direct access to <em>Oracle</em> gives impressive results in BATCH mode (aka
array binding). It may be an obligation if your end-customer stores already its
data in such a server, for instance, and want to leverage the licensing cost of
its own IT solution. <em>Oracle Express</em> edition is free, but somewhat
heavy and limited in terms of data/hardware size (see its licensing
terms);<br />
- <em>MS SQL Server</em>, directly accessed via <em>OleDB</em> (or
<em>ODBC</em>) gives pretty good timing. A <em>MS SQL Server 2008 R2
Express</em> instance is pretty well integrated with the <em>Windows</em>
environment, for a very affordable price (i.e. for free) - the <em>LocalDB</em>
(MSI installer) edition is enough to start with, but also with data/hardware
size limitation, just like <em>Oracle Express</em>;<br />
- IBM <em>DB2</em> is another good candidate, and the <em>Express-C</em> ("C"
standing for Community) offers a no-charge opportunity to run an industry
standard engine, with no restriction on the data size, and somewhat high
hardware limitations (16 GB of RAM and 2 CPU cores for the latest 10.5 release)
or enterprise-level features;<br />
- <em>NexusDB</em> may be considered, if you have existing Delphi code and data
- but it is less known and recognized as the its commercial competitors.</p>
<p><em>Open Source</em> databases are worth considering, especially in
conjunction with an Open Source framework like <em>mORMot</em>:<br />
- <em>MySQL</em> is the well-known engine used by a lot of web sites, mainly
with <em>LAMP</em> (<em>Linux MySQL Apache PHP</em>) configurations. Windows is
not the best platform to run it, but it could be a fairly good candidate,
especially in its <em>MariaDB</em> fork, which sounds more attractive those
days than the official main version, owned by Oracle;<br />
- <em>PostgreSQL</em> is an Enterprise class database, with amazing features
among its Open Source alternatives, and really competes with commercial
solutions. Even under Windows, we think it is easy to install and administrate,
and uses less resource than the other commercial engines.<br />
- <em>Firebird</em> gave pretty consistent timing, when accessed via Zeos/ZDBC.
We show here the embedded version, but the server edition is worth considering,
even if it has a less</p>
<p>To access those databases, OleDB, ODBC or ZDBC providers may also be used,
with direct access. But <em>mORMot</em> is very open-minded: you can use any
<code>DB.pas</code> provider, e.g. <em>FireDAC</em>, <em>UniDAC</em>,
<em>DBExpress</em>, <em>NexusDB</em> or even the <em>BDE</em>, but with the
additional layer introduced by using a <code>TDataSet</code> instance, at
reading.</p>
<p>Therefore, the typical use may be the following (<em>int.</em> meaning
"internal", <em>ext.</em> for "external", <em>mem</em> for "in-memory"):</p>
<table>
<tbody>
<tr>
<td><strong>Database</strong></td>
<td><strong>Use case</strong></td>
</tr>
<tr>
<td>int. SQLite3 file</td>
<td>Created by default.<br />
General safe data handling, with amazing speed in "off exc" mode</td>
</tr>
<tr>
<td>int. SQLite3 mem</td>
<td>Created with <code>:memory:</code> file name.<br />
Fast data handling with no persistence (e.g. for testing or temporary
storage)</td>
</tr>
<tr>
<td><code>TObjectList</code> static</td>
<td>Created with <code>StaticDataCreate</code>.<br />
Best possible performance for small amount of data, without ACID nor SQL</td>
</tr>
<tr>
<td><code>TObjectList</code> virtual</td>
<td>Created with <code>VirtualTableRegister</code>.<br />
Best possible performance for SQL over small amount of data (or even unlimited
amount under Win64), if ACID is not required nor complex SQL</td>
</tr>
<tr>
<td>ext. SQLite3 file</td>
<td>Created with <code>VirtualTableExternalRegister<br /></code>External
back-end, e.g. for disk spanning</td>
</tr>
<tr>
<td>ext. SQLite3 mem</td>
<td>Created with <code>VirtualTableExternalRegister<br /></code>Fast external
back-end (e.g. for testing)</td>
</tr>
<tr>
<td>ext. Oracle / MS SQL / DB2 / PostgreSQL / MySQL / Firebird</td>
<td>Created with <code>VirtualTableExternalRegister<br /></code>Fast, secure
and industry standard back-ends; data can be shared outside
<em>mORMot</em></td>
</tr>
<tr>
<td>ext. NexusDB</td>
<td>Created with <code>VirtualTableExternalRegister<br /></code>The free
embedded version let the whole engine be included within your executable, and
use any existing code, but <em>SQlite3</em> sounds like a better option</td>
</tr>
<tr>
<td>ext. Jet</td>
<td>Created with <code>VirtualTableExternalRegister<br /></code>Could be used
as a data exchange format (e.g. with Office applications)</td>
</tr>
<tr>
<td>ext. Zeos</td>
<td>Created with <code>VirtualTableExternalRegister<br /></code>Allow access to
several external engines, with direct Zeos/ZDBC access which will by-pass the
<code>DB.pas</code> unit and its <code>TDataSet</code> bottleneck - and we will
also prefer an active Open Source project!</td>
</tr>
<tr>
<td>ext. FireDAC/UniDAC</td>
<td>Created with <code>VirtualTableExternalRegister<br /></code>Allow access to
several external engines, including the <code>DB.pas</code> unit and its
<code>TDataSet</code> bottleneck</td>
</tr>
</tbody>
</table>
<p>Whatever database back-end is used, don't forget that <em>mORMot</em> design
will allow you to switch from one library to another, just by changing a
<code>TSQLDBConnectionProperties</code> class type. And note that you can
<em>mix</em> external engines, on purpose: you are not tied to one single
engine, but the database access can be tuned for each ORM table, according to
your project needs.</p>
<p>Feedback is <a href="http://synopse.info/forum/viewtopic.php?id=1642">welcome in our forum, as
usual</a>.</p>