deleting old folder after move
[geo.git] / edit_wp5_web_folder / geo / curves / inc / adodb5 / docs / tips_portable_sql.htm
diff --git a/edit_wp5_web_folder/geo/curves/inc/adodb5/docs/tips_portable_sql.htm b/edit_wp5_web_folder/geo/curves/inc/adodb5/docs/tips_portable_sql.htm
deleted file mode 100644 (file)
index aa75069..0000000
+++ /dev/null
@@ -1,367 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">\r
-\r
-<html>\r
-<head>\r
-       <title>Tips on Writing Portable SQL for Multiple Databases for PHP</title>\r
-</head>\r
-\r
-<body bgcolor=white>\r
-<table width=100% border=0><tr><td><h2>Tips on Writing Portable SQL &nbsp;</h2></td><td>\r
- <div align=right><img src="cute_icons_for_site/adodb.gif"></div></td></tr></table>\r
-  <p>Updated 6 Oct 2006. Added OffsetDate example.\r
-  <p>Updated 18 Sep 2003. Added Portable Native SQL section.\r
-<p>\r
-\r
- If you are writing an application that is used in multiple environments and \r
-  operating systems, you need to plan to support multiple databases. This article \r
-  is based on my experiences with multiple database systems, stretching from 4th \r
-  Dimension in my Mac days, to the databases I currently use, which are: Oracle, \r
-  FoxPro, Access, MS SQL Server and MySQL. Although most of the advice here applies \r
-  to using SQL with Perl, Python and other programming languages, I will focus on PHP and how \r
-  the <a href="http://adodb.sourceforge.net/">ADOdb</a> database abstraction library \r
-  offers some solutions.<p></p>\r
-<p>Most database vendors practice product lock-in. The best or fastest way to \r
-  do things is often implemented using proprietary extensions to SQL. This makes \r
-  it extremely hard to write portable SQL code that performs well under all conditions. \r
-  When the first ANSI committee got together in 1984 to standardize SQL, the database \r
-  vendors had such different implementations that they could only agree on the \r
-  core functionality of SQL. Many important application specific requirements \r
-  were not standardized, and after so many years since the ANSI effort began, \r
-  it looks as if much useful database functionality will never be standardized. \r
-  Even though ANSI-92 SQL has codified much more, we still have to implement portability \r
-  at the application level.</p>\r
-<h3><b>Selects</b></h3>\r
-<p>The SELECT statement has been standardized to a great degree. Nearly every \r
-  database supports the following:</p>\r
-<p>SELECT [cols] FROM [tables]<br>\r
-  &nbsp;&nbsp;[WHERE conditions]<br>\r
-  &nbsp; [GROUP BY cols]<br>\r
-  &nbsp; [HAVING conditions] <br>\r
-  &nbsp; [ORDER BY cols]</p>\r
-<p>But so many useful techniques can only be implemented by using proprietary \r
-  extensions. For example, when writing SQL to retrieve the first 10 rows for \r
-  paging, you could write...</p>\r
-<table width="80%" border="1" cellspacing="0" cellpadding="0" align="center">\r
-  <tr> \r
-       <td><b>Database</b></td>\r
-       <td><b>SQL Syntax</b></td>\r
-  </tr>\r
-  <tr> \r
-       <td>DB2</td>\r
-       <td>select * from table fetch first 10 rows only</td>\r
-  </tr>\r
-  <tr> \r
-       <td>Informix</td>\r
-       <td>select first 10 * from table</td>\r
-  </tr>\r
-  <tr> \r
-       <td>Microsoft SQL Server and Access</td>\r
-       <td>select top 10 * from table</td>\r
-  </tr>\r
-  <tr> \r
-       <td>MySQL and PostgreSQL</td>\r
-       <td>select * from table limit 10</td>\r
-  </tr>\r
-  <tr> \r
-       <td>Oracle 8i</td>\r
-       <td>select * from (select * from table) where rownum &lt;= 10</td>\r
-  </tr>\r
-</table>\r
-<p>This feature of getting a subset of data is so useful that in the PHP class \r
-  library ADOdb, we have a SelectLimit( ) function that allows you to hide the \r
-  implementation details within a function that will rewrite your SQL for you:</p>\r
-<pre>$connection-&gt;SelectLimit('select * from table', 10);\r
-</pre>\r
-<p><b>Selects: Fetch Modes</b></p>\r
-<p>PHP allows you to retrieve database records as arrays. You can choose to have \r
-  the arrays indexed by field name or number. However different low-level PHP \r
-  database drivers are inconsistent in their indexing efforts. ADOdb allows you \r
-  to determine your prefered mode. You set this by setting the variable $ADODB_FETCH_MODE \r
-  to either of the constants ADODB_FETCH_NUM (for numeric indexes) or ADODB_FETCH_ASSOC \r
-  (using field names as an associative index).</p>\r
-<p>The default behaviour of ADOdb varies depending on the database you are using. \r
-  For consistency, set the fetch mode to either ADODB_FETCH_NUM (for speed) or \r
-  ADODB_FETCH_ASSOC (for convenience) at the beginning of your code. </p>\r
-<p><b>Selects: Counting Records</b></p>\r
-<p>Another problem with SELECTs is that some databases do not return the number \r
-  of rows retrieved from a select statement. This is because the highest performance \r
-  databases will return records to you even before the last record has been found. \r
-</p>\r
-<p>In ADOdb, RecordCount( ) returns the number of rows returned, or will emulate \r
-  it by buffering the rows and returning the count after all rows have been returned. \r
-  This can be disabled for performance reasons when retrieving large recordsets \r
-  by setting the global variable $ADODB_COUNTRECS = false. This variable is checked \r
-  every time a query is executed, so you can selectively choose which recordsets \r
-  to count.</p>\r
-<p>If you prefer to set $ADODB_COUNTRECS = false, ADOdb still has the PO_RecordCount( \r
-  ) function. This will return the number of rows, or if it is not found, it will \r
-  return an estimate using SELECT COUNT(*):</p>\r
-<pre>$rs = $db-&gt;Execute(&quot;select * from table where state=$state&quot;);\r
-$numrows = $rs-&gt;PO_RecordCount('table', &quot;state=$state&quot;);</pre>\r
-<p><b>Selects: Locking</b> </p>\r
-<p>SELECT statements are commonly used to implement row-level locking of tables. \r
-  Other databases such as Oracle, Interbase, PostgreSQL and MySQL with InnoDB \r
-  do not require row-level locking because they use versioning to display data \r
-  consistent with a specific point in time.</p>\r
-<p>Currently, I recommend encapsulating the row-level locking in a separate function, \r
-  such as RowLock($table, $where):</p>\r
-<pre>$connection-&gt;BeginTrans( );\r
-$connection-&gt;RowLock($table, $where); </pre>\r
-<pre><font color=green># some operation</font></pre>\r
-<pre>if ($ok) $connection-&gt;CommitTrans( );\r
-else $connection-&gt;RollbackTrans( );\r
-</pre>\r
-<p><b>Selects: Outer Joins</b></p>\r
-<p>Not all databases support outer joins. Furthermore the syntax for outer joins \r
-  differs dramatically between database vendors. One portable (and possibly slower) \r
-  method of implementing outer joins is using UNION.</p>\r
-<p>For example, an ANSI-92 left outer join between two tables t1 and t2 could \r
-  look like:</p>\r
-<pre>SELECT t1.col1, t1.col2, t2.cola <br>  FROM t1 <i>LEFT JOIN</i> t2 ON t1.col = t2.col</pre>\r
-<p>This can be emulated using:</p>\r
-<pre>SELECT t1.col1, t1.col2, t2.cola FROM t1, t2 <br>    WHERE t1.col = t2.col \r
-   UNION ALL\r
-SELECT col1, col2, null FROM t1 <br>      WHERE t1.col not in (select distinct col from t2)\r
-</pre>\r
-<p>Since ADOdb 2.13, we provide some hints in the connection object as to legal \r
-  join variations. This is still incomplete and sometimes depends on the database \r
-  version you are using, but is useful as a general guideline:</p>\r
-<p><font face="Courier New, Courier, mono">$conn-&gt;leftOuter</font>: holds the \r
-  operator used for left outer joins (eg. '*='), or false if not known or not \r
-  available.<br>\r
-  <font face="Courier New, Courier, mono">$conn-&gt;rightOuter</font>: holds the \r
-  operator used for right outer joins (eg '=*'), or false if not known or not \r
-  available.<br>\r
-  <font face="Courier New, Courier, mono">$conn-&gt;ansiOuter</font>: boolean \r
-  that if true means that ANSI-92 style outer joins are supported, or false if \r
-  not known.</p>\r
-<h3><b>Inserts</b> </h3>\r
-<p>When you create records, you need to generate unique id's for each record. \r
-  There are two common techniques: (1) auto-incrementing columns and (2) sequences. \r
-</p>\r
-<p>Auto-incrementing columns are supported by MySQL, Sybase and Microsoft Access \r
-  and SQL Server. However most other databases do not support this feature. So \r
-  for portability, you have little choice but to use sequences. Sequences are \r
-  special functions that return a unique incrementing number every time you call \r
-  it, suitable to be used as database keys. In ADOdb, we use the GenID( ) function. \r
-  It has takes a parameter, the sequence name. Different tables can have different \r
-  sequences. </p>\r
-<pre>$id = $connection-&gt;GenID('sequence_name');<br>$connection-&gt;Execute(&quot;insert into table (id, firstname, lastname) <br>                      values ($id, $firstname, $lastname)&quot;);</pre>\r
-<p>For databases that do not support sequences natively, ADOdb emulates sequences \r
-  by creating a table for every sequence.</p>\r
-<h3><b>Binding</b></h3>\r
-<p>Binding variables in an SQL statement is another tricky feature. Binding is \r
-  useful because it allows pre-compilation of SQL. When inserting multiple records \r
-  into a database in a loop, binding can offer a 50% (or greater) speedup. However \r
-  many databases such as Access and MySQL do not support binding natively and \r
-  there is some overhead in emulating binding. Furthermore, different databases \r
-  (specificly Oracle!) implement binding differently. My recommendation is to \r
-  use binding if your database queries are too slow, but make sure you are using \r
-  a database that supports it like Oracle. </p>\r
-<p>ADOdb supports portable Prepare/Execute with:</p>\r
-<pre>$stmt = $db-&gt;Prepare('select * from customers where custid=? and state=?');\r
-$rs = $db-&gt;Execute($stmt, array($id,'New York'));</pre>\r
-<p>Oracle uses named bind placeholders, not "?", so to support portable binding, we have Param() that generates \r
-the correct placeholder (available since ADOdb 3.92):\r
-<pre><font color="#000000">$sql = <font color="#993300">'insert into table (col1,col2) values ('</font>.$DB-&gt;Param('a').<font color="#993300">','</font>.$DB-&gt;Param('b').<font color="#993300">')'</font>;\r
-<font color="#006600"># generates 'insert into table (col1,col2) values (?,?)'\r
-# or        'insert into table (col1,col2) values (:a,:b)</font>'\r
-$stmt = $DB-&gt;Prepare($sql);\r
-$stmt = $DB-&gt;Execute($stmt,array('one','two'));\r
-</font></pre>\r
-<a name="native"></a>\r
-<h2>Portable Native SQL</h2>\r
-<p>ADOdb provides the following functions for portably generating SQL functions \r
-  as strings to be merged into your SQL statements (some are only available since \r
-  ADOdb 3.92): </p>\r
-<table width="75%" border="1" align=center>\r
-  <tr> \r
-    <td width=30%><b>Function</b></td>\r
-    <td><b>Description</b></td>\r
-  </tr>\r
-  <tr> \r
-    <td>DBDate($date)</td>\r
-    <td>Pass in a UNIX timestamp or ISO date and it will convert it to a date \r
-      string formatted for INSERT/UPDATE</td>\r
-  </tr>\r
-  <tr> \r
-    <td>DBTimeStamp($date)</td>\r
-    <td>Pass in a UNIX timestamp or ISO date and it will convert it to a timestamp \r
-      string formatted for INSERT/UPDATE</td>\r
-  </tr>\r
-  <tr> \r
-    <td>SQLDate($date, $fmt)</td>\r
-    <td>Portably generate a date formatted using $fmt mask, for use in SELECT \r
-      statements.</td>\r
-  </tr>\r
-  <tr> \r
-    <td>OffsetDate($date, $ndays)</td>\r
-    <td>Portably generate a $date offset by $ndays.</td>\r
-  </tr>\r
-  <tr> \r
-    <td>Concat($s1, $s2, ...)</td>\r
-    <td>Portably concatenate strings. Alternatively, for mssql use mssqlpo driver, \r
-      which allows || operator.</td>\r
-  </tr>\r
-  <tr> \r
-    <td>IfNull($fld, $replaceNull)</td>\r
-    <td>Returns a string that is the equivalent of MySQL IFNULL or Oracle NVL.</td>\r
-  </tr>\r
-  <tr>\r
-    <td>Param($name)</td>\r
-    <td>Generates bind placeholders, using ? or named conventions as appropriate.</td>\r
-  </tr>\r
-  <tr><td>$db->sysDate</td><td>Property that holds the SQL function that returns today's date</td>\r
-</tr>\r
-<tr><td>$db->sysTimeStamp</td><td>Property that holds the SQL function that returns the current\r
-timestamp (date+time).\r
-</td>\r
-</tr>\r
-<tr>\r
-<td>$db->concat_operator</td><td>Property that holds the concatenation operator\r
-</td>\r
-</tr>\r
-<tr><td>$db->length</td><td>Property that holds the name of the SQL strlen function.\r
-</td></tr>\r
-\r
-<tr><td>$db->upperCase</td><td>Property that holds the name of the SQL strtoupper function.\r
-</td></tr>\r
-<tr><td>$db->random</td><td>Property that holds the SQL to generate a random number between 0.00 and 1.00.\r
-</td>\r
-</tr>\r
-<tr><td>$db->substr</td><td>Property that holds the name of the SQL substring function.\r
-</td></tr>\r
-</table>\r
-<p>&nbsp; </p>\r
-<h2>DDL and Tuning</h2>\r
-There are database design tools such as ERWin or Dezign that allow you to generate data definition language commands such as ALTER TABLE or CREATE INDEX from Entity-Relationship diagrams. \r
-<p>\r
-However if you prefer to use a PHP-based table creation scheme, adodb provides you with this feature. Here is the code to generate the SQL to create a table with: \r
-<ol>\r
-       <li> Auto-increment primary key 'ID', </li>\r
-       <li>The person's 'NAME' VARCHAR(32) NOT NULL and defaults to '', </li>\r
-       <li>The date and time of record creation 'CREATED', </li>\r
-       <li> The person's 'AGE', defaulting to 0, type NUMERIC(16). </li>\r
-</ol>\r
-<p>\r
-Also create a compound index consisting of 'NAME' and 'AGE': \r
-<pre>\r
-$datadict = <strong>NewDataDictionary</strong>($connection);\r
-$flds = " \r
-<font color="#660000">  ID I AUTOINCREMENT PRIMARY,\r
-  NAME C(32) DEFAULT '' NOTNULL,\r
-  CREATED T DEFTIMESTAMP,\r
-  AGE N(16) DEFAULT 0</font>\r
-";\r
-$sql1 = $datadict-><strong>CreateTableSQL</strong>('tabname', $flds);\r
-$sql2 = $datadict-><strong>CreateIndexSQL</strong>('idx_name_age', 'tabname', 'NAME,AGE');\r
-</pre>\r
-\r
-<h3>Data Types</h3>\r
-<p>Stick to a few data types that are available in most databases. Char, varchar \r
-  and numeric/number are supported by most databases. Most other data types (including \r
-  integer, boolean and float) cannot be relied on being available. I recommend \r
-  using char(1) or number(1) to hold booleans. </p>\r
-<p>Different databases have different ways of representing dates and timestamps/datetime. \r
-  ADOdb attempts to display all dates in ISO (YYYY-MM-DD) format. ADOdb also provides \r
-  DBDate( ) and DBTimeStamp( ) to convert dates to formats that are acceptable \r
-  to that database. Both functions accept Unix integer timestamps and date strings \r
-  in ISO format.</p>\r
-<pre>$date1 = $connection-&gt;DBDate(time( ));<br>$date2 = $connection-&gt;DBTimeStamp('2002-02-23 13:03:33');</pre>\r
-<p>We also provide functions to convert database dates to Unix timestamps:</p>\r
-<pre>$unixts = $recordset-&gt;UnixDate('#2002-02-30#'); <font color="green"># MS Access date =gt; unix timestamp</font></pre>\r
-<p>For date calculations, we have OffsetDate which allows you to calculate dates such as <i>yesterday</i> and <i>next week</i> in a RDBMS independant fashion. For example, if we want to set a field to 6 hour from now, use:\r
-<pre>\r
-$sql = 'update table set dtimefld='.$db-&gt;OffsetDate($db-&gtsysTimeStamp, 6/24).' where ...';\r
-</pre>\r
-<p>The maximum length of a char/varchar field is also database specific. You can \r
-  only assume that field lengths of up to 250 characters are supported. This is \r
-  normally impractical for web based forum or content management systems. You \r
-  will need to be familiar with how databases handle large objects (LOBs). ADOdb \r
-  implements two functions, UpdateBlob( ) and UpdateClob( ) that allow you to \r
-  update fields holding Binary Large Objects (eg. pictures) and Character Large \r
-  Objects (eg. HTML articles):</p>\r
-<pre><font color=green># for oracle </font>\r
-$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1,empty_blob())'); \r
-$conn->UpdateBlob('blobtable','blobcol',$blobvalue,'id=1'); \r
-   \r
-<font color=green># non-oracle databases</font>\r
-$conn->Execute('INSERT INTO blobtable (id, blobcol) VALUES (1, null)'); \r
-$conn->UpdateBlob('blobtable','blobcol',$blobvalue,'id=1');\r
-</pre>\r
-<p>Null handling is another area where differences can occur. This is a mine-field, \r
-  because 3-value logic is tricky.\r
-<p>In general, I avoid using nulls except for dates and default all my numeric \r
-  and character fields to 0 or the empty string. This maintains consistency with \r
-  PHP, where empty strings and zero are treated as equivalent, and avoids SQL \r
-  ambiguities when you use the ANY and EXISTS operators. However if your database \r
-  has significant amounts of missing or unknown data, using nulls might be a good \r
-  idea. \r
-  <p>\r
-  ADOdb also supports a portable <a href=http://phplens.com/adodb/reference.functions.concat.html#ifnull>IfNull</a> function, so you can define what to display\r
-  if the field contains a null.\r
-<h3><b>Stored Procedures</b></h3>\r
-<p>Stored procedures are another problem area. Some databases allow recordsets \r
-  to be returned in a stored procedure (Microsoft SQL Server and Sybase), and \r
-  others only allow output parameters to be returned. Stored procedures sometimes \r
-  need to be wrapped in special syntax. For example, Oracle requires such code \r
-  to be wrapped in an anonymous block with BEGIN and END. Also internal sql operators \r
-  and functions such as +, ||, TRIM( ), SUBSTR( ) or INSTR( ) vary between vendors. \r
-</p>\r
-<p>An example of how to call a stored procedure with 2 parameters and 1 return \r
-  value follows:</p>\r
-<pre>  switch ($db->databaseType) {\r
-       case '<font color="#993300">mssql</font>':\r
-         $sql = <font color="#000000"><font color="#993333">'<font color="#993300">SP_RUNSOMETHING</font>'</font></font>; break;\r
-       case '<font color="#993300">oci8</font>':\r
-         $sql = \r
-<font color="#993300">   </font><font color="#000000"><font color="#993300">&quot;declare RETVAL integer;begin :RETVAL := </font><font color="#000000"><font color="#993333"><font color="#993300">SP_RUNSOMETHING</font></font></font><font color="#993300">(:myid,:group);end;&quot;;\r
-</font>          break;</font>\r
-       default:\r
-         die('<font color="#993300">Unsupported feature</font>');\r
-       }\r
-<font color="#000000"><font color="green">     # @RETVAL = SP_RUNSOMETHING @myid,@group</font>\r
-       $stmt = $db-&gt;PrepareSP($sql);        <br>    $db-&gt;Parameter($stmt,$id,'<font color="#993300">myid</font>'); \r
-       $db-&gt;Parameter($stmt,$group,'<font color="#993300">group</font>');\r
-       <font color="green"># true indicates output parameter<br>       </font>$db-&gt;Parameter($stmt,$ret,'<font color="#993300">RETVAL</font>',true); \r
-       $db-&gt;Execute($stmt); </font></pre>\r
-<p>As you can see, the ADOdb API is the same for both databases. But the stored \r
-  procedure SQL syntax is quite different between databases and is not portable, \r
-  so be forewarned! However sometimes you have little choice as some systems only \r
-  allow data to be accessed via stored procedures. This is when the ultimate portability \r
-  solution might be the only solution: <i>treating portable SQL as a localization \r
-  exercise...</i></p>\r
-<h3><b>SQL as a Localization Exercise</b></h3>\r
-<p> In general to provide real portability, you will have to treat SQL coding \r
-  as a localization exercise. In PHP, it has become common to define separate \r
-  language files for English, Russian, Korean, etc. Similarly, I would suggest \r
-  you have separate Sybase, Intebase, MySQL, etc files, and conditionally include \r
-  the SQL based on the database. For example, each MySQL SQL statement would be \r
-  stored in a separate variable, in a file called 'mysql-lang.inc.php'.</p>\r
-<pre>$sqlGetPassword = '<font color="#993300">select password from users where userid=%s</font>';\r
-$sqlSearchKeyword = quot;<font color="#993300">SELECT * FROM articles WHERE match (title,body) against (%s</font>)&quot;;</pre>\r
-<p>In our main PHP file:</p>\r
-<pre><font color=green># define which database to load...</font>\r
-<b>$database = '<font color="#993300">mysql</font>';\r
-include_once(&quot;<font color="#993300">$database-lang.inc.php</font>&quot;);</b>\r
-\r
-$db = NewADOConnection($database);\r
-$db->PConnect(...) or die('<font color="#993300">Failed to connect to database</font>');\r
-\r
-<font color=green># search for a keyword $word</font>\r
-$rs = $db-&gt;Execute(sprintf($sqlSearchKeyWord,$db-&gt;qstr($word)));</pre>\r
-<p>Note that we quote the $word variable using the qstr( ) function. This is because \r
-  each database quotes strings using different conventions.</p>\r
-<p>\r
-<h3>Final Thoughts</h3>\r
-<p>The best way to ensure that you have portable SQL is to have your data tables designed using \r
-sound principles. Learn the theory of normalization and entity-relationship diagrams and model \r
-your data carefully. Understand how joins and indexes work and how they are used to tune performance.\r
-<p> Visit the following page for more references on database theory and vendors: \r
-  <a href="http://php.weblogs.com/sql_tutorial">http://php.weblogs.com/sql_tutorial</a>. \r
-  Also read this article on <a href=http://phplens.com/lens/php-book/optimizing-debugging-php.php>Optimizing PHP</a>.\r
-<p>\r
-<font size=1>(c) 2002-2003 John Lim.</font>\r
-\r
-</body>\r
-</html>\r