mirror of
https://github.com/php/presentations.git
synced 2026-03-23 23:22:22 +01:00
Add "Limit Yourself to MySQL No More" presentation from php|works 2005.
This commit is contained in:
41
mysql-limits.xml
Normal file
41
mysql-limits.xml
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<presentation
|
||||
template="css"
|
||||
navsize="1.5em"
|
||||
navmode="html"
|
||||
navbarbackground="#4373b4"
|
||||
navbartopiclinks="1"
|
||||
navColor="#f1fbff"
|
||||
logo1=""
|
||||
stylesheet="presentations/slides/mysql-limits/yuck.css"
|
||||
backgroundfixed="1" >
|
||||
<topic>Databases</topic>
|
||||
<title>LIMIT Yourself to MySQL No More</title>
|
||||
<event>php|works 2005</event>
|
||||
<location>Toronto, Ontario</location>
|
||||
<date>September 17th, 2005</date>
|
||||
<speaker>Dan Scott</speaker>
|
||||
<email>dan@coffeecode.net</email>
|
||||
<url>http://coffeecode.net/</url>
|
||||
|
||||
<slide>slides/mysql-limits/title.xml</slide>
|
||||
<slide>slides/mysql-limits/intro.xml</slide>
|
||||
<slide>slides/mysql-limits/why-not-mysql.xml</slide>
|
||||
<slide>slides/mysql-limits/subject.xml</slide>
|
||||
<slide>slides/mysql-limits/target.xml</slide>
|
||||
<slide>slides/mysql-limits/opening.xml</slide>
|
||||
<slide>slides/mysql-limits/intertwining.xml</slide>
|
||||
<slide>slides/mysql-limits/limit.xml</slide>
|
||||
<slide>slides/mysql-limits/counting.xml</slide>
|
||||
<slide>slides/mysql-limits/reinventing.xml</slide>
|
||||
<slide>slides/mysql-limits/abstraction.xml</slide>
|
||||
<slide>slides/mysql-limits/abstraction-2.xml</slide>
|
||||
<slide>slides/mysql-limits/schemas.xml</slide>
|
||||
<slide>slides/mysql-limits/schemas-time.xml</slide>
|
||||
<slide>slides/mysql-limits/schemas-indexes.xml</slide>
|
||||
<slide>slides/mysql-limits/schemas-schemas.xml</slide>
|
||||
<slide>slides/mysql-limits/wrapping-up.xml</slide>
|
||||
<slide>slides/mysql-limits/resources.xml</slide>
|
||||
<slide>slides/mysql-limits/thank-you.xml</slide>
|
||||
|
||||
</presentation>
|
||||
39
slides/mysql-limits/abstraction-2.xml
Normal file
39
slides/mysql-limits/abstraction-2.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Database abstractions (continued)</title>
|
||||
<blurb>
|
||||
Sure enough, taking a page at random, we can quickly find MySQL-specific SQL
|
||||
being issued through the "abstraction" layer:
|
||||
</blurb>
|
||||
|
||||
<example type="php"><![CDATA[// get the subset (based on limits) of required records
|
||||
$query = "SELECT cd.*, cc.title AS category, u.name AS user, v.name as editor"
|
||||
. "\n FROM #__contact_details AS cd"
|
||||
. "\n LEFT JOIN #__categories AS cc ON cc.id = cd.catid"
|
||||
. "\n LEFT JOIN #__users AS u ON u.id = cd.user_id"
|
||||
. "\n LEFT JOIN #__users AS v ON v.id = cd.checked_out"
|
||||
. $where
|
||||
. "\n ORDER BY cd.catid, cd.ordering, cd.name ASC"
|
||||
. "\n LIMIT $pageNav->limitstart, $pageNav->limit"
|
||||
;
|
||||
$database->setQuery( $query );
|
||||
$rows = $database->loadObjectList();]]></example>
|
||||
|
||||
<blurb>
|
||||
So there are some problems here from a portability perspective,
|
||||
primarily in the hard-coded non-standard LIMIT clause.
|
||||
It is not worth defining a database abstraction layer
|
||||
for your application if you are going to issue SQL directly from within every
|
||||
other file in your application.
|
||||
</blurb>
|
||||
<blurb>
|
||||
Instead, consider defining a database abstraction with functions,
|
||||
or classes and methods, for the functionality required by your application,
|
||||
so that other databases can simply implement that interface.
|
||||
Then a simple setting in the user's configuration
|
||||
file will determine which database they want to use, and which implementation of the
|
||||
application logic to invoke.
|
||||
</blurb>
|
||||
<blurb>For example, instead of defining %Myappdb::issueQuery()%, define %Myapp::getActiveEditors()% with the expected input parameters and expected return values, and let
|
||||
the actual database-specific code implement that in the most efficient way.</blurb>
|
||||
</slide>
|
||||
86
slides/mysql-limits/abstraction.xml
Normal file
86
slides/mysql-limits/abstraction.xml
Normal file
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Database abstractions</title>
|
||||
<blurb>
|
||||
Well, so far so good. But while FRED appears to be defining a database access
|
||||
abstraction in %includes/database.php%, the implementation is hardcoded for
|
||||
MySQL using the %mysql% extension, which somewhat defeats the purpose of
|
||||
abstracting an interface. Let's take a quick look for our favourite MySQL keyword
|
||||
to see where the real work is being done:
|
||||
</blurb>
|
||||
|
||||
<example><![CDATA[daniels@dullard:~/phpworks> find . -name '*.php' -exec grep -Hil 'LIMIT' {} \;
|
||||
./FREDts/content/geshi/geshi/xml.php
|
||||
./FREDts/content/geshi/geshi/css.php
|
||||
./FREDts/content/geshi/geshi/sql.php
|
||||
./FREDts/content/geshi/geshi/php-brief.php
|
||||
./FREDts/content/geshi/geshi/html4strict.php
|
||||
./FREDts/content/geshi/geshi/javascript.php
|
||||
./FREDts/content/geshi/geshi/php.php
|
||||
./FREDts/content/geshi/geshi.php
|
||||
./FREDts/content/mospaging.php
|
||||
./components/com_rss/rss.php
|
||||
./components/com_content/content.html.php
|
||||
./components/com_content/content.php
|
||||
./components/com_banners/banners.php
|
||||
./components/com_newsfeeds/newsfeeds.html.php
|
||||
./installation/install4.php
|
||||
./modules/mod_latestnews.php
|
||||
./modules/mod_banners.php
|
||||
./modules/mod_sections.php
|
||||
./modules/mod_archive.php
|
||||
./modules/mod_mostread.php
|
||||
./modules/mod_newsflash.php
|
||||
./administrator/components/com_contact/admin.contact.php
|
||||
./administrator/components/com_content/admin.content.php
|
||||
./administrator/components/com_messages/admin.messages.html.php
|
||||
./administrator/components/com_messages/admin.messages.php
|
||||
./administrator/components/com_menumanager/admin.menumanager.html.php
|
||||
./administrator/components/com_menumanager/admin.menumanager.php
|
||||
./administrator/components/com_typedcontent/admin.typedcontent.php
|
||||
./administrator/components/com_FREDts/admin.FREDts.php
|
||||
./administrator/components/com_syndicate/admin.syndicate.php
|
||||
./administrator/components/com_statistics/admin.statistics.html.php
|
||||
./administrator/components/com_statistics/admin.statistics.php
|
||||
./administrator/components/com_poll/admin.poll.php
|
||||
./administrator/components/com_modules/admin.modules.php
|
||||
./administrator/components/com_sections/admin.sections.php
|
||||
./administrator/components/com_categories/admin.categories.php
|
||||
./administrator/components/com_banners/admin.banners.html.php
|
||||
./administrator/components/com_banners/admin.banners.php
|
||||
./administrator/components/com_config/admin.config.php
|
||||
./administrator/components/com_newsfeeds/admin.newsfeeds.php
|
||||
./administrator/components/com_templates/admin.templates.php
|
||||
./administrator/components/com_languages/admin.languages.php
|
||||
./administrator/components/com_weblinks/admin.weblinks.php
|
||||
./administrator/components/com_frontpage/admin.frontpage.php
|
||||
./administrator/components/com_admin/admin.admin.html.php
|
||||
./administrator/components/com_menus/admin.menus.php
|
||||
./administrator/components/com_menus/admin.menus.html.php
|
||||
./administrator/components/com_trash/admin.trash.php
|
||||
./administrator/components/com_trash/admin.trash.html.php
|
||||
./administrator/components/com_users/admin.users.html.php
|
||||
./administrator/components/com_users/admin.users.php
|
||||
./administrator/modules/mod_popular.php
|
||||
./administrator/modules/mod_latest.php
|
||||
./administrator/modules/mod_logged.php
|
||||
./administrator/modules/mod_fullmenu.php
|
||||
./administrator/modules/mod_components.php
|
||||
./administrator/includes/pcl/pcltar.lib.php
|
||||
./administrator/includes/pageNavigation.php
|
||||
./index.php
|
||||
./includes/frontend.html.php
|
||||
./includes/Cache/Lite.php
|
||||
./includes/domit/php_text_cache.php
|
||||
./includes/gacl_api.class.php
|
||||
./includes/sef.php
|
||||
./includes/pathway.php
|
||||
./includes/FRED.php
|
||||
./includes/database.php
|
||||
./includes/patTemplate/patTemplate.php
|
||||
./includes/patTemplate/patTemplate/Modifier/Surround.php
|
||||
./includes/phpmailer/class.smtp.php
|
||||
./includes/pageNavigation.php
|
||||
]]>
|
||||
</example>
|
||||
</slide>
|
||||
15
slides/mysql-limits/counting.xml
Normal file
15
slides/mysql-limits/counting.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Counting results of SELECT statement</title>
|
||||
<blurb>A very popular MySQL idiom is to use the return value of %mysql_query()% to
|
||||
reflect the number of rows that will be returned by the SELECT statement that was
|
||||
issued.</blurb>
|
||||
<list>
|
||||
<bullet>But in most database APIs, the return value from executing a SELECT statement is simply true or false reflecting success or failure.</bullet>
|
||||
<bullet>If your database supports scrollable cursors, and you request a scrollable cursor when you issue your SELECT statement, you *will* get an accurate count of the number of rows in the result set.</bullet>
|
||||
<bullet>Most times, however, you can either issue a separate SELECT COUNT(*)
|
||||
if you actually want the number of rows, or just try fetching the first row from the result set.</bullet>
|
||||
<bullet>For small result sets, you can use %PDO::fetchAll()% to return all of the rows in the result set. The %ibm_db2% extension that Apache Derby requires will probably implement a %db2_fetch_all()% function in the near future to provide the equivalent functionality.</bullet>
|
||||
<bullet>You can also step through the results of a forward-only cursor until the fetch call fails and increment a counter to find out how many rows are in the result set.</bullet>
|
||||
</list>
|
||||
</slide>
|
||||
44
slides/mysql-limits/intertwining.xml
Normal file
44
slides/mysql-limits/intertwining.xml
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Intertwining</title>
|
||||
<blurb>
|
||||
So let's look at the most obvious file in the list: %includes/database.php%:
|
||||
</blurb>
|
||||
<example><![CDATA[class database {
|
||||
/* ... */
|
||||
/**
|
||||
* Database object constructor
|
||||
* @param string Database host
|
||||
* @param string Database user name
|
||||
* @param string Database user password
|
||||
* @param string Database name
|
||||
* @param string Common prefix for all tables
|
||||
*/
|
||||
function database( $host='localhost', $user, $pass, $db, $table_prefix ) {
|
||||
// perform a number of fatality checks, then die gracefully
|
||||
if (!function_exists( 'mysql_connect' )) {
|
||||
//or die( 'FATAL ERROR: MySQL support not available. Please check your configuration.' );
|
||||
$mosSystemError = 1;
|
||||
$basePath = dirname( __FILE__ );
|
||||
include $basePath . '/../configuration.php';
|
||||
include $basePath . '/../offline.php';
|
||||
exit();
|
||||
}]]></example>
|
||||
<blurb>
|
||||
Well, we immediately see the assumptions about MySQL being the one and only
|
||||
database that FRED will ever connect to. One approach to opening up this file
|
||||
structure to support multiple databases would be:
|
||||
</blurb>
|
||||
<list>
|
||||
<bullet type="number">Turn %database.php% into a directory</bullet>
|
||||
<bullet type="number">Include database-specific implementations inside that directory</bullet>
|
||||
<bullet type="number">Load the right implementation based on a configuration directive</bullet>
|
||||
</list>
|
||||
<example><![CDATA[includes/database/adodb.php
|
||||
includes/database/ibm_db2.php
|
||||
includes/database/ibm_db2_derby.php
|
||||
includes/database/mdb2.php
|
||||
includes/database/mysql.php
|
||||
includes/database/oci8.php
|
||||
includes/database/pgsql.php]]></example>
|
||||
</slide>
|
||||
20
slides/mysql-limits/intro.xml
Normal file
20
slides/mysql-limits/intro.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>What I'm here to talk about</title>
|
||||
<list>
|
||||
<bullet>I'm sick...</bullet>
|
||||
<bullet>I really enjoy working with databases</bullet>
|
||||
<bullet class="indent">and PHP</bullet>
|
||||
<bullet>but many PHP applications are written for MySQL only</bullet>
|
||||
<bullet class="indent">and I want to run them on Apache Derby and DB2</bullet>
|
||||
</list>
|
||||
<blurb>So in this presentation I'm going to step through the process of
|
||||
what would be required to convert a large PHP application from being
|
||||
limited to running on MySQL to supporting any one of a number of databases,
|
||||
without sacrificing performance, and without complicating the design^1^.
|
||||
</blurb>
|
||||
<blurb>
|
||||
^1^ - ~too much~
|
||||
</blurb>
|
||||
</slide>
|
||||
|
||||
48
slides/mysql-limits/limit.xml
Normal file
48
slides/mysql-limits/limit.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>LIMITing success</title>
|
||||
<blurb>
|
||||
%includes/database.php% also gives us our first encounter with the LIMIT clause:
|
||||
</blurb>
|
||||
<example><![CDATA[function move( $dirn, $where='' ) {
|
||||
$k = $this->_tbl_key;
|
||||
|
||||
$sql = "SELECT $this->_tbl_key, ordering FROM $this->_tbl";
|
||||
|
||||
if ($dirn < 0) {
|
||||
$sql .= "\nWHERE ordering < $this->ordering";
|
||||
$sql .= ($where ? "\n AND $where" : '');
|
||||
$sql .= "\nORDER BY ordering DESC\nLIMIT 1";
|
||||
} else if ($dirn > 0) {
|
||||
$sql .= "\nWHERE ordering > $this->ordering";
|
||||
$sql .= ($where ? "\n AND $where" : '');
|
||||
$sql .= "\nORDER BY ordering\nLIMIT 1";
|
||||
}]]>
|
||||
</example>
|
||||
<blurb>
|
||||
In this case, it appears that the LIMIT clause is being used because the
|
||||
application wants to prevent MySQL from returning every row in the result
|
||||
set. Most other databases will simply return the first row when requested,
|
||||
and nothing more, avoiding the whole problem of having to artificially
|
||||
limit your result set.
|
||||
</blurb>
|
||||
<blurb>
|
||||
If you do have a legitimate requirement for limiting the rows returned by
|
||||
a query in this fashion, and your database does not support the LIMIT clause
|
||||
(MySQL, PostgreSQL, SQLite do) there are a number of quasi-standard approaches
|
||||
you can take depending on the database you are running against:
|
||||
</blurb>
|
||||
<list>
|
||||
<bullet>Forward-only cursors: calling %db2_fetch_row()% or %PDO->fetch()% steps through the rows in the result set, without retrieving them,
|
||||
until you reach the row(s) that you actually want to return. Fetching until the call fails will also let you count the approximate number of rows in the result set.</bullet>
|
||||
<bullet>Scrollable cursors: calling %db2_fetch_row()% or %PDO->fetch()% with a specified row number enables you to skip to the right section and retrieve only the rows you want. Not supported by Apache Derby.</bullet>
|
||||
<bullet>Standard SQL2003 approach: Subselect %ROW_NUMBER OVER(ORDER BY ~column~) AS number, ~column~% -- but this is currently only supported by DB2 and Oracle, so we cannot rely on that for Apache Derby.</bullet>
|
||||
</list>
|
||||
<example><![CDATA[SELECT * FROM (
|
||||
SELECT ROW_NUMBER OVER (ORDER BY column) AS rownum, id, cost
|
||||
FROM items
|
||||
WHERE cost < 595
|
||||
)
|
||||
WHERE rownum >= 10 AND rownum <= 20
|
||||
]]></example>
|
||||
</slide>
|
||||
43
slides/mysql-limits/opening.xml
Normal file
43
slides/mysql-limits/opening.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Assessing the depth of penetration</title>
|
||||
<blurb>
|
||||
First, let's find every PHP file where the application calls a mysql function:
|
||||
</blurb>
|
||||
<example>find . -name '*.php' -exec grep -Hil 'mysql_' {} \;
|
||||
|
||||
-H with filename
|
||||
-i ignore case (because PHP is case-insensitive, MYSQL_ is possible but quite rare)
|
||||
-l list only the filename, with no context for the match
|
||||
</example>
|
||||
<blurb>
|
||||
Results in:
|
||||
</blurb>
|
||||
<example>./FREDts/content/geshi/geshi/php-brief.php
|
||||
./FREDts/content/geshi/geshi/php.php
|
||||
./components/com_user/user.php
|
||||
./installation/install2.php
|
||||
./installation/install4.php
|
||||
./installation/index.php
|
||||
./modules/mod_stats.php
|
||||
./administrator/components/com_checkin/admin.checkin.php
|
||||
./administrator/components/com_admin/admin.admin.html.php
|
||||
./includes/feedcreator.class.php
|
||||
./includes/phpInputFilter/class.inputfilter.php
|
||||
./includes/database.php
|
||||
./includes/getids.php
|
||||
</example>
|
||||
<blurb>
|
||||
Hmm. FRED doesn't offer a clean separation of database functionality,
|
||||
either by database (everything is calling the %mysql_% functions directly inline)
|
||||
or by type of functionality (for example, there isn't a %*_db.php% file corresponding
|
||||
to each subdirectory).
|
||||
</blurb>
|
||||
<blurb>
|
||||
One way of proceeding would be to abstract the database functionality into a
|
||||
separate layer so that the correct database implementation can be loaded based
|
||||
on a configuration option.
|
||||
</blurb>
|
||||
</slide>
|
||||
|
||||
|
||||
30
slides/mysql-limits/reinventing.xml
Normal file
30
slides/mysql-limits/reinventing.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Abstracting database access</title>
|
||||
<blurb>
|
||||
The %updateOrder()% function contains examples of the custom class in action like:
|
||||
</blurb>
|
||||
<example type="php"><![CDATA[$this->_db->setQuery( "UPDATE $this->_tbl"
|
||||
. "\nSET ordering='".$orders[$i]->ordering."' WHERE $k='".$orders[$i]->$k."'"
|
||||
);
|
||||
$this->_db->query();]]></example>
|
||||
<blurb>
|
||||
Apart from being rather hard to read, we can see that the %setQuery()% and %query()%
|
||||
methods are nothing more than painfully crafted reimplementations of %prepare()%
|
||||
and %execute()% methods that have existed in most standard database APIs for
|
||||
decades, and which has been introduced to PHP is a standard API as
|
||||
PHP Data Objects (PDO).
|
||||
</blurb>
|
||||
<blurb>Let's rewrite this using PDO:
|
||||
</blurb>
|
||||
<example type="php"><![CDATA[$query = $this->_db->prepare("UPDATE $this->_tbl
|
||||
SET ordering = ?
|
||||
WHERE $this->_tbl_key = ?";
|
||||
$query->execute(array($orders[$i]->ordering, $orders[$i]->$k));]]></example>
|
||||
<blurb>
|
||||
While defining a basic database access abstraction layer for your application
|
||||
seems like reinventing the wheel when things like PDO, MDB2, ADODB, and PEAR::DB
|
||||
already exist, none of these were real options when FRED was being developed. Still,
|
||||
a cautionary tale for you: make sure you abstract the right layer of your application.
|
||||
</blurb>
|
||||
</slide>
|
||||
19
slides/mysql-limits/resources.xml
Normal file
19
slides/mysql-limits/resources.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Resources</title>
|
||||
<blurb>*Apache Derby*</blurb>
|
||||
<link leader="Apache Derby project- " href="http://db.apache.org/derby" />
|
||||
<link leader="Apache Derby: Off to the Races- " href="http://www.amazon.com/exec/obidos/tg/detail/-/0131855255" />
|
||||
<link leader="ibm_db2 extension for PHP- " href="http://php.net/ibm_db2" />
|
||||
<blurb>*SQL Portability*</blurb>
|
||||
<link leader="A Gentle Introduction to SQL (includes SQL92 BNF) - " href="http://sqlzoo.net" />
|
||||
<link leader="Comparison of different SQL implementations - " href="http://troels.arvin.dk/db/rdbms/" />
|
||||
<link leader="SQL in a Nutshell- " href="http://www.oreilly.com/catalog/sqlnut2" />
|
||||
<link leader="SQL API Portability: Mysql-isms To Be Avoided for API Portability- " href="http://home.fnal.gov/~dbox/SQL_API_Portability.html" />
|
||||
<blurb>*Database abstraction layers*</blurb>
|
||||
<link leader="ADOdb- " href="http://adodb.sourceforge.net" />
|
||||
<link leader="MDB2- " href="http://pear.php.net/mdb2" />
|
||||
<link leader="PEAR::DB- " href="http://pear.php.net/db" />
|
||||
<blurb>*Standard PHP API for database access*</blurb>
|
||||
<link leader="PHP Data Objects (PDO)- " href="http://php.net/pdo" />
|
||||
</slide>
|
||||
35
slides/mysql-limits/schemas-indexes.xml
Normal file
35
slides/mysql-limits/schemas-indexes.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version='1.0' encoding='iso-8859-1'?>
|
||||
<slide>
|
||||
<title>Database schemas: indexes</title>
|
||||
<blurb>
|
||||
Using standard keywords: KEY vs. INDEX
|
||||
</blurb>
|
||||
<example type="sql"><![CDATA[CREATE TABLE `#__categories` (
|
||||
`id` int(11) NOT NULL auto_increment,
|
||||
-- ...
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `cat_idx` (`section`,`published`,`access`),
|
||||
KEY `idx_section` (`section`),
|
||||
KEY `idx_access` (`access`),
|
||||
KEY `idx_checkout` (`checked_out`)
|
||||
) TYPE=MyISAM;
|
||||
]]>
|
||||
</example>
|
||||
|
||||
<blurb>
|
||||
KEY here is not standard SQL -- this is actually a MySQL shortcut for creating
|
||||
indexes on columns within the table. The CREATE INDEX statement is a much more
|
||||
portable syntax across databases (including MySQL).
|
||||
</blurb>
|
||||
|
||||
<example type="sql"><![CDATA[CREATE TABLE #__categories (
|
||||
id int(11) NOT NULL auto_increment,
|
||||
-- ...
|
||||
PRIMARY KEY (id);
|
||||
CREATE INDEX cat_idx ON #__categories (section, published, access);
|
||||
CREATE INDEX idx_section ON #__categories (section);
|
||||
CREATE INDEX idx_access ON #__categories (access);
|
||||
CREATE INDEX idx_checkout ON #__categories (checked_out);
|
||||
]]>
|
||||
</example>
|
||||
</slide>
|
||||
64
slides/mysql-limits/schemas-schemas.xml
Normal file
64
slides/mysql-limits/schemas-schemas.xml
Normal file
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Creating schemas portably</title>
|
||||
<blurb>
|
||||
Database abstraction layers also try to solve the problem of creating portable schemas.
|
||||
</blurb>
|
||||
<list>
|
||||
<bullet>
|
||||
Both MDB2_Schema and ADOdb XML Schema (AXMLS) define an XML format (different formats, of course), that their respective abstraction layer can use to create a schema for different databases.
|
||||
</bullet>
|
||||
<bullet>
|
||||
Both handle basic data types, as well as more sophisticated features like sequences, auto-increment columns, UNIQUE and NOT NULL constraints, indexes, and primary keys.
|
||||
</bullet>
|
||||
</list>
|
||||
<example type="sql" title="MDB2_Schema example"><![CDATA[<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<database>
|
||||
|
||||
<name><variable>database</variable></name>
|
||||
<create><variable>create</variable></create>
|
||||
<overwrite><variable>overwrite</variable></overwrite>
|
||||
|
||||
<table>
|
||||
|
||||
<name>ce_bad_word</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>badword_id</name>
|
||||
<type>integer</type>
|
||||
<notnull>true</notnull>
|
||||
<default>0</default>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>word</name>
|
||||
<type>text</type>
|
||||
<length>255</length>
|
||||
<notnull>true</notnull>
|
||||
<default> </default>
|
||||
</field>]]></example>
|
||||
<example type="sql" title="ADOdb-xmlschema example"><![CDATA[<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<schema version="0.2">
|
||||
|
||||
<table name="users">
|
||||
<desc>A typical users table for our application.</desc>
|
||||
<field name="userId" type="I">
|
||||
<descr>A unique ID assigned to each user.</descr>
|
||||
|
||||
<KEY/>
|
||||
<AUTOINCREMENT/>
|
||||
</field>
|
||||
|
||||
<field name="userName" type="C" size="16"><NOTNULL/></field>
|
||||
|
||||
|
||||
<index name="userName">
|
||||
<descr>Put a unique index on the user name</descr>
|
||||
<col>userName</col>
|
||||
<UNIQUE/>
|
||||
|
||||
</index>
|
||||
</table>]]></example>
|
||||
</slide>
|
||||
27
slides/mysql-limits/schemas-time.xml
Normal file
27
slides/mysql-limits/schemas-time.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Database schemas: date / time values</title>
|
||||
<blurb>
|
||||
Another fun one -- there are many columns defined as follows:
|
||||
</blurb>
|
||||
<example><![CDATA[`checked_out_time` datetime NOT NULL default '0000-00-00 00:00:00',
|
||||
]]></example>
|
||||
|
||||
<blurb>
|
||||
Two problems here:
|
||||
</blurb>
|
||||
<list>
|
||||
<bullet type="number">DATETIME is not a part of standard SQL; SQL92 defines DATE,
|
||||
TIME, and TIMESTAMP. TIMESTAMP in MySQL is a special column that automatically
|
||||
inserts the current date and time when a row is inserted, which explains why
|
||||
they use DATETIME instead. Most other databases use ~special registers~ to request
|
||||
a special value, such as %CURRENT TIMESTAMP%.
|
||||
</bullet>
|
||||
<bullet type="number">Second problem is that TIMESTAMP's string signature
|
||||
of YYYY-MM-DD hh:mm:ss should not accept all zeros -- what date and time is pure
|
||||
zeros? And in the context of the application, this special value does not actually
|
||||
help the data; a NULL value would be just as useful in identifying rows without a
|
||||
timestamp.
|
||||
</bullet>
|
||||
</list>
|
||||
</slide>
|
||||
68
slides/mysql-limits/schemas.xml
Normal file
68
slides/mysql-limits/schemas.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Database schemas</title>
|
||||
<blurb>
|
||||
So far we have not found any actual schema for the database -- that is, the
|
||||
structure of the database tables and columns. This is another area that
|
||||
databases tend to differ in rather creative ways. FRED contains all of the
|
||||
CREATE TABLE statements in %installation/sql/FRED.sql%. Let's take a look and
|
||||
try to spot some potential problems:
|
||||
</blurb>
|
||||
|
||||
<example type="sql"><![CDATA[CREATE TABLE `#__bannerclient` (
|
||||
`cid` int(11) NOT NULL auto_increment,
|
||||
`name` varchar(60) NOT NULL default '',
|
||||
`extrainfo` text NOT NULL,
|
||||
`checked_out` tinyint(1) NOT NULL default '0',
|
||||
`checked_out_time` time default NULL,
|
||||
PRIMARY KEY (`cid`)
|
||||
) TYPE=MyISAM;]]></example>
|
||||
|
||||
<list>
|
||||
<bullet effect="hide">
|
||||
%TYPE=MyISAM%: this clause will make any database other than MySQL choke.
|
||||
Note also that the table type gives you speed at the expense of transactions,
|
||||
relational integrity, row locking, and other standard database features.
|
||||
</bullet>
|
||||
|
||||
<bullet effect="hide">
|
||||
Quotation marks: most databases do not allow you to put single-quotes around table
|
||||
names, column names, and other SQL identifiers and will return an error message.
|
||||
You can use double-quotes, but these indicate to the database that the SQL
|
||||
identifier will be case-sensitive.
|
||||
</bullet>
|
||||
|
||||
<bullet effect="hide">
|
||||
The auto_increment clause used to define an identity column may not be
|
||||
understood by most databases, as this is non-standard DDL. "Standard" DDL
|
||||
defines an auto-incrementing column using the GENERATED ALWAYS AS IDENTITY
|
||||
clause instead:
|
||||
</bullet>
|
||||
</list>
|
||||
<list class="indent" effect="hide">
|
||||
<bullet>DB2 and Apache Derby offer the GENERATED ... IDENTITY CLAUSE</bullet>
|
||||
<bullet>PostgreSQL offers the SERIAL data type</bullet>
|
||||
<bullet>Microsoft SQL Server offers the IDENTITY data type attribute</bullet>
|
||||
<bullet>Oracle uses a combination of sequences and triggers to simulate an identity
|
||||
column.</bullet>
|
||||
</list>
|
||||
|
||||
<list>
|
||||
<bullet effect="hide">
|
||||
TEXT is a non-standard data type that best maps to a
|
||||
VARCHAR(65,535) or CLOB(64K) standard data type.
|
||||
</bullet>
|
||||
<bullet effect="hide">TINYINT (an integer accepting values from 0-255 or -128 to 128)
|
||||
can be mapped best to either a plain old INTEGER or a NUMERIC(3,0).
|
||||
In other cases in the DDL, the TINYINT column is defined with a maximum of 1 or 3
|
||||
-- this simply reduces the portability of the schema to other databases.
|
||||
I would say that a more portable
|
||||
approach would be to define a CHECK CONSTRAINT for a plain INTEGER column to
|
||||
ensure that the value is less than a defined value -- however, MySQL in turn
|
||||
does not support that. Of course, MySQL (until version 5, with STRICT MODE turned on)
|
||||
would simply convert a value outside of the defined maximum to the maximum
|
||||
value.
|
||||
</bullet>
|
||||
<bullet effect="hide">The "special" table name prefix %#__% is a way of avoiding table name clashes with other applications stored in the same database. Apache Derby, like most database servers, supports the use of ~schemas~ to provide a namespace for tables. For example, application %dboy%'s tables could be created in the %dboy% schema and accessible as %dboy.table1%, %dboy.table2% without conflicting with %dgirl.table1%, %dgirl.table2% existing in the same database.</bullet>
|
||||
</list>
|
||||
</slide>
|
||||
19
slides/mysql-limits/subject.xml
Normal file
19
slides/mysql-limits/subject.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>The subject application</title>
|
||||
<blurb>The application we'll be looking at (name changed to FRED to protect the innocent) is a well-known PHP content management system
|
||||
that has been in existence for over five years. No slights are intended against the
|
||||
developers of the code -- they created it with the intention of originally supporting
|
||||
MySQL alone.</blurb>
|
||||
<blurb>But that makes it a perfect candidate for our purposes.</blurb>
|
||||
<list>
|
||||
<bullet>It reflects a common "V1.0" application development scenario
|
||||
resulting from the literal adoption of the LAMP stack.</bullet>
|
||||
<bullet>FRED is now morphing into a "commercial support available for a fee" software
|
||||
package that requires broader database support.</bullet>
|
||||
<bullet>FRED is running into the same problems that we will see as we walk through the code.</bullet>
|
||||
</list>
|
||||
<blurb>~Note~: I have an idea of how FRED's team is approaching some
|
||||
of these problems, and can share those with you along the way, but any
|
||||
approach requires trade-offs of some kind.</blurb>
|
||||
</slide>
|
||||
13
slides/mysql-limits/target.xml
Normal file
13
slides/mysql-limits/target.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>The target database</title>
|
||||
<blurb>The goal I will be working towards is porting this application so that it will run
|
||||
against a standards-compliant database. Yes, *standards* is a loaded term.</blurb>
|
||||
<blurb>We'll be looking at porting to Apache Derby, which has as its project charter the
|
||||
goal of being the SQL92 standards-compliant database engine for the Apache DB project.</blurb>
|
||||
<link leader="Apache Derby project - " href="http://db.apache.org/derby" />
|
||||
<blurb>When you write for Apache Derby, you gain almost complete upwards compatibility with DB2
|
||||
(a standards-compliant and standards-setting database).</blurb>
|
||||
<blurb>~Full disclosure~: I wrote a book on Apache Derby coming out in the next couple of months,
|
||||
and as an IBM employee DB2 pays my mortgage and puts food on my table.</blurb>
|
||||
</slide>
|
||||
9
slides/mysql-limits/thank-you.xml
Normal file
9
slides/mysql-limits/thank-you.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<slide>
|
||||
<title>Finally...</title>
|
||||
<blurb class="big-center-blurb" align="center" margintop="3em">Thank you for being a |3333cc|wonderful| audience!</blurb>
|
||||
|
||||
<blurb class="big-center-blurb" align="center" margintop="3em">Dan Scott</blurb>
|
||||
<link class="medium-center-blurb" align="center" text="dan@coffeecode.net" href="mailto:dan@coffeecode.net" />
|
||||
<link class="medium-center-blurb" align="center" href="http://coffeecode.net" />
|
||||
</slide>
|
||||
4
slides/mysql-limits/title.xml
Normal file
4
slides/mysql-limits/title.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide template="titlepage">
|
||||
<title>Welcome!</title>
|
||||
</slide>
|
||||
17
slides/mysql-limits/why-not-mysql.xml
Normal file
17
slides/mysql-limits/why-not-mysql.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Why not code purely for MySQL?</title>
|
||||
<list>
|
||||
<bullet>MySQL allows many interesting variations on standard SQL</bullet>
|
||||
<bullet class="indent">In some cases, these can be considered developer-friendly</bullet>
|
||||
<bullet class="indent">These can also considered to be hindrances to supporting multiple databases</bullet>
|
||||
<bullet>Your hosting service might provide different databases (for example, hub.org offers PHP with PostgreSQL)</bullet>
|
||||
<bullet>You may be interested in enhancing your portable, standard database development skills</bullet>
|
||||
<bullet>Potential customers of your software might have standardized software environments and required a different database. Prediction from Mark Driver of Gartner Consulting on Sept. 13, 2005:</bullet>
|
||||
</list>
|
||||
<blurb class="nested-indent">*PHP will become a mainstream enterprise tool.*</blurb>
|
||||
<link class="nested-indent" href="http://trends.newsforge.com/trends/05/09/14/0653243.shtml?tid=138&tid=18" />
|
||||
<blurb>~Note~: Recent releases of MySQL show that it, too, is adopting more
|
||||
standard SQL -- so this presentation should help you port your MySQL application
|
||||
to MySQL, as well.</blurb>
|
||||
</slide>
|
||||
13
slides/mysql-limits/wrapping-up.xml
Normal file
13
slides/mysql-limits/wrapping-up.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<slide>
|
||||
<title>Reflections</title>
|
||||
<list>
|
||||
<bullet>Portability is hard. Refactoring an application that was hardcoded for a single database is a daunting task.</bullet>
|
||||
<bullet>Make your porting life easier by basing your application on standard SQL, then optimizing with database-specific tweaks where needed.</bullet>
|
||||
<bullet>There's a lot to be said for using the Model-View-Controller pattern to design your application. If your model is implemented in text files or a database partitioned over dozens of servers, the view and controller elements of the application shouldn't even notice.</bullet>
|
||||
<bullet>If each page in your application requires lots of nested dynamic includes as a result of your desire to support multiple databases, and it is affecting performance, consider designing the application installer as a simple precompiler that resolves all of the database-based includes at install time and places them statically in your pages.</bullet>
|
||||
<bullet>If you are using PHP 5.x, consider using PDO as your primary native database access interface. You won't have to worry about significant API differences, and will be able to focus on plain old SQL and database differences instead.</bullet>
|
||||
<bullet>If you are using PHP 4.x, consider using a database abstraction layer (MDB2, ADOdb, PEAR::DB) with an opcode cache to minimize API differences. Remember that it might take time for new database APIs to be added to any particular abstraction.</bullet>
|
||||
<bullet>You might be surprised by what databases your users are going to want to run your application on in the future. Leave some room in your design to accept their generous contribution of a model that supports their database.</bullet>
|
||||
</list>
|
||||
</slide>
|
||||
200
slides/mysql-limits/yuck.css
Normal file
200
slides/mysql-limits/yuck.css
Normal file
@@ -0,0 +1,200 @@
|
||||
<style title="Default" type="text/css">
|
||||
body {
|
||||
font-size: 10pt;
|
||||
margin-top:0em;
|
||||
margin-left:0em;
|
||||
margin-right:0em;
|
||||
margin-bottom:0em;
|
||||
background-image: url(slides/intro/background.png);
|
||||
background-attachment : fixed;
|
||||
background-repeat : repeat
|
||||
}
|
||||
div.mainarea {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
div.blurb {
|
||||
margin-top: 0.5em;
|
||||
font-size: 2em;
|
||||
}
|
||||
div.navbar_title {
|
||||
font-size: 2em;
|
||||
}
|
||||
div.sticky {
|
||||
margin: 0px;
|
||||
position: fixed;
|
||||
top: 0em;
|
||||
left: 0em;
|
||||
right: auto;
|
||||
bottom: auto;
|
||||
width: auto;
|
||||
}
|
||||
div.bsticky {
|
||||
margin: 0px;
|
||||
position: fixed;
|
||||
top: auto;
|
||||
left: 0em;
|
||||
right: auto;
|
||||
bottom: 0em;
|
||||
width: 100%;
|
||||
}
|
||||
div.shadow {
|
||||
background: #777777;
|
||||
padding: 0.5em;
|
||||
}
|
||||
div.navbar {
|
||||
background: url(images/trans.png) transparent fixed;
|
||||
padding: 4px;
|
||||
margin: 0px;
|
||||
height: 6em;
|
||||
color: #ffffff;
|
||||
font-family: verdana, tahoma, arial, helvetica, sans-serif;
|
||||
z-index: 99;
|
||||
}
|
||||
div.emcode {
|
||||
background: #cccccc;
|
||||
border: thin solid #000000;
|
||||
padding: 0.5em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
div.output {
|
||||
font-family: monospace;
|
||||
background: #eeee33;
|
||||
border: thin solid #000000;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
table.index {
|
||||
background: #cccccc;
|
||||
border: thin dotted #000000;
|
||||
padding: 0.5em;
|
||||
font-family: monospace;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
td.index {
|
||||
background: #cccccc;
|
||||
padding: 1em;
|
||||
font-family: monospace;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
}
|
||||
p,li {
|
||||
font-size: 2em;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.c2right {
|
||||
margin : 1em 1em 0em 0em;
|
||||
padding-left : 1%;
|
||||
padding-right : 1%;
|
||||
border-style : solid;
|
||||
border-top-width : 1px;
|
||||
border-right-width : 1px;
|
||||
border-bottom-width : 1px;
|
||||
border-left-width : 1px;
|
||||
border-right-color : inherit;
|
||||
border-left-color : inherit;
|
||||
width : 46%;
|
||||
float : right;
|
||||
}
|
||||
.c2rightnb {
|
||||
margin : 1em 1em 0em 0em;
|
||||
padding-left : 1%;
|
||||
padding-right : 1%;
|
||||
width : 46%;
|
||||
float : right;
|
||||
}
|
||||
.c2left {
|
||||
margin : 1em 1em 0em 0em;
|
||||
padding-left : 1%;
|
||||
padding-right : 1%;
|
||||
border-style : solid;
|
||||
border-top-width : 1px;
|
||||
border-right-width : 1px;
|
||||
border-bottom-width : 1px;
|
||||
border-left-width : 1px;
|
||||
border-right-color : inherit;
|
||||
border-left-color : inherit;
|
||||
width : 46%;
|
||||
float : left;
|
||||
}
|
||||
.c2leftnb {
|
||||
margin : 1em 1em 0em 0em;
|
||||
padding-left : 1%;
|
||||
padding-right : 1%;
|
||||
border-style : none;
|
||||
width : 46%;
|
||||
float : left;
|
||||
}
|
||||
.box {
|
||||
margin : 0em 0em 0em 0em;
|
||||
padding-left : 1%;
|
||||
padding-right : 1%;
|
||||
border-style : solid;
|
||||
border-top-width : 1px;
|
||||
border-right-width : 1px;
|
||||
border-bottom-width : 1px;
|
||||
border-left-width : 1px;
|
||||
border-right-color : inherit;
|
||||
border-left-color : inherit;
|
||||
float : left;
|
||||
}
|
||||
|
||||
A.linka { text-decoration: none; color: #000000; }
|
||||
td.foo {color: #ffffff; font-family: arial,verdana,helvetica; font-size: 70%}
|
||||
span.c4 {position: fixed; bottom: 0.5em; right: 4em; top: auto; left: auto; color: #ffffff; font-family: arial,verdana,helvetica; font-size: 70%}
|
||||
td.c3 {color: #CC6600; font-family: arial, helvetica, verdana}
|
||||
span.c2 {color: #ffffff; font-family: arial,hevetica,verdana}
|
||||
span.c5 {position: fixed; bottom: 0.5em; right: 1em; top: auto; left: auto; color: #000000; font-family: arial,verdana,helvetica; font-size: 80%}
|
||||
td.c1 {font-family: arial,helvetica,verdana; font-size: 80%}
|
||||
tt { font-size: 0.8em; }
|
||||
div.link {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.indent {
|
||||
margin-left: 2em;
|
||||
font-size: 1.8em;
|
||||
}
|
||||
.nested-indent {
|
||||
margin-left: 4em;
|
||||
font-size: 1.8em;
|
||||
}
|
||||
.example-title {
|
||||
font-weight: bold;
|
||||
font-size: 2em;
|
||||
}
|
||||
ul.indent > div > li {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.example {
|
||||
background-color: #cccccc;
|
||||
font-size: 1.5em;
|
||||
font-family: courier, monospace;
|
||||
border-width: thick;
|
||||
border-style: outset;
|
||||
border-color: #000000;
|
||||
}
|
||||
|
||||
div.medium-center-blurb {
|
||||
font-size: 3em;
|
||||
text-align: center;
|
||||
padding-top: 1em;
|
||||
}
|
||||
|
||||
div.big-center-blurb {
|
||||
font-size: 5em;
|
||||
text-align: center;
|
||||
padding-top: 1em;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user