Introduction

Database programming is like a confused little monkey. Puddlin’ on the floor and screaming at the walls, it’s a tedious little thing that’s prone to memory leaks and lots of errors and mistakes. To the dismay of anyone hoping to avoid a headache, database programming is ridiculously redundant and incredibly boring. Because web development loves its little monkey sooooo much, it’s unfortunately here to stay.

HTML Form Builder

Today, however, we’re going to show a little love to the thing we all love to hate, and go over a small, clean and reusable class to help us in all our projects. This class will improve our relationship with programming databases in the following ways:

  • First, it will place all of your database code in a single file. If you ever need to alter a function, the change carries through to the entire project.

  • Second, it makes your life easier by handling INSERT and UPDATE string syntax for you. Because most errors occur while modifying a database, it’s just nice to have a little help. Hopefully, you will never encounter a missing apostrophe ever again.

  • Third, and most importantly, it’s going to remove most of your redundant code. This class can also handle the creation and deletion of objects and you’ll ever need to get out your old project to find out how to do “that database thing you did.”

The Code

The database class that follows consists of 4 functions and is portable across most databases (however, it’s only been tested on SQL Server and MySQL). Because we here at Particletree are code agnostic about these things, we got it in three languages for you today.

Implementation

1) Include the class in your file.

//PHP
include "cDatabase.php";

.

//ASP
<!--#include file="cDatabase.asp" -->

.

//.NET
Imports projectName.cDatabase

2) If you are following along with us, you’ll need to create a simple database table. We’re going to name it “tPersons” and it’ll have the following 4 fields: - personID - firstName - lastName - age

3) Change the connection string at the top of the class to match your database. If your using PHP or .NET, this is where the object is created.

//PHP
$cDB = new cDatabase(host, user, password, database);

.

//ASP
sMysqlConn = "Your connection string here!"

.

//.NET
Dim cDB as New cDatabase(connString)

4) And you’re done. Now you can call the functions listed below.

sqlInsert

This is used to get data into your database. To call the sqlInsert function, you’ll need to create two arrays, and a reference to those arrays and our demo table called ‘tPersons’.

//PHP
$names = array("firstName", "lastName", "age");
$values = array("ryan", "campbell", "22");
$cDB->sqlInsert($names, $values, "tPersons");

.

//ASP
dataNames = array("firstName", "lastName", "age")
dataValues = array("Ryan", "Campll", "22")
sqlInsert dataNames, dataValues, "tPerson"

.

//.NET
dim dataNames() as String = {"firstName", "lastName", "age"}
dim dataValues() as String = {"Ryan", "Campll", "22"}
cDB.sqlInsert(dataNames, dataValues, "tPerson")

The first array consists of strings naming the database field you wish to have inserted. The second array contains the string values corresponding to the first array. Order is important here, the positions in the arrays must match.

sqlUpdate

As you may know, sql UPDATE statements modify existing records in a database. ‘sqlUpdate()’ works almost identical to ‘sqlInsert()’ above, except it adds a fourth paramter called condition. The condition is simply a string stating the sql condition statement a record must meet in order to be affected by the update.

//PHP
$names = array("lastName");
$values = array("campbell");
$cDB->sqlUpdate($names, $values, "tPersons", "WHERE firstName = 'Ryan'");

.

//ASP
dataNames = array("lastName")
dataValues = array("Campbell")
sqlUpdate dataNames, dataValues, "tPerson", "WHERE firstName = 'Ryan'"

.

//.NET
dim dataNames() as String = {"lastName"}
dim dataValues() as String = {"Campbell"}
cDB.sqlUpdate(dataNames, dataValues, "tPerson", "WHERE firstName = 'Ryan'")

Also note that the arrays do not need to contain values for all of the fields. If you do not reference a field in the array, it will not be affected by the update. In the example above, only records with a first name of “Ryan” will have their last name updated to “Campbell”.

ExecuteReader

My favorite function in the class. ‘ExecuteReader()’ will return a recordset of all matching records in a SELECT query. You do not have to create any objects, or worry about the execution of the query. Pass in a string with a properly formatted sql select statement, and a recordset is returned for you to do as you will. Here’s an example:

//PHP
$rs = $cDB->ExecuteReader("SELECT * FROM tPersons");
while ($row = mysql_fetch_array($rs, MYSQL_ASSOC)) {
  echo $row['firstName'];
}

.

//ASP
rs = ExecuteReader("SELECT * FROM tPerson " & "WHERE firstName = 'Ryan'")'Write the first and last name if the recordset is not empty.
if not rs.EOF then 
  response.write rs("firstName") & " " & rs("lastName")
end if
rs.close

.

//.NET
Dim rs As SqlDataReader
rs = cDB.sqlSelect("SELECT * FROM tPersons")        
If rs.Read Then
   response.write(rs("firstName"))
End Ifrs.Close()
rs = Nothing

What’s also nice about this function is that it minimizes the chance memory leaks.

ExecuteNonQuery

‘ExecuteNonQuery’ executes a query that doesn’t return any data. The “NonQuery” part is actually a misnomer and comes from a function that Microsoft introduced in .NET. I liked it so much that I ported to PHP and ASP here. One way to use this function is to remove data from a database. Basically, DELETE statements are just strings that need to be executed. Here is how we would execute a DELETE statement using ‘ExecuteNonQuery()’.

//PHP
$cDB->ExecuteNonQuery("DELETE tPerson WHERE firstName = 'Ryan'");

.

//ASP
ExecuteNonQuery("DELETE tPerson WHERE firstName = 'Ryan'")

.

//.NET
cDB.ExecuteNonQuery("DELETE tPerson WHERE firstName = 'Ryan'")

Depending on the language you’re using, ‘ExecuteNonQuery’ can save you up to 6 lines of code each time you call it.

How It Works

So now that you know how to call the functions, let’s look at what’s under the hood.

sqlInsert

//PHP
function sqlInsert($dataNames, $dataValues, $tableName)
{
    $sqlNames = "INSERT INTO " . $tableName . "(";
    for($x = 0; $x < count($dataNames); $x++) {
        if($x != (count($dataNames) - 1)) {
            $sqlNames = $sqlNames . $dataNames[$x] . ", ";
            $sqlValues = $sqlValues . "'" . $dataValues[$x] . "', ";
        }
        else {
            $sqlNames = $sqlNames . $dataNames[$x] . ") VALUES(";
            $sqlValues = $sqlValues . "'" . $dataValues[$x] . "')";
        }
    }
    echo $sqlNames . $sqlValues;
    ExecuteNonQuery($sqlNames . $sqlValues);
}

.

//ASP
function sqlInsert(dataNames, dataValues, tableName)
   sqlNames = "INSERT INTO " & tableName & "("
   for x = 0 to ubound(dataNames)
      if x <> ubound(dataNames) then
         sqlNames = sqlNames & dataNames(x) & ", "
         sqlValues = sqlValues & "'" & dataValues(x) & "', "
      else
         sqlNames = sqlNames & dataNames(x) & ") VALUES
         sqlValues = sqlValues & "'" & dataValues(x) & "')"
      end if
   next   ExecuteNonQuery(sqlNames & sqlValues)
end function

.

//.NET
Public Sub sqlInsert(ByVal dataNames(), ByVal dataValues(), ByVal tableName)Dim sqlNames As String
Dim sqlValues As String
Dim x As IntegersqlNames = "INSERT INTO " & tableName & "("
For x = 0 To UBound(dataNames)
    If x <> UBound(dataNames) Then
        sqlNames = sqlNames &     Replace(dataNames(x), "'", "''") & ", "
        sqlValues = sqlValues & "'" & Replace(dataValues(x), "'", "''") & "', "
    Else
        sqlNames = sqlNames & Replace(dataNames(x), "'", "''") & ") VALUES("
        sqlValues = sqlValues & "'" & Replace(dataValues(x), "'", "''") & "')"
    End If
NextExecuteQuery(sqlNames & sqlValues)End Sub

Basically, ‘sqlInsert()’ loops through the name / value paired arrays, and builds the sql string. The resulting string will appear in the following proper syntax:

INSERT INTO tPersons(firstName, lastName, age) 
VALUES('Ryan', 'Campll', '22')

Also note the call to ‘ExecuteNonQuery()’ which executes the query. We’ll look at this function at the end.

sqlUpdate

//PHP
function sqlUpdate($dataNames, $dataValues, $tableName, $condition)
{
    $sql = "UPDATE " . $tableName . " SET ";
    for($x = 0; $x < count($dataNames); $x++) {
        if($x != (count($dataNames) - 1)) {
            $sql = $sql . $dataNames[$x] . "= '" . $dataValues[$x] . "', ";
        }
        else {
            $sql = $sql . $dataNames[$x] . "= '" . $dataValues[$x] . "' ";
        }
    }
    $sql = $sql . $condition;
    echo $sql;
    ExecuteNonQuery($sql)
}

.

//ASP
function sqlUpdate(dataNames, dataValues, tableName, condition)
   sql = "UPDATE " & tablename & " SET "
   for x = 0 to ubound(dataNames)
      if x <> ubound(dataNames) then
         sql = sql & dataNames(x) & "= '" & dataValues(x) & "', "
      else
         sql = sql & dataNames(x) & "= '" & dataValues(x) & "' "
      end if
   next
   sql = sql & condition
   ExecuteNonQuery(sql)
end function

.

//.NET
Public Sub sqlUpdate(ByVal dataNames(), ByVal dataValues(), ByVal tableName, ByVal condition)Dim sql As String
Dim x As Integersql = "UPDATE " & tableName & " SET "
For x = 0 To UBound(dataNames)
    If x <> UBound(dataNames) Then
        sql = sql & dataNames(x) & "= '" & Replace(dataValues(x), "'", "''") & "', "
    Else
        sql = sql & dataNames(x) & "= '" & Replace(dataValues(x), "'", "''") & "' "
    End If
Next
sql = sql & conditionExecuteQuery(sql)End Sub

‘sqlUpdate()’ loops through the name / value paired arrays, and builds the sql string. The resulting string will appear in proper syntax:

UPDATE tPersons 
SET lastName = 'Campbell'
WHERE firstName = 'Ryan'

ExecuteReader

//PHP
function ExecuteReader($query){
    $conn = mysql_connect($this->host, $this->user, $this->password);
    mysql_select_db ($this->database);
    $rs = mysql_query($query,$conn);
    mysql_close($conn);
    return $rs;
} 

.

//ASP
function ExecuteReader(sql)
  Set oConn = Server.CreateObject("ADODB.Connection")  oConn.Open sMysqlConn
  Set rs = Server.CreateObject("ADODB.Recordset")
  rs.CursorLocation = 3
  rs.Open sql, oConn, 3, 4  
  set rs.ActiveConnection = Nothing
  Set ExecuteReader = rs
  oConn.Close
  Set oConn = Nothing
  Set rs = Nothing
end function

.

//.NET
Public Function sqlSelect(ByVal sQuery As String) As SqlDataReader    Dim conn As SqlConnection
    Dim db As SqlCommand
    Dim rs As SqlDataReader    conn = New SqlConnection(eConn)
    conn.Open()
    db = New SqlCommand(sQuery, conn)
    rs = db.ExecuteReader(CommandBehavior.CloseConnection)
    Return rsEnd Function

The key line to point out here (Does not apply to PHP) is the following:

//PHP
This excerpt applies to ASP and .NET only.

.

//ASP
set rs.ActiveConnection = Nothing

.

//.NET
rs = db.ExecuteReader(CommandBehavior.CloseConnection)

Normally you may try to return a recordset before everything has been properly closed. This line allows you to close all database connections, but keep the recordset active. This prevents memory leaks from occuring. When you call this function, you are given a handle on the object that allows you to close it anywhere in your code.

ExecuteNonQuery

//PHP
function ExecuteNonQuery($sql){
    $conn = mysql_connect($this->host, $this->user, $this->password);
    mysql_select_db ($this->database);
    $rs = mysql_query($sql,$conn);
    settype($rs, "null");
    mysql_close($conn);
}

.

//ASP
sub ExecuteNonQuery(sql)
  set db = Server.CreateObject("ADODB.Connection")
  db.open sMysqlConn
  set rs = db.execute(sql)
  set rs = nothing
  db.close
  set db = nothing
end sub

.

//.NET
Public Sub ExecuteQuery(ByVal sQuery As String)    Dim conn As SqlConnection
    Dim db As SqlCommand    conn = New SqlConnection(eConn)
    conn.Open()
    db = New SqlCommand(sQuery, conn)
    db.ExecuteNonQuery()    conn.Close()
    db.Dispose()End Sub

This function is referenced by ‘sqlInsert()’ and ‘sqlUpdate()’. This is a core function because it creates and destroys the objects needed to execute any database operation. I spoke about redundancy before: These are the 6 lines you would write whenever you wanted to communicate with a database, and now they are consolidated into one function.

PHP Users: Note that you have to use the appropriate function for your database (ie: mysql_close vs mssql_close) and modify the connection string in this function.

Addendum

Obviously, these classes can easily be enhanced / expanded upon and so if you do so in any way, I’d love to update them and give you credit for it. As always, comments and suggestions are always welcome. Let me also point out that I intentionally left out error handling to make the tutorial easier to understand. Be sure that you’re also error checking your database activity. Because this tutorial covers 3 different languages, I didn’t get a chance to really dive into the particular quirks of each language. If you have any questions, please let me know.

Additional Help

If you need to touch up on your database skills check out the following resources for each language:

HTML Form Builder
Ryan Campbell

Database Simplicity With Class by Ryan Campbell

This entry was posted 5 years ago and was filed under Features.
Comments are currently closed.

· 29 Comments! ·

  1. Kevin Hale · 5 years ago

    Even though my knowledge on databases is slim, I’d just like to share the following:

    Ryan smells like poo.

    P.S. Good work, buddy. This is very sweet stuff.

  2. Dave · 5 years ago

    Nice, that will undoubtedly make a few people’s lives easier!

    However, if you’re the type of person who has had enough of messing around doing the little things in web application development and just want to get on and develop, I’d strongly encourage to you check out Ruby On Rails. RoR does this kind of stuff and a whole lot more, using a whole lot less code. It’s beautiful too, and you’ll wonder how you coped before you knew about it:

    http://www.rubyonrails.com/

  3. mark · 5 years ago

    Microsoft OLE DB Provider for SQL Server error ‘80040e14’

    Line 1: Incorrect syntax near ‘=’.

    /tutorials/dbClass/cDatabase.asp, line 19

    riiiight… i’m so gonna use this! i don’t even have to write gibberish code myself, i’ve got ryan’s website to spew out errors FOR ME!

    ;)

  4. Brian Benzinger · 5 years ago

    Excellent job. Looks great and seems very easy. I have never tried to use a class for managing databases before, I usually like to just manaully do it so I can see everything tats going on right there. But, Im going to give it a test run and see how I like it. Great work, nice example of it too.

  5. Brian · 5 years ago

    Anything for StoredProcs?

  6. Brian · 5 years ago

    Also just curious, does it handle sql-injection?

    If my insert value has an apostrophe does it handle well or commas?

  7. Ryan Campbell · 5 years ago

    Dave: When I have the time, that will be the next language I learn. I’m looking forward to it.

    Mark: Hah I knew it was inevitable that someone would get an error. Was that on the demo page? If so, it is not because of the class, but rather the way I structured the page (ie: no validation and timed out session variables) - if you downloaded the class and got that error, let me know so I can figure out what happened.

    Brian: Stored Procs - not yet. SQL Injection - I am writing a follow up to this article now. The follow handles all special characters (and injection) as well as writing a recordset to xml, seed tables, and a few other small things. Check back for the follow up in about two weeks.

    Thank you everyone for the feedback!

  8. David · 5 years ago

    The script as written seems to cause a database connection to be opened then closed for every query. Isn’t that a sub-optimal solution for the typical page?

    (It was my impression, perhaps incorrectly so, that opening a connection was a much more expensive action than running a query. So a 1:1 connection:query might cause issues in the long run.)

  9. Ryan Campbell · 5 years ago

    David – you are correct. When I revisit this article I will probably change it by adding db open / close functions possibly in the constructor. This is where 3 languages become a problem. .NET has a constructor / destructor. PHP only has a constructor. ASP has no class support at all. I may have to offer different versions.

    As a note though, I use this class in query intensive program that has 1000 – 3000 users on at any given time. Performance is pretty good, but I can definately see it becoming a problem with handling a large site.

    Thanks for calling that out – makes me a better programmer.

  10. David · 5 years ago

    I quite agree it’s not really a problem under most circumstances. It’s just one of those issues that nags at me, simply because I know that it’s not the optimum solution. :-)

    (Off the top of my head, I think that PHP automatically closes non-persistent connections at the end of script execution, so creating the connection in the constructor and then just ignoring it could be a practical approach. Not necessarily elegant, but…. You’d be able to get away with only needing ASP to have a different structure, at least.)

  11. worm · 5 years ago

    wow. absolutely great page.

  12. some guy · 5 years ago

    dear sir,

    i’m using php to query a mysql db on a windows server and found that after performing $rs = $db->Execute(“SELECT * FROM myocations”); and then a while ($row = mysql_fetch_array($rs, MYSQL_ASSOC)) I get told this:

    Warning: mysql_fetch_array(): supplied argument is not a valid MySQL result resource in

    when i try and replace the while ($row = mysql_fetch_array($rs, MYSQL_ASSOC)) with while (!$rs->EOF) i get a loop that goes forever so i thin i really need to use the mysql_fetch_Array but do not know what i am doing wrong.

    thank you yaffa

  13. Mark · 5 years ago

    ASP/VBScript does in fact support classes (at least > v5) - with constructors and destructors as well. It’s been awhile since I’ve done VBScript, but IIRC the constructor is Class_Initialize() and destructor is Class_Terminate(). Constructor fires on New (obviously), and destructor fires when setting to Nothing or being GC. Property getters and setters are also a little strange on syntax due to having to use Set syntax to assign an object. So, there’s a “Property Let” for basic subtypes (integer, string, double, etc.) and a “Property Set” for objects. Hope that helps you keep a manageable code base. ;)

  14. Matthijs · 5 years ago

    Hi Ryan, sorry to be so impatient , but when’s the follow-up article going to be published? (ok, that’s what you get from teasing us ;) Thanx.

  15. Ryan Campbell · 5 years ago

    Don’t worry, the follow up is coming. I just finished rewriting / cleaning up the PHP and ASP version of this tutorial, and am in the process of cleaning up the additional functions that I will introduce.

    Sorry for the delay - I’ve got a lot on my plate right now. There will be a lot of changes coming to Particletree soon, so I can only ask you to keep being patient :)

  16. Matthijs · 5 years ago

    Ryan, no worries. All your work is appreciated! There’s already so much to read on this side… And I know about the upcoming changes, so understand you’re really busy. And about those: I’m definitely interested in the upcoming Pdf-magazine. Really hope you’ll find a way to make it all work and worthwile. Good luck.

  17. Oliver · 5 years ago

    Nice work there! Is it me or the Switch Language (PHP/ASP/ASP.NET) function doesn’t work on this page? (used IE 5.5 and Firefox 1.0.7)? thanks

  18. Ryan Campbell · 5 years ago

    You are correct - the switch code functions have been temporarily disabled. We are adding new features to the way code is presented in our tutorials, so it will not be fixed until that update comes out. Sorry for any problesm this may cause.

  19. Relapse · 5 years ago

    How does your sqlInsert statement handle rows with ‘autoincrement’ or trigger+sequences?

  20. Ryan Campbell · 5 years ago

    So far I have had no problems with auto increment. I haven’t tried it with a trigger yet, but I can’t imagine there being any problems.

  21. Jonathan Snook · 5 years ago

    One of the things I have always tried to do with any SQL calls is to release the connection as quickly as possible. In this case, have ExecuteReader (oh, someone has been using .Net) return a multidimensional array. In ASP, I usually use rs.getRows() to do this. In PHP, I think you’d have to drop that while loop into the function to build the array to return. And I can’t remember what it is in .Net but likely the getRows method still exists as I think it’s a part of ADO.

  22. Ryan Campbell · 5 years ago

    Jonathan - I have always wondered about the performance benefits of that. I have never been in a situation where briefly holding on to a recordset has caused any problems. I can see the necessity for extremely large scale sites that are being bottlenecked at the database. At the same time, looping to create an array, and then looping through the array to get your data can easily slow down your page if the result set is large enough. But I am curious to know about what experience you have with both that it has become habit for your to drop the recordset.

  23. Jonathan Snook · 5 years ago

    True, having to loop through all rows with the connection open in PHP has always been a peeve of mine. To be honest, I’ve never had any poor experiences with leaving the connection open that had me resorting to using getRows. It was simply a technique that I learned early on and that made sense to me so I kept doing it. It’d be interesting to do some stress tests that could confirm that theory either way.

  24. Grant Palin · 5 years ago

    Regarding the issue of connections being resource-heavy in Classic ASP, it is good practice to open a connection only when you’re about to use it, and close it as soon as you can. But opening and closing multiple connections (say, across a whole site) is OK, since ASP does some sort of connection pooling.

  25. Maarten Blokdijk · 4 years ago

    Microsoft VBScript runtime error ‘800a01b6’

    Object doesn’t support this property or method: ‘EOF’

    i am using the asp version…. but i cannot get it to work.

  26. Chris Stormer · 4 years ago

    Was an update to this ever published?

  27. Diarmaid · 3 years ago

    Hi guys, just trying to download the php class and it looks like the download is broken: Made it in Warning: Cannot modify header information - headers already sent by (output started at /home/2462/domains/particletree.com/html/mint/pepper/orderedlist/downloads/class.php:104) in /home/2462/domains/particletree.com/html/mint/pepper/orderedlist/downloads/download.php on line 32

    any way to get a hold of the code? Many thanks, Diarmaid

  28. Diarmaid · 3 years ago

    DOwnload is working now, great, thanks!