<?xml version="1.0" encoding="latin1" ?>
<!DOCTYPE chapter SYSTEM "chapter.dtd">
<chapter>
<header>
<copyright>
<year>2002</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
The contents of this file are subject to the Erlang Public License,
Version 1.1, (the "License"); you may not use this file except in
compliance with the License. You should have received a copy of the
Erlang Public License along with this software. If not, it can be
retrieved online at http://www.erlang.org/.
Software distributed under the License is distributed on an "AS IS"
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
the License for the specific language governing rights and limitations
under the License.
</legalnotice>
<title>Databases</title>
<prepared>Ingela Anderton</prepared>
<responsible></responsible>
<docno></docno>
<approved></approved>
<checked></checked>
<date></date>
<rev></rev>
<file>databases.xml</file>
</header>
<section>
<title>Databases</title>
<p>If you need to access a relational database such as
<c>sqlserver</c>, <c>mysql</c>, <c>postgres</c>, <c>oracle</c>,
<c>cybase</c> etc. from your erlang application using the Erlang
ODBC interface is a good way to go about it.</p>
<p></p>
<p>The Erlang ODBC application should work for any relational
database that has an ODBC driver. But currently it is only
regularly tested for <c>sqlserver</c> and <c>postgres</c>.</p>
</section>
<section>
<title>Database independence </title>
<p>The Erlang ODBC interface is in principal database
independent, e.i. an erlang program using the interface could be
run without changes towards different databases. But as SQL is
used it is alas possible to write database dependent
programs. Even though SQL is an ANSI-standard meant to be
database independent, different databases have proprietary
extensions to SQL defining their own data types. If you keep to
the ANSI data types you will minimize the problem. But
unfortunately there is no guarantee that all databases actually
treats the ANSI data types equivalently. For instance an
installation of <c>Oracle Enterprise release 8.0.5.0.0 for unix</c> will accept that you create a table column with the
ANSI data type <c>integer</c>, but when retrieving values from
this column the driver reports that it is of type
<c>SQL_DECIMAL(0, 38)</c> and not <c>SQL_INTEGER</c> as you may have
expected. </p>
<p>Another obstacle is that some drivers do not support scrollable
cursors which has the effect that the only way to traverse the
result set is sequentially, with next, from the first row to the
last, and once you pass a row you can not go back. This means
that some functions in the interface will not work together with
certain drivers. A similar problem is that not all drivers
support "row count" for select queries, hence resulting in that
the function <c>select_count/[3,4]</c> will return <c>{ok, undefined}</c> instead of <c>{ok, NrRows}</c> where
<c>NrRows</c> is the number of rows in the result set.</p>
</section>
<section>
<title>Data types </title>
<p>The following is a list of the ANSI data types. For details
turn to the ANSI standard documentation. Usage of other data types
is of course possible, but you should be aware that this makes your
application dependent on the database you are using at the moment.</p>
<list type="bulleted">
<item>CHARACTER (size), CHAR (size)</item>
<item>NUMERIC (precision, scale), DECIMAL (precision, scale), DEC
(precision, scale ) precision - total number of digits, scale
- total number of decimal places</item>
<item>INTEGER, INT, SMALLINT</item>
<item>FLOAT (precision)</item>
<item>REAL</item>
<item>DOUBLE PRECISION</item>
<item>CHARACTER VARYING(size), CHAR VARYING(size)</item>
</list>
<p>When inputting data using sql_query/[2,3] the values will
always be in string format as they are part of an SQL-query.
Example:</p>
<code type="none">
odbc:sql_query(Ref, "INSERT INTO TEST VALUES(1, 2, 3)").
</code>
<note>
<p>Note that when the value of the data to input is a string, it
has to be quoted with <c>'</c>. Example: </p>
<code type="none">
odbc:sql_query(Ref, "INSERT INTO EMPLOYEE VALUES(1, 'Jane', 'Doe', 'F')").
</code>
</note>
<p>You may also input data using <seealso marker="odbc#param_query">param_query/[3,4]</seealso> and then
the input data will have the Erlang type corresponding to the
ODBC type of the column.<seealso marker="#type">See ODBC to Erlang mapping</seealso></p>
<p> <marker id="type"></marker>
When selecting data from a table, all data
types are returned from the database to the ODBC driver as an
ODBC data type. The tables below shows the mapping between those
data types and what is returned by the Erlang API.</p>
<table>
<row>
<cell align="left" valign="middle">ODBC Data Type </cell>
<cell align="left" valign="middle">Erlang Data Type </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_CHAR(size)</cell>
<cell align="left" valign="middle">String | Binary (configurable)</cell>
</row>
<row>
<cell align="left" valign="middle">SQL_WCHAR(size) </cell>
<cell align="left" valign="middle">Unicode binary encoded as UTF16 little endian.</cell>
</row>
<row>
<cell align="left" valign="middle">SQL_NUMERIC(p,s) <br></br>
when (p >= 0 and p <= 9 and s == 0) </cell>
<cell align="left" valign="middle">Integer </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_NUMERIC(p,s) <br></br>
when (p >= 10 and p <= 15 and s == 0) or (s <= 15 and s > 0)</cell>
<cell align="left" valign="middle">Float </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_NUMERIC(p,s) <br></br>
when p >= 16 </cell>
<cell align="left" valign="middle">String </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_DECIMAL(p,s) <br></br>
when (p >= 0 and p <= 9 and s == 0) </cell>
<cell align="left" valign="middle">Integer </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_DECIMAL(p,s) <br></br>
when (p >= 10 and p <= 15 and s == 0) or (s <= 15 and s > 0)</cell>
<cell align="left" valign="middle">Float </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_DECIMAL(p,s) <br></br>
when p >= 16 </cell>
<cell align="left" valign="middle">String </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_INTEGER </cell>
<cell align="left" valign="middle">Integer </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_SMALLINT </cell>
<cell align="left" valign="middle">Integer </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_FLOAT </cell>
<cell align="left" valign="middle">Float </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_REAL </cell>
<cell align="left" valign="middle">Float </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_DOUBLE</cell>
<cell align="left" valign="middle">Float</cell>
</row>
<row>
<cell align="left" valign="middle">SQL_VARCHAR(size) </cell>
<cell align="left" valign="middle">String | Binary (configurable)</cell>
</row>
<row>
<cell align="left" valign="middle">SQL_WVARCHAR(size) </cell>
<cell align="left" valign="middle">Unicode binary encoded as UTF16 little endian.</cell>
</row>
<tcaption>Mapping of ODBC data types to the Erlang data types returned to the Erlang application.</tcaption>
</table>
<table>
<row>
<cell align="left" valign="middle">ODBC Data Type </cell>
<cell align="left" valign="middle">Erlang Data Type </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_TYPE_DATE </cell>
<cell align="left" valign="middle">String </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_TYPE_TIME </cell>
<cell align="left" valign="middle">String </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_TYPE_TIMESTAMP </cell>
<cell align="left" valign="middle"> {{YY, MM, DD}, {HH, MM, SS}} </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_LONGVARCHAR </cell>
<cell align="left" valign="middle">String | Binary (configurable)</cell>
</row>
<row>
<cell align="left" valign="middle">SQL_WLONGVARCHAR(size) </cell>
<cell align="left" valign="middle">Unicode binary encoded as UTF16 little endian.</cell>
</row>
<row>
<cell align="left" valign="middle">SQL_BINARY</cell>
<cell align="left" valign="middle">String | Binary (configurable)</cell>
</row>
<row>
<cell align="left" valign="middle">SQL_VARBINARY</cell>
<cell align="left" valign="middle">String | Binary (configurable)</cell>
</row>
<row>
<cell align="left" valign="middle">SQL_LONGVARBINARY</cell>
<cell align="left" valign="middle">String | Binary (configurable)</cell>
</row>
<row>
<cell align="left" valign="middle">SQL_TINYINT </cell>
<cell align="left" valign="middle">Integer </cell>
</row>
<row>
<cell align="left" valign="middle">SQL_BIT</cell>
<cell align="left" valign="middle">Boolean </cell>
</row>
<tcaption>Mapping of extended ODBC data types to the Erlang data types returned to the Erlang application.</tcaption>
</table>
<note>
<p>To find out which data types will be returned for the
columns in a table use the function <seealso marker="odbc#describe_table">describe_table/[2,3]</seealso></p>
</note>
</section>
<section>
<title>Batch handling</title>
<p>Grouping of SQL queries can be desirable in order to reduce
network traffic. Another benefit can be that the data source
sometimes can optimize execution of a batch of SQL queries.</p>
<p>Explicit batches an procedures described below will result
in multiple results being returned from sql_query/[2,3].
while with parameterized queries only one result will be returned
from param_query/[2,3].</p>
<section>
<title>Explicit batches</title>
<p>The most basic form of a batch is created by semicolons
separated SQL queries, for example:</p>
<code type="none">
"SELECT * FROM FOO; SELECT * FROM BAR" or
"INSERT INTO FOO VALUES(1,'bar'); SELECT * FROM FOO"
</code>
</section>
<section>
<title>Procedures </title>
<p>Different databases may also support creating of procedures
that contains more than one SQL query. For example, the
following SQLServer-specific statement creates a procedure that
returns a result set containing information about employees
that work at the department and a result set listing the
customers of that department. </p>
<code type="none">
CREATE PROCEDURE DepartmentInfo (@DepartmentID INT) AS
SELECT * FROM Employee WHERE department = @DepartmentID
SELECT * FROM Customers WHERE department = @DepartmentID
</code>
</section>
<section>
<title>Parameterized queries</title>
<p>To effectively perform a batch of similar queries, you can use
parameterized queries. This means that you in your SQL query
string will mark the places that usually would contain values
with question marks and then provide lists of values for each
parameter. For instance you can use this to insert multiple
rows into the <c>EMPLOYEE</c> table while executing only a
single SQL statement, for example code see <seealso marker="getting_started#param_query">"Using the Erlang API"</seealso> section in the "Getting Started" chapter.</p>
</section>
</section>
</chapter>