navigate site menu

Start learning with our library of video tutorials taught by experts. Get started

SQLite 3 with PHP Essential Training
Richard Downs

SQLite 3 with PHP Essential Training

with Bill Weinman

 


In SQLite 3 with PHP Essential Training, Bill Weinman addresses all of SQLite’s major features in the context of the PHP environment. This course covers the fundamentals of SQLite, including a thorough overview of its unique data type system, expressions, functions, transactions, views, and event triggers. A functional CRUD application and web site testimonial engine are demonstrated, and a quick-start guide is included to get experienced developers up and running fast. Exercise files are included in the course.
Topics include:
  • Creating a database
  • Setting permissions for a database
  • Storing data with SQLite data types
  • Using SQL statements like SELECT and UPDATE
  • Understanding expressions in SQLite
  • Matching patterns and values
  • Working with core functions
  • Counting, finding, and grouping with aggregates
  • Formatting dates and times
  • Sorting and indexing
  • Reusing queries with subselects and views
  • Automating queries with triggers

show more

author
Bill Weinman
subject
Developer, Web, Databases, Web Development
software
SQLite 3
level
Beginner
duration
6h 2m
released
Oct 20, 2010

Share this course

Ready to join? get started


Keep up with news, tips, and latest courses.

submit Course details submit clicked more info

Please wait...

Search the closed captioning text for this course by entering the keyword you’d like to search, or browse the closed captioning text by selecting the chapter name below and choosing the video title you’d like to review.



Introduction
Welcome
00:04Hi! I'm Bill Weinman, and welcome to SQLite 3 with PHP Essential Training.
00:10In this course, we'll explore the world of SQLite 3, a full-featured, relational
00:14database engine, entirely self-contained in a driver.
00:17SQLite has really changed the way people think of a database engine.
00:22It's a small, fast, reliable, ACID- compliant, fully transactional database
00:27engine that can run a busy web site, or fit in a phone.
00:31I'll explain the major features of SQLite, and its usage in a PHP environment,
00:36focusing on real-world examples to aid you in implementing this amazing little
00:41database engine in your applications.
00:43I'll show you the details of its SQL usage, core and aggregate functions, its
00:50unique manifest typing scheme, and even how to create user-defined functions in PHP.
00:56I'll demonstrate how to use subselects, views, and triggers to improve
01:01performance, compliance, and security in your database applications.
01:05This course is a great introduction to SQLite 3, but some programming and
01:10database experience will really help you get the most out of these technologies.
01:14I've been a programmer for more than 30 years, and I have rarely been this
01:18excited about a database engine.
01:20So, let's get under the hood with SQLite 3 with PHP Essential Training.
Collapse this transcript
What is SQLite and what are the prerequisites?
00:00SQLite is a full-featured, relational database engine, entirely self-contained in a driver.
00:05SQLite is a complete implementation of ANSI standard SQL.
00:10It is a fast, reliable, ACID- compliant transactional database engine.
00:16SQLite is well suited for web, desktop, or embedded applications.
00:21SQLite is not a database server.
00:24It is a fully self-contained database management system in a driver.
00:28That means you don't need login credentials.
00:30You don't need to connect to anything. SQLite opens a file and uses it as a database.
00:37In order to get the most out of this course, you will need some understanding of
00:41programming in PHP or another similar scripting language.
00:45You'll need some understanding of relational databases and SQL.
00:49You'll need a web server with PHP 5.3 or later and SQLite 3.6 or later.
00:56Keep in mind that SQLite 3.6 comes with most current installations of PHP.
01:01You will need a text editor;
01:04a word processor will not work for this purpose.
01:06A word processor saves formatting information along with the text in its files,
01:11and that won't work for writing code.
01:13The XAMPP package is used for demonstrations in this course.
01:17You do not need XAMPP in order to do the exercises in this course;
01:21you may use any server with PHP and SQLite, but XAMPP is convenient for testing
01:26code on your desktop.
01:28XAMPP runs on most modern operating systems, including current versions of
01:32Windows and Mac OS X, and it includes the latest versions of PHP, PDO, and
01:38SQLite, all used in the examples in this course.
01:42Installation instructions for XAMPP on Windows and Mac OS X are included in
01:46this course as well.
Collapse this transcript
Using the exercise files
00:00If you're a premium member of the lynda.com Online Training Library, or if you're
00:04watching this tutorial on a DVD-ROM, you have access to the exercise files used
00:09throughout this title.
00:10The ExerciseFiles folder for this title is not going to be used from the
00:14desktop. Please follow the instructions in the installation movie for
00:19your operating system.
00:20The layout of the ExerciseFiles folder is like this. The lib and assets
00:26folders, those are used for the sandbox environment that's used for some of the
00:30examples in this course.
00:33Some of the chapter files have PHP scripts, and when they have PHP scripts they
00:38will usually be a start.php or something like this, create-start.php.
00:44These are the starting points for those exercises.
00:47Make a copy of that starting point script and work from the copy.
00:51That way you always have a place you can fall back to, if you need to get started again.
00:55The end files are there for comparison, to see if your results match mine.
01:00Some of the chapters have simply an examples.sql file.
01:05These files have snippets of SQL that are the same, or similar, to the examples
01:11that I'm typing into the screen, as we're doing the exercises in that chapter.
01:15You can use these to copy and paste or to see if your results don't match mine.
01:21The folders CRUD, SID, and Testimonials contain complete applications.
01:27The Testimonials application is an application for displaying testimonials on your web site.
01:32The CRUD application is a demonstration application of the four basic functions
01:37of a database: create, read, update, and delete.
01:41And the SID application is used to demonstrate SQL statements throughout this course.
01:46The SQL folder contains the SQL used to create the databases that are used as
01:51examples in this course.
01:53Each of these files is an SQL script, which is used to pipe into the SQLite 3
02:00command line application to create the databases.
02:04If you prefer, the complete working databases are available in these .db files,
02:10and you do not need to create them from scratch.
02:12One of the features of SQLite is that its database files are cross-platform, so
02:17these same files will work on any operating system.
02:21If you don't have access to the exercise files, you can follow along from scratch
02:24or with your own assets.
02:26So let's get started.
Collapse this transcript
1. Quick Start
Getting the most out of the quick start
00:00If you're a developer with experience in PHP, or a similar scripting language, and
00:05perhaps a different database, this Quick Start is designed to help you get up and
00:09running with SQLite quickly, and with a minimum of detail.
00:13The examples in this Quick Start use the bwSQLite 3.php library to interface
00:20with SQLite 3 from PHP.
00:22This is a library that I wrote that uses PDO, PHP's unified database interface.
00:29I strongly recommend that you write your own library with your own interface
00:33that works well for you.
00:35In the meantime, feel free to use this one for the Quick Start and perhaps as a
00:40basis for your own library.
00:42The exercises in this Quick Start use this sandbox framework that I created
00:46for the purpose of demonstrating and experimenting with SQL and SQLite in the PHP environment.
00:53This is what it looks like in the editor.
00:56We're going to start at the bottom here, and we're going to look at these functions
00:59here: message, error_message and error, and what these do is these display those
01:05messages that you see on the screen in the sandbox environment in the browser.
01:11Coming back up to the top, you'll notice we have some constants to find.
01:15We read in the bwSQLite 3 library with the require_once directive, and we call
01:21init, main, and page.
01:23init is used for setting up the database and setting up the variables and arrays
01:28that are used by the sandbox environment,
01:31main is where we will put our code that we're going to be using for operating on
01:35the database, and page is used for displaying the page on the browser screen.
01:41Here in init, we initialize our display variables, we set up a few variables for
01:46use on the screen, and we create the table.
01:50The database is open with the bwSQLite 3 object, and we set up the table
01:56name, and then we have some SQL for creating the table and inserting four
02:01rows into the table.
02:02This try and catch is PHP's exception handling, and the bwSQLite 3 library uses
02:11exceptions for all of it's error reporting.
02:15Here in the main function, we display a couple of messages and we start a timer,
02:22and we do our database here. This is the count_recs method from the bwSQLite 3
02:29library. And then we mark the end time and display the elapsed time.
02:34And so that looks like this in the browser.
02:37Reload, it says 16 milliseconds.
02:40If I reload again, it says 4 milliseconds, because it gets cached, and that's how
02:45the sandbox works for the examples in this chapter.
02:49bwSQLite 3 provides a simple, object-oriented interface.
02:56It's designed specifically to make common CRUD operations easy.
02:59CRUD is create, read, update, and delete. These are the four most common
03:04functions of a relational database.
03:07The placeholder arguments for SQL are passed as variable argument lists, much
03:12like they would be in printf, and any C like language.
03:16The CRUD records are passed as associative arrays.
03:19These are PHP's equivalent of hash arrays or dictionaries in other languages.
03:23Rows are also returned as associative arrays in the CRUD interface.
03:28This provides a simple interface for the most basic operations. Anything
03:32more complex than that, there is a facility for passing SQL directly into the database.
03:37This interface does not support prepared statements or other
03:40optimization techniques.
03:41If you need that, I strongly recommend that you write a library specifically for
03:45the application that you're looking at, or write another general library that
03:50works the way that you would like for it to work.
03:53So the examples in this chapter are designed to give you a starting point for
03:56working on your own applications.
03:58They work well for that purpose, but they're not designed to teach you the
04:01specific techniques involved.
04:03For more details on any topic, see the corresponding lessons in the rest of
04:06this course.
Collapse this transcript
Creating and using a database
00:00In SQLite 3, creating a database is as simple as opening a file.
00:05Let's start by making a copy of create- start.php, and I'm going to hold down the
00:11Option key here on this Mac. And if you're on a PC, you can drag it and hold
00:15down the Control key to make a copy. And then I'm going to take that copy, and
00:19I'm going to rename it;
00:20I'm just going to call it create.php, and we'll open that in our text editor.
00:27You'll notice that this version of the sandbox is a little bit different, in
00:31that there's nothing here in the init, because we're going to do that
00:34initialization in the main.
00:36We're going to go ahead and create the database right there.
00:39I'm going to start by building the try and catch block, which is used for
00:44exception catching in PHP.
00:47So that looks like this, and the catch,
00:53we'll catch PDOException, and put it in a $e variable. And then inside that catch,
01:04we'll handle the error simply by calling our error function, with $e->getMessage,
01:13like that, and that's our exception handling.
01:16Now, the bwSQLite 3 Library uses PDO, and so it goes ahead and uses the
01:21PDOException class for its exceptions, because that's really convenient, and works well.
01:27We'll start with the message, and we'll say, creating the db object, and we'll
01:34go ahead and do that. db equals new bwSQLite 3 and DB_FILENAME, TABLE_NAME,
01:47and you'll notice that these constants are defined up here at the top,
01:52DB_FILENAME and TABLE_NAME.
01:55We're using this special :memory:
01:58file name for the database file name.
02:01That's special in SQLite for the in-memory database.
02:05And so this will be using an in-memory database.
02:08Once we've got it all up and running, we'll create an actual database file, and
02:12you'll see how that works and then how that affects the performance at all, but
02:16for now, there we have it.
02:17We have created the database object, and we'll go ahead and initialize a variable.
02:23We're going to call it tn for the table name, and that's because we can use
02:27that inside of a string;
02:30you cannot use a constant inside of a string. And I'll show you an example of
02:34what I mean there in a moment.
02:36So, we'll send out another message, and we'll say 'creating the table,' and we'll
02:43go ahead and do that. And we're using the sql_do method in the bwSQLite, and
02:50BBEdit's completion doesn't know about that, and that's okay. And we'll
02:54just say 'drop table if exists $tn,' and then we'll go ahead and create the
03:03table, 'create table $tn.'
03:14First column is called id., and this is a special column in SQLite. If you say
03:19integer primary key, like that, it's a special type in SQLite that actually
03:29doesn't take up any space in the table,
03:30because SQLite is already doing this;
03:33it already is assigning a row id to every row in the table, and this just
03:37makes it accessible.
03:38And so it doesn't cost anything. It's very useful.
03:41We're not actually going to use it in this table, but it's something that I typically
03:44throw in almost by default, because it just doesn't cost me thing to have it,
03:48and it's often very useful.
03:51Then we'll call our first column 'animal,' and it'll be a text column, and
03:55then we'll have a sound, and it will also be a text column, and that is our table definition.
04:04Now, I'll go ahead and insert some records, and these also use sql_do, and we
04:18need to use the double quotes here because we're doing variable expansion inside
04:22of the string, 'insert into $tn,' 'animal,' and 'sound values,' and we'll just put in a
04:34couple of question marks for the values. And we use SQLite's facility for value
04:39substitution, and we'll put in the strings over here, 'cat,' and the sound that a
04:45cat makes is 'purr.'
04:47Now, you'll notice that we're able to use this value substitution with the
04:51question marks for these values, but we're not able to use it for the table
04:56name, and this is true across the board for every database that I've ever used.
05:00A table name is not substitutable, but the values are.
05:05And so we can use the table name this way.
05:07You just need to be careful that this variable doesn't come from anywhere
05:10outside, that it's defined right here locally.
05:14In this case, we've used a constant, and we've put that directly into a variable.
05:18So, we know that this is not something that's going to be subject to attack from
05:22the outside for SQL injection errors, and things like that.
05:26In the case of these variables here, we want to do it this way.
05:31We don't want to just put them in with a dollar variable like that, because
05:37that's prone to a lot of error, even if we have code that does escaping and goes
05:42through a lot of trouble to try and clean those variables up.
05:46This method is much more reliable, the code in SQLite, and the code in PDO
05:52is already there for doing the escaping and for making these into safe
05:56variables inside the SQL.
05:58So, it's much safer to do it this way where we can, and we're just not allowed
06:04to for the table name, and there's a lot of reasons why that's always true.
06:07I'm going to go ahead and copy this and insert a couple more.
06:12We'll do 'dog' and 'duck' and 'bear,' and we'll put in their sounds. Dog goes 'woof.'
06:25Let's go ahead and capitalize this as well, and a duck says 'quack' and a bear says 'grrr.'
06:38All right, and now we have created our database, we've created our table, we've
06:43inserted some records, and we'll go ahead and send one more message. And we'll
06:51say 'there are %d rows in the table and $db->count_recs,'
07:01The message function here - and we can look at it down here at the end -
07:04it uses this vsprintf so that you can actually use it just like you would
07:11with the printf. You can say %d here, and you can put in a variable out here
07:16that gives you a number.
07:17If you say %s, you can use a string, and that's just like printf in any language
07:22that works like that.
07:23Now, you'll notice that all we did to actually create the database is to
07:28instantiate the object. And so this opens the database. And just like if you're
07:33opening a file with fopen, if the file does not exist, the file gets created,
07:38and that's really all there is to creating a database.
07:41In SQLite a database is a file, and the creators of SQLite have often said that
07:48SQLite is really more of a replacement for fopen than anything else.
07:52Now, in my opinion, it's a whole lot more powerful than that, and I would use it
07:56in a lot of the places where people are using MySQL for small to medium-size web
08:00sites that don't really require database replication in a separate server and
08:06client for the database engine.
08:07SQLite is a great substitute for that, but it works very much like fopen.
08:12You open a file, and it creates the file if the file isn't already there.
08:17So, let's go ahead and save this, and we'll open up the browser and go to
08:25exercise files and Chapter01, and this is create.php, and there it is.
08:31The file name is :memory:
08:34and that creates an in-memory database, and it creates the db object, it creates
08:38the table and then counts the rows.
08:39So, we can see there are four rows in the table, and they are in our code;
08:43we inserted these four rows:
08:44cat, dog, duck and bear.
08:46Now, if we wanted to create an actual file and not just an in-memory database,
08:51up here where it says memory, we would put the actual file name.
08:56In this case, on this computer, I've created the SQLite 3 data directory in my
09:02home directory, which on a Mac is in the Users folder. It's called bweinman,
09:07and it's sqlite3_data, and then I'll give it a file name. And I'll call this
09:14chapter01.sqlite, and I will save that. And we'll go ahead and reload here, and
09:25we can see now that that's the name of the file, and it took 6 milliseconds this
09:31time, instead of the fraction of a millisecond that it took before with the
09:34in-memory database.
09:36So now, if we look in the SQLite 3_ data directory, we'll see that here's the
09:41file that we just created. And in fact, if I go ahead and delete this, pressing
09:46Command+Delete on my keyboard, and then I run this script again,
09:50you see there is the file; it came back.
09:53So, creating the database in SQLite is as simple as opening a file.
10:00Be sure your file and directory permissions allow your application to create and
10:05write to files in that directory.
10:07That's really important.
10:08You must, of course, have permissions for the file itself, but you also need
10:12permissions for the directory.
10:13If you don't have permissions for the directory, SQLite can't create and manage
10:18its old temporary files that it occasionally uses for the various things that it does.
10:22So that's all there is to creating a database in SQLite.
10:25You just open the database and be creative if it doesn't exist,
10:29create your tables using SQL, and you're off and running it.
Collapse this transcript
Inserting data into a table
00:00Inserting data into a table in SQLite is accomplished with SQL, using
00:04the insert statement.
00:05So we'll go ahead and look an example of how this is done.
00:08I'll start by making a working copy of start.php.
00:11I'm going to name this one insert.php, and open that in my editor.
00:16You'll notice that we've moved the code from the create.php down into the init
00:21function here, so that when we get to the main function, the database is
00:25already initialized, the table has been created, and four rows have been inserted into it.
00:30I'm going to start by putting our cursor after the database operation 'go here,'
00:35and we're going to create our exception handling block with try and catch.
00:51And that's because all errors are handled with exceptions with the PDOException
00:55class in bwSQLite 3.
00:59And so, we'll handle that error with the error function, like that, with the
01:07getMessage method. I'll go ahead and start inserting data into the table.
01:12Use the db object, which you'll notice here, comes from this global $G, and
01:18it's copied into the global $G down here, in the init function.
01:23So that's already initialized for us, and that is an object of the bwSQLite 3
01:29class, and we'll use the sql_do method, which we'll use quite a bit.
01:36Go ahead and put some SQL in here, and 'INSERT INTO $tn' and tn is the TABLE_NAME;
01:44you see that that's initialized up here on line 26. And the SQL for this
01:49goes like this, we say animal and sound, specifying the columns to insert values into.
01:58We can now say VALUES, and then we're just going to use the place markers
02:02question marks, and we'll put in the values after here.
02:06I want to do this on the next line and say that this is horse, animal, and
02:11the sound that a horse makes, of course, is sings a song, 'a horse is a horse,
02:18of course, of course.'
02:22That's the sound that a horse makes.
02:24Now, you notice that we have inserted the row, and this table was created with
02:29this integer primary key column.
02:32This is a special column in SQLite.
02:34It works like auto-increment does in some other database engines, and it's very
02:39convenient, and it's very easy to use, and it doesn't actually even take up any
02:43space in the table, because SQLite is already internally keeping a row id for
02:48every row in the table. And it just assigns it when you use a special syntax
02:52integer primary key.
02:54So, I'd like to get a copy of that value for the row that I've just
02:57inserted, and so I'll use the last insert row id function from SQLite, and
03:04then that'll work like this.
03:05So I'll say, horse_id equals sql_query_value.
03:12This is a special method in the bwSQLite 3 that returns a single value from a
03:17query, so you want to give it a query that will naturally return a single value.
03:22And I want to give it this one here.
03:25I'm going to say SELECT LAST_INSERT_ ROWID, like that, and we'll say 'message( 'Added
03:36id number %d, and that'll be horse_id like that.
03:44Now, we'll go ahead, and we'll save this, and we'll run it in the browser, and
03:54you see we added id number 5.
03:56There were 4 rows in the table before, and now we added id number 5. And if we
04:01go ahead and say, message ( 'There are now %d rows in the table, like that, and
04:17switch back to the browser and reload, see, there are now 5 rows in the table.
04:24Now, that's one way to do it.
04:26There is actually a simpler way to do it, using the CRUD methods in the bwSQLite
04:303 Library, and this is one of the values of this Library.
04:35So, I'm going to go ahead, before this how many rows in the table, because we're
04:38going to add another one, and
04:40I'm going to say db->insert, and this will return the new row id, so I'm going
04:47to call this one bird_id.
04:49What we give it is an associative array.
04:54So, I'm going to create an array in place here, and this will have animal, and
05:01this is the syntax for creating an associative array, and sound, like that.
05:11That will create a new record, and that's all that there is to it. And I can go
05:16ahead, and I can just copy this line down here a little bit, and change horse_id
05:22to bird_id. And we'll see that we did here in one line of code what it took two
05:27lines of code to do there.
05:28I had two SQL queries.
05:30So go ahead and save that and reload it in the browser, and we see Added id
05:36number 5 and id number 6, and there are now 6 rows in the table.
05:41And so let's go ahead and print them out.
05:44We can print this out with a select, and we can say, row equals
05:48db->sql_query_row, so that'll get us a whole row. And I can say SELECT *
05:57FROM $tn, which is the table name, WHERE id = ?, and I can give
06:05this one the horse_id, and then I can print it.
06:15I can say id is %d, and The %s says %s, and so we'll give it row sub id, row sub
06:30animal and row sub sound.
06:37I'll go ahead and save that and run it, and I have a little syntax error here.
06:43That's because I put in this dollar sign before message;
06:46that doesn't belong.
06:47Go ahead and run it, and there we have id: 5:
06:51The horse says, A horse is a horse, of course, of course,...
06:54There is of course another way to do this using the CRUD methods, and that
06:57would go like this:
06:58row = db - it's very simple - get_rec and bird_id.
07:06And so that will allow the Library to create the SQL, and we'll see that in
07:11another lesson, how that works. And then we can just print it out, save this, and
07:18go to the browser and reload, and The bird says Tweet.
07:22So, we can see that inserts are accomplished in SQLite using the insert
07:28statement in SQL. And using a library like bwSQLite 3 or library that you create
07:34yourself, it's possible to do it without writing the SQL every time for
07:40operations that you're just going to end up doing very, very often.
07:44So, we can use something like insert with the appropriate data structure,
07:50instead of this SQL, and likewise we can use the get_rec, instead of the SELECT statement.
08:00So that's how data is inserted into a table using SQLite 3, and that's also how
08:06you can do it using a CRUD Library like bwSQLit3, or use that as a starting
08:12point in creating your own general or application-specific libraries.
Collapse this transcript
Getting data from a table
00:00Retrieving data from a table is accomplished with the SQL select statement.
00:04Using PHP's various interfaces, you may retrieve rows into arrays, indexed by
00:09numbers or associative arrays, indexed by column names, or even single values.
00:14We'll start by making a working copy of start.php, and I'll name this 'retrieve.php.'
00:23I will open that in the editor, and then go down here into the main function,
00:29where it says database operations go here, and we'll start by making a try catch
00:34block for handling exceptions.
00:37
00:54Now we'll put in our select statement for retrieving a row, and we'll create a
00:59row variable to handle the associative array.
01:02
01:20So there is my select statement, and this is the placeholder for the variable,
01:24and there is the value that will go into the placeholder. And so now I
01:29have the row, I'm going to go ahead and print the row.
01:32
01:53So that's it. We have the select statement, and we have this message that prints it out.
01:59So we will go ahead and save that and load it up in the browser. And here we have
02:05the row, row number three the duck says quack, and you will notice that we
02:09selected where animal equals duck, so we got that row, and we used this
02:14associative array with the column names as the keys.
02:20So that's how you retrieve a row of data from the table using SQL directly.
02:25You can also get one particular value from a select statement, and that
02:30would look like this.
02:32
02:58So if I just want to see the sound that a dog makes, I can say select sound
03:03from the table where animal equals to dog, and I can print it directly because
03:08that will turn a string, so I can print it directly with this %s in the message
03:15functions. So I'm going to save that and run it.
03:18It says the value result is woof,
03:21so that's the sound that the dog makes.
03:24Using the crud interface, it's very simple to print out all the rows, and I can
03:29use PHP's 'for each' to iterate over a table and say db-get-recs with an s at the
03:38end for plural and say as row and message and just take all this here, copy it
03:51over there, and save that, and reload in the browser, and I get all four of those rows:
03:59the cat says purr, the dog says woof, et cetera.
04:02So the way that this works is this get_recs actually returns an object that is
04:08an iterator and allows you to use 'for each' and so for each row this is really
04:13just all from that one line, and you can actually step through the entire
04:17table and operate on it.
04:19So retrieving data from the table is accomplished using the SQL select
04:22statement. Using interfaces provided by PHP, you may retrieve rows in several
04:27convenient formats, or feel free to use the bwSQLite 3 libraries, as a
04:32starting point in creating their own general, or application-specific libraries.
Collapse this transcript
Updating data in a table
00:00Updating a table in SQLite 3 is as simple as using the SQL update statement.
00:06We'll start by making a copy of the start.php file, and we'll rename that to
00:12update.php. We'll open that in the editor. And scrolling down here to the main function,
00:19where it says database operations go here,
00:22we can start by creating our exception handling with the try catch block, and
00:38we'll handle that error with the error function, and then we will
00:46input our SQL in here.
00:50We'll start by getting a row, and we'll select a row using the SQL select statement.
01:02We'll use SQL query row from the BWS SQLite library.
01:06We'll select for particular animal, and this will be the animal 'dog,' and
01:19we will go ahead and print it out using the message function.
01:23
01:41Now we have a row, and we can get its ID like this.
01:49We can use that ID for updating it.
01:51So we'll message that we are going to update it, and then we'll use some SQL for updating it.
02:07Use the SQL update command.
02:18So I've set update in this table and set sound equals particular sound where ID
02:24equals, and we'll use the ID that we got in the lines before.
02:28So the sound would be 'bow wow' for the dog, so we can change it something else
02:32that the dog may say, and we use ID for the ID' and so that we'll replace this
02:39first question mark with the bow wow, and the second question mark with the ID.
02:44Then we'll go ahead and will print the row again.
02:49Now that we have the ID, we can use the CRUD get-rec method, and we can go ahead,
02:58and I'll just copy and paste this message statement here.
03:01I will save that, and we will go ahead and run it.
03:07So this is update.php, and here we have these four rows in the table and
03:12originally it says the dog says woof, and we've updated it. Now it says the
03:16dog says bow wow, and so that update was accomplished with this SQL right here for the update.
03:22Now, of course, we can also update it with a CRUD method, and we can say
03:27message updating id %d with CRUD, and then we'll go ahead and use db update and we'll
03:41give it the with ID, and we've given it an associative array with just the part
03:46that we want to update.
03:47So sound 'Ruff,' like that, and then we'll go ahead and get the record again and
03:58print it again with those two lines copied from up there, and so now we're
04:02updating with a new sound that the dog might say 'Ruff.' And we'll save that and reload
04:07over here, and we see now updating with CRUD, and we get Ruff from the table.
04:13Updating data in a table use as simple as using the SQL update statement.
04:18You can make the process even easier by creating corresponding CRUD methods in the library;
04:23As usual, feel free to use bwSQLite 3 library as a starting point for your
04:29own custom libraries.
Collapse this transcript
Deleting data from a table
00:00Deleting rows in a table is accomplished with the SQL delete statement.
00:05So let's make a working copy of start.php, and we'll rename this to delete.php.
00:13Open that in the editor, and come down here to the main function, where it says
00:19database operations go here, and we'll start with our exception handling block,
00:25using try and catch, and error function for the error message.
00:45And let's start by printing out all the records that we have, so we can see
00:49where we're starting as we go onto delete things.
01:03We'll print them out with that message function, and the row id, and animal and sound.
01:30And now we'll go ahead and delete something.
01:34So we're going to delete the record with id of 3, and we'll say message, deleting, like that.
01:46Now I'll do the delete.
01:48We'll do this using sql_do.
01:58I'm going to pass it the id, and then after, we'll have so many records in the
02:10database, and we'll go ahead and print them all out again, so we can see that we
02:23deleted the correct one.
02:25Now, let's go ahead and save that and run it.
02:32And here we have, there's four rows in a table, and there they are.
02:35And we're going to delete id 3, which is this one here that is The duck, and
02:40after delete, there are three records in the table.
02:42That's correct, and there is no duck.
02:45Id number 3 is gone.
02:47So that's how you delete a record using SQL in SQLite.
02:51You see here is our SQL, DELETE FROM $tn, WHERE id = id.
02:58Now, of course using CRUD, this can be quite a bit simpler.
03:03We'll set the id to 2, and we'll give it another message.
03:09Actually, I have this already up here;
03:10I can cut and paste.
03:11I'm a big fan of cut and paste and db->delete($id).
03:19It's just like that, except I need to spell it right.
03:24And then we can just copy and paste all of this and say, after delete there is
03:28that many records and print it out again.
03:31So we'll save this, and we'll come over here to the browser and reload.
03:36Now, let's scroll down.
03:39After we had these three left, now we're deleting id 2.
03:42After the delete, there is two records left, and id 2 was 'The dog says Woof,' and
03:47that's the one that's missing.
03:49And so there we have it.
03:50It's really very simple.
03:52Deleting rows in a table is accomplished with the SQL delete statement, and this
03:58is a process that easily lends itself to using CRUD, and you can see how simple
04:03that is with the CRUD Library.
04:05And again, I do recommend that you create your own CRUD Library and feel free to
04:09use mine as a starting point.
Collapse this transcript
Creating a database library
00:00A custom CRUD library can be a very powerful tool.
00:03It can be helpful to have a generalized library for creating small applications,
00:08and that can be used as a starting point for more specific libraries, targeted at
00:13individual applications.
00:15bwSQLite 3.php is included with the exercise files for this course.
00:22This is the library that I use for SQLite work in my own PHP work.
00:27test-bwSQLite 3 is a unit testing script that I use for testing the library as I
00:35work on the library itself.
00:37So let's take a look at the library.
00:39We're just going to take a tour through it here, a very quick tour.
00:42I'll give you an idea of what it is, how it works, and I'd like you to be thinking
00:47about how you would do it differently, how you would create something like this
00:51that works in your own way, and in your own style of programming.
00:55So this is object-oriented PHP, this is a class definition, and the constructor
01:01takes two arguments:
01:02a file name and a table_name. And if you don't pass it those, then it will
01:07default to the in-memory database, and it will default to having no table_name at all.
01:12You can use the setter/getters for table_name and a PDO handle, if you want to
01:18initialize the database that way outside of the constructor for this particular class.
01:24sql_do is a very common function that I use a lot for passing SQL that's
01:30not select-oriented.
01:32That doesn't return a result set.
01:34sql_do_multiple simply passes to the DBO exec method, and that's useful for SQL
01:42scripts when there's more than one statement of SQL.
01:49sql_query returns a statement handle, and a statement handle, in PDO, is actually
01:55an iterable object, so it allows you to iterate through the results.
02:01It also sets up a query for the get_next method.
02:05The get_next method uses a saved statement handle to do pretty much the same
02:09thing, and so you'll notice that it initializes the statement handle that is
02:15part of the object itself.
02:16It's one of the private variables up here.
02:23sql_query_all does a fetchAll, which is a very dangerous thing.
02:27You only want to do this if you absolutely positively know that you have a small
02:31set of results that will fit in memory without causing you problems.
02:35I strongly recommend against using this, if you can instead use one of
02:40the iterable methods.
02:41sql_query_row returns a row, sql_query_ value returns a value, and all of these
02:47SQL methods take an SQL statement as their first argument.
02:52These are the transaction functions, and these just translate directly to PDO functions.
02:59Now we get into the CRUD.
03:01These all take either an id, as in this case, which makes it very easy to
03:05create the SQL, or some of them will actually take an associative array, as in
03:11this insert takes a rec, and then it goes, and it creates the SQL, it analyzes
03:18the associative array, finds out what the column names are from the keys of the
03:23associative array, and then it goes ahead, and it builds the SQL and runs the query.
03:29So insert and update both do that, and you can see that they're a little
03:34bit more complicated.
03:35So feel free to crib from this code because this code works, and it's pretty
03:39clean, and it has been refined over a period of time, and I know that it works well.
03:45get_next is a pseudo-iterator.
03:48If for some reason you prefer to not use the for_each and you would rather use
03:52a while, or if your code lends itself well to that, get_next works for that purpose.
03:59And then count_recs just does a Select Count.
04:02Table_exists uses the SQLite special table sqlite_master, to find out if a table exists.
04:11And then we have version for getting the version number of the SQLite and timer
04:15functions for timing your queries and refining them.
04:19So it's a very small and simple library, as a generalized library should be.
04:25You know, a lot of times you'll see libraries, like PDO, that's very huge, and
04:30has a lot of functions and a lot of code that may apply to somebody else's style
04:35of coding, may apply to some other application, may apply to something larger.
04:39But for most purposes, where you're simply writing a small application,
04:44something like this is ideal. It's small.
04:46If it's easily in memory, it's easy to use, it's easy to remember what's in
04:50there, and it's easy to look through it to find the things that you might have
04:53forgotten that you had.
04:55I strongly suggest that you create your own generalized library for
04:58accessing your database.
05:00Set up the interface so that it works the way that you like it, the way that you
05:03work. Refine it, polish it, and then use it as a starting point for more
05:07specific libraries for individual applications.
05:10This strategy will serve you well as you create applications using PHP
05:15and SQLite 3.
Collapse this transcript
2. Getting Started
Installing XAMPP on a Mac
00:00XAMPP, which is how I pronounce X-A-M-P-P, is a cross-platform version of the LAMP
00:05stack that includes the Apache web server, PHP, SQLite, and a number of
00:10other useful tools.
00:11You do not need to install XAMPP to do the exercises in this course.
00:16You do need a web server with PHP 5 and SQLite 3, and XAMPP is a convenient
00:21option to provide that on your desktop, but any server with PHP 5.3 and SQLite
00:253.6 or later will do.
00:29This is the www.apachefriends.org web site, where you can download XAMPP for Mac
00:34OS X. And if we scroll down here to the Download section of the page, you'll
00:39notice that the current version as of recording is 1.7.3.
00:45The thing about this is that the good folks at apachefriends.org update XAMPP
00:51frequently, which is a wonderful thing.
00:53They keep it up to date.
00:54They're using the latest versions of stuff.
00:56The down side of that is that if you want to follow along with the
00:59installation instructions in this movie, that's likely to be different with a
01:03later version of XAMPP.
01:05So if you want to follow along, you want to have XAMPP for Mac OS X Version
01:101.7.3, and the way to get that is on my web site. Because I've run into this before,
01:16I'm providing a download for this exact version so that you can follow along
01:22with these installation instructions, and it'll work exactly the same for you,
01:26and this is the version that I'm using throughout this course as well.
01:30So you can find this download on my web site at sqlite.bw.org. That's bw, as
01:36in Bill Weinman, .org.
01:38Now I've already downloaded and put on my desktop this version of XAMPP, and
01:43we're going to go ahead and install it.
01:45But before we do, I'll come up here to the Apple menu, and I'm going to
01:49open System Preferences.
01:50Now this is an important step that you must not skip.
01:53I'm going to open the Sharing, and I'm going to check this check box right here,
01:58web sharing, and you'll notice that it's not checked, and that's the way that it should be.
02:02What that does is it starts a version of the Apache web server on your Mac.
02:07It's the version that came with your Mac, and there's nothing wrong with that version.
02:12It's just that I don't know what version it is.
02:14I don't know what version of PHP there is.
02:16I don't know if that version of PHP has the required libraries compiled in.
02:21So if you want to use that, there may be some work for you to do.
02:25But you cannot use that and XAMPP at the same time, because they fight for the
02:29same resources and one or the other of them it's not going to work, and it's
02:32probably going to be the one that you don't want.
02:34So if you're going to install XAMPP, you need to make sure the Web Sharing is
02:37turned off, so that there's not another web server running on your Mac.
02:41So, I'm going to go ahead in quit System Preferences, and I'm going to open up
02:45this DMG file. And that mounts a virtual drive on my desktop, and I simply
02:51drag the XAMPP folder into the Applications folder, and it copies it over.
02:57And this can take a while.
03:00When this is done, I'll hold down the Ctrl key and click on an open spot in this
03:05folder here and press Eject.
03:07That will unmount that virtual drive from my desktop.
03:09Now, I can file this away some place, so that it's safe, should I need it again.
03:14Let me come down here and click on Finder and go to Applications and scroll
03:19all the way down to the end, which is where I'm likely to find something that begins with an X.
03:24I'm going to drag this XAMPP control app to my dock, and I'm going to click on that.
03:33What this does is this is the controls for starting the servers that come with XAMPP.
03:36I'm not going to start MySQL, and I'm not going to start FTP, because I don't
03:40need those for this course.
03:42I'm just going to start Apache, and I'll need to type in my password.
03:46Now, your account must be an administrator account in order to run a server on your Mac.
03:51I am going to wait for that to say that it started.
03:56I've got a green light. That means it started, and that it's running.
03:59So I can completely quit this Control app, and the server is now running, and
04:05it will remain running.
04:06When I reboot my Mac, it will not start up again.
04:09Anytime I want to start it or stop it, I just bring this up, and I can start it and stop it.
04:14But for now, let's close this, and I'm going to open my web browser, and I'm
04:20going to type in "localhost," and I should get this page here.
04:24That means that the Apache server is now running and working.
04:28If it weren't, then I can just go ahead and stop it here and see you'll see what
04:32happens if it's not running,
04:36reload, and I'll get this page.
04:38If you get this page that the Apache server is not running, and I'm just
04:42going to start that up again and close the XAMPP Control panel and click Try
04:50Again, and there it is.
04:51Now, I'm going to click on English.
04:53You're welcome to click on whatever language you like, but English works for me.
04:57You'll notice that I get the version of XAMPP -
04:59it's like this little XAMPP welcome page and a lot of things that I can test.
05:03Now some of these things won't work, because we haven't started all the services
05:06here, but phpinfo is the one that we care about.
05:09phpinfo will tell us what version of PHP is running, and that's compiled in
05:14with the Apache server here, and it will also allow us to search for the string 'sqlite.'
05:19So I'm going to press Ctrl+F here, and I'm going to type 'sqlite,' and you'll see
05:26that I found it there, and I'm pressing Command+G to search over and over again,
05:30until I get down to the PDO section.
05:33You'll notice that sqlite and sqlite2 are in the PDO drivers.
05:37The one that's just sqlite by itself, without a 2 after it, that's actually
05:40SQLite 3, and that's the one that we care about.
05:43I am going to search again to find the version of SQLite 3, and here we have the
05:48version number, and it's 3.6.12. And that's all we care about.
05:52So these are acceptable versions, and we have now successfully installed
05:56XAMPP for Mac OS X.
Collapse this transcript
Installing XAMPP on a PC
00:00In this movie, we're going to install the XAMPP package on a Windows operating system.
00:05Now it is not necessary to install XAMPP in order to follow the examples in this
00:10course, but the examples in this course are using XAMPP, and it is convenient.
00:15So if you'd like to install XAMPP on your local machine, I'll be showing you
00:19how to do that here.
00:20So this is the XAMPP web site, where you would download XAMPP, and down here is
00:25the XAMPP downloader. You'll see the current version is 1.7.3.
00:29One of the nice things about XAMPP is that it's well maintained, which means
00:34that by the time you look at this page, it may very well have a different version.,
00:38and unfortunately the installation procedure for that different version may be
00:42different than what I'm going to show you here.
00:44So if you really want to follow along, you want to be installing 1.7.3.
00:48If you want to install a later version, you're welcome to do that, and I'm
00:52sure it'll work, and the installation procedure might be different than what I'm showing here.
00:57So this movie might not be as helpful for you.
01:00If the latest version is different than 1.7.3, and you'd like to follow along,
01:04you can come to my web site, right here at www.sqlite.bw.org.
01:09Down around the middle of the page here, you'll see you can download version
01:131.7.3, so that you can follow along and run exactly the same environment as I'm
01:18running in the movies.
01:20So here on the desktop, I have XAMPP for Windows version 1.7.3.
01:27So I'm going to double-click on this installer, and it says Destination
01:32folder, the root of drive C, and that's exactly where I'm going to install it,
01:36just because that's what they're suggesting. And I'm assuming that their
01:39package is set up for that.
01:41While it's not the preferred place where I would really like to install it, it's
01:46fine, and it doesn't really bother me.
01:47So I'm going to click Install, and this process does take a while.
01:51So go get a cup of coffee or whatever is that you like.
01:57So at this point, I tend to just take the defaults as much possible,
02:00so I'm going to say yes to the shortcuts and yes to shall I proceed, because I
02:07do actually want to do the installation.
02:09I'm going to say no to this one, because I actually know what it means, and
02:12that default is correct, unless you're doing something very different than what
02:16our purposes are here.
02:19Now it says XAMPP is ready to use.
02:21I'll press Enter to continue, and it's setting the time zone, and I'll press Return.
02:26Again, if I want to change those things, I can.
02:29At this point I'm going to press the X for exit, to exit the installer.
02:33Now with the XAMPP Control panel on the desktop, and I'm just going to
02:37double-click on it here, because this is the control panel for starting XAMPP.
02:41Before we actually start any of the services, we're going to click on this button
02:45here that says Port-Check.
02:46And this is very important.
02:48What this does is it checks to make sure that the ports are open on our computer
02:52for running the servers in the XAMPP package.
02:55And in particular, this one here -
02:58this is really the only one we're going to be using for our purposes, Apache
03:01port 80, and it says Free, and that's what it should say.
03:04If it says something else here, it will tell you what program is using that
03:08port, and you'll need to get that program to stop using that port in order to use
03:12the Apache web server.
03:14The two most common things that you'll see here are Skype and another web server,
03:20like Microsoft IIS, or another installation of Apache.
03:24So that's what you want to look for here.
03:25If it says Free, then you're fine.
03:27I am gong to press Return to continue, and it goes away.
03:30Now you can start Apache.
03:32It says Apache started, and we have Running here, and we don't need any of these
03:36other things for our purposes,
03:38so I'll go ahead, and I'll close this, and I'm going to start up my web browser
03:43and type in "localhost."
03:46There I have the XAMPP server running.
03:48I'm going to click on English, and I'm going to confirm that PHP is running, so
03:52I'm going to click on this thing that says phpinfo.
03:54Again, none of these other things matter to me.
03:56I'm just going to click on phpinfo.
03:59Here we are, PHP Version 5.3.1, which is just fine.
04:03I'm going to press Ctrl+F for Find and type in "sqlite."
04:09There, under PDO, I see that we have sqlite, which is fine.
04:14Sqlite2 is an older version.
04:16This is actually sqlite3,
04:17the one that says sqlite there.
04:20Scrolling down a little bit, it says SQLite Library version 3.6.20, and that is excellent.
04:27Anything over 3.6 is just fine for our purposes.
04:30So XAMPP is running, and it's installed, and it's working.
04:35The Apache server is running, and PHP is running, and all our versions are correct.
04:40So we have successfully installed XAMPP on this Windows box.
Collapse this transcript
Setting up SID and exercise files on a Mac
00:00In this movie, we're going to set up the exercise files in the XAMPP
00:03development environment.
00:05Now, you don't need to be using XAMPP in order to follow along with the
00:08exercises in this course, but if you're not using XAMPP, these instructions will
00:13be different for you.
00:14So follow along and use the same principles, but you're going to need to
00:18translate whatever needs to happen in your environment from my instructions for
00:22the XAMPP environment.
00:23So first thing we need to do is we need to make sure that XAMPP is running.
00:27So I'm going to start the XAMPP Control Panel, and if we don't have a green
00:31light here already on Apache, I'm going to press Start for Apache and type in my password.
00:35Now, Apache needs to be running.
00:38MySQL and FTP do not need to be running.
00:41I'm just going to bring up my browser and make sure that that is indeed
00:46running, and there it is.
00:49So I'll close my browser, and I'm going to open a new Finder window and navigate
00:56over to XAMPP down here, and I'm going to click on htdocs.
01:02This is a shortcut, pointing to the htdocs folder, which is actually under the
01:06xamppfiles folder, if you're looking for it. And then I'm going to copy my
01:10ExerciseFiles folder into the htdocs folder.
01:14Now, I've got it on my desktop here;
01:15you might have it in a different place, and that's okay.
01:18I'm going to hold down the Option key as I drag it, and that gives me a little
01:22Plus sign, and that means that I'm copying it instead of moving it. There we go.
01:28Now the ExerciseFiles folder is here in the htdocs.
01:32So I'm going to drag a shortcut to my ExerciseFiles folder over here into my
01:37Places on my sidebar.
01:38That just makes it convenient for me to be able to get back to it.
01:42The next thing I'm going to do is I'm going to click on my Home directory, and
01:45notice here, this is the short name for my Home directory.
01:49You're going to need that later on, so make a note of that.
01:52And then I'm going to create a new folder in here.
01:55I'm going to Ctrl+click on an empty space and select New Folder, and I'm going
02:00to name it sqlite3_data. And this is where I'm going to put the database files
02:07for the SQLite 3 examples in this course.
02:11Now, going back over to ExerciseFiles, we're going to find the files that go in
02:14there, and I'm going to click on the SQL folder inside my ExerciseFiles, and
02:19this is the ExerciseFiles, not the one on my Desktop;
02:22this is the one in the XAMPP folder.
02:24So if I Ctrl+click up here at the top, you'll see that it gives me this path,
02:28and it's coming from XAMPP, xamppfiles, htdocs, ExerciseFiles, SQL, and that's the one that I want.
02:34If this is pointing to your desktop, you've got the wrong shortcut in your Places.
02:38Now I'm going to select these three database files that say .db at the end:
02:42album.db - and I'm going to hold down the Command key while I do this so I can
02:46select all three of them, test.db, and world.db.
02:50And before I do anything else, I'm going to Ctrl+click on one of those and press Get Info.
02:57If you have the Ctrl key clicked down, that changes.
03:00You want to release the Ctrl key, so that you get the little Get Info in the pop-up menu.
03:04And you'll notice that all three of these say Read only for everyone in the
03:08Sharing & Permissions.
03:09If you don't have Sharing & Permissions open, you can just click on this little
03:12triangle here, and you'll get Sharing & Permissions open.
03:15And you want to change the one for everyone, the bottom one, to say Read &
03:19Write, and you want to do that for all three of these. And this will allow
03:23SQLite to actually read and write these files.
03:26That's really important, because SQLite runs as a driver inside of PHP, inside
03:32of your web server, and your web server is running as a user, not you.
03:36So it needs Read/Write Permissions for these files, and this is the way that you
03:41give it those permissions.
03:42So I'm going to close these; done with those.
03:45These three are still selected here.
03:47If they're not, then you can Command+ click all three of them. And I'm going to
03:51drag them, and I'm going to press down my Option key so that I'm copying and not
03:56moving over to my Home directory.
03:58And I'm going to hold on that, so it brings up the directory, and then I'm going
04:01to drop them in this sqlite3_ data directory, and there they are.
04:05Now I'm going to do the same thing here.
04:07I'm going to Ctrl+click on sqlite3_data, and I'm going to press Get Info, and
04:12I'm going to do the same thing.
04:13I'm going to give it Read & Write Permission for the web server, which is running
04:17in the everyone column, and that will allow SQLite 3 to create files in here and
04:24do whatever it needs to do to operate in here.
04:26And just again, I might put a shortcut to this in the Places, because it might
04:31come in handy later.
04:33Now our databases are in the right place, and they have the right permissions,
04:37and you'll notice when I bring up the Get Info on these, you can also get it by
04:40pressing Command+I on your keyboard, that it has got that Read/Write Permission,
04:45and that is correct.
04:47And you can actually confirm that for all three of these, and there they are:
04:51Read & Write, Read & Write, Read & Write in everyone;
04:54can't hurt to confirm that everything is working as planned.
04:57Now I'm going to go back to ExerciseFiles, and I'm going to edit the path in
05:02SID, and we're going to need to do this in a couple of places.
05:05So I'm going to Ctrl+click, and say Open With, and select my text editor. I'm using BBEdit;
05:12you can use whatever text editor works for you.
05:14And I'm going to come down here where it says path/to/sqlite3_data.
05:18It will be around Line 21. The token here is DBDIR.
05:22That's being defined as a constant in PHP.
05:24I'm going to select path/to, and I'm going to put in the path to that directory,
05:29which is under Users, and then my short name, which in my case is bweinman;
05:34in your case it will be something else.
05:35So I'm going to save this file.
05:37I'm pressing Command+S; do whatever works in your editor, and close it.
05:42And then I'm going to do the same in CRUD, the CRUD file, open it in your editor.
05:47I'm going to come down here to path/to and put in Users/bweinman, and save that.
05:55Now we're going to go and do something a little bit different in the Testimonials.
06:00The Testimonials apps use the data in their own directories, so we don't need to
06:03edit the app and put in the correct path.
06:06What we do need to do is we need to set the permissions on the data directory and
06:09on the database file.
06:10So I'm going to bring up the Get Info on the data directory and set the
06:16Privilege Permission for everyone to Read & Write, and I'm going to close that,
06:20and I'm going to do the same thing on the testimonials.db file, and bring up the
06:26Permissions and set it correctly, and there, that's done.
06:30Now, we've copied all the database files we needed.
06:32We've set up the PHP files to point to the correct directories.
06:36We've done everything we need to do to get all this stuff working; now let's
06:39just confirm that it's actually working.
06:41I'm going to close that Finder folder and open up my web browser.
06:45I'm going to come up here to my location bar, and I'm going to type in
06:49"localhost," and "ExerciseFiles," like that, and this will give us a directory
06:56to our ExerciseFiles.
06:58I'm just going to put that in my bookmark bar, so I can get back to it easily.
07:02And I'm going to come over here to SID and bring up sid.php, and there it is.
07:07And just to make sure that I'm accessing the database files correctly, and that
07:11I have Read & Write Permission, I'm going to come over here to the test.db, and
07:15I'm going to actually create a table, put some data in it, and read it back.
07:19So just type along with me.
07:20If you don't understand the SQL now, you will later.
07:23I'm going to create table t, and in parenthesis, put a, b, and that will create
07:31a table with two columns in it.
07:33And I'm going to insert into t values, parenthesis 1, 2 (1, 2) close
07:39parenthesis, and a semicolon at the end of each of these statements;
07:44all SQL statements must be terminated with a semicolon.
07:47And then I'm going to select star.
07:51That's an asterisk from t semicolon.
07:54We'll click on the Go button.
07:56And if everything is just perfect, you'll get a different elapsed time probably,
08:02but you'll see down here, this little table, a, b, 1, 2, and that is the
08:06result that we want.
08:08So that means that I'm able to open that test.db file,
08:11I'm able to write in that test.db file, I'm able to read in that test.db file,
08:17and that's exactly what we need to be able to do.
08:19So just to put the database back in its original state, I'm going to type 'drop
08:24table t;' and Go.
08:29Now that database is as it was originally.
08:32So I'm going to make a shortcut here in my Bookmark bar, so I can get back to
08:36SID very easily, because we're going to use that a lot.
08:38I'm going to go back to ExerciseFiles and open up CRUD.
08:42So we're going to bring up CRUD, and we see that that's working, and it's
08:46accessing its database okay.
08:48I'm going to drag a shortcut up here to the toolbar, and then we'll go back to
08:51the ExerciseFiles, and we'll bring up the Testimonials and the db.php, and
08:56that's running, so we know that that's working right.
08:59So we've now set up the ExerciseFiles and made sure all our permissions are
09:03correct, and we've tested it all, and we're ready to go and do the exercises in
09:08the rest of this course.
Collapse this transcript
Setting up SID and exercise files on a PC
00:00In this movie, we will set up the exercise files on a PC running XAMPP.
00:05Now, you don't need XAMPP in order to run the exercise files;
00:09you can run them on any server with PHP and SQLite of the proper versions.
00:14So, if you're running in a different environment and you want to follow along,
00:18just keep in mind the principles of what I'm doing and not the exact file
00:22locations, because those will be different in a different environment.
00:24So, I'm going to start out by making sure that XAMPP is running, and it looks like it is.
00:31Bring up the little Control panel over here, and Apache is running, and so
00:35that's all very good.
00:36Now, I'm going to find the XAMPP directory here.
00:41So, under Computer, on the C drive, I installed XAMPP in its default location,
00:46which is right in the root of the C drive, and inside of there, I'm going to go
00:49to htdocs directory, and I've got my exercise files on my desktop here.
00:54If you have them at someplace else, you want to get them from there, and I'm
00:57going to drag it and hold down the Control key on this PC keyboard and copy it
01:03right into the htdocs directory.
01:05Now, you want to make a copy of it;
01:07you don't want to move it, because that way you've got a working copy and should
01:12you want to start over again, you've got the original still there.
01:15Now, before we go any further, there is a setting on the PC that I want to change.
01:20I'm going to the Control panel.
01:22I'm going to explain to you what I'm doing.
01:24XAMPP runs very slowly in the default configuration here on Windows 7, and this
01:30may be true on other versions of Windows as well. And the reason is a cache
01:34setting in how the hard disks are set up.
01:36So, I'm going to go to Control panel, Hardware and Sound and right here under
01:41Devices and Printers, there is a Device Manager, and under Disk drives - now I've
01:46got two disk drives here,
01:47I'm going to set them both, but I'll go in the first one and Policies, and
01:51you'll see this right Write-caching policy.
01:54Now, Enable write caching is turned on by default, but this Turn Off write-cache
02:00buffer flushing on the device is actually unchecked by default, and you want to
02:04make sure that it's checked.
02:06In a nutshell, the explanation for this is that write-caching can be dangerous.
02:11Should you have a power loss on your hard drive while write-caching is in
02:16effect, you could lose data.
02:19Now, in order to mitigate that, they came up with this write-cache buffering, and
02:24the write-cache buffering actually pretty much defeats the purpose of the
02:28write-caching in the first place. And so it slows down database writes very much,
02:34at a difference of orders of magnitude.
02:36Things that take a few milliseconds end up taking hundreds or thousands of
02:40milliseconds with this write-cache buffering on. And just to further confuse
02:45things, the check box is checked when the write-cache buffer is turned off, and
02:51it's unchecked when the write- cache buffering is turned on.
02:54So if all of that sounds confusing to you, here's what you do.
02:57You get yourself an inexpensive battery backup device for your computer, plug
03:01your computer into that, so you know that you're not going to lose power
03:04unexpectedly and then you check this box. And that'll make you perform it
03:08sufficient for you to be able to use SQLite on your PC.
03:12So, I've got that checked here.
03:14I'm going to say OK.
03:15I'm going to go this other one and go under Policies, and I've got that checked
03:19there, and I'm going to say OK, and now I'm okay.
03:23So I'll close the Control panel, and now we will install the database.
03:27So before we go any further, I'm going to take this folder here, because I'm
03:31going to want to get back to it, and I'm going to put it into my Favorites.
03:33I'm going to go to my Home directory, and I'm going to create a folder inside my
03:39Home directory, say New > Folder.
03:41I'm going to call this sqlite3_data.
03:46Then I'm going to drag that into my Favorites, so that I can get back to it
03:51easily, and I'm going to go to my ExerciseFiles, and down here under SQL,
03:56double-click on that, and you'll see in here there is three files that say .db.
04:01These are the database files that we'd be using in our examples.
04:04So, I'm going to hold down the Control key on my PC keyboard and click on all
04:08three of those and scroll up here and drag these three files. And I'm going to
04:13hold down my Control key, so I'm making a copy, instead of moving them, so that I
04:17have a working copy and drag them into the sqlite3_data folder, and I'm going to
04:21click on that and make sure they are in there.
04:23Okay, now we've got our databases installed exactly where they're going to go,
04:27and we want to remember this path.
04:28So, if you click your mouse up here, it's just a little trick,
04:31you'll get the path, and you can press Ctrl+C on your PC keyboard and copy that
04:35into your copy buffer, and you're going to need that in a moment.
04:38So, we're going to now click on ExerciseFiles and scroll down here to the
04:42bottom and click on the SID folder. Double-click on that, and I'm going to open
04:47sid.php in my text editor and make sure you have a good text editor and not the
04:52default Notepad on a PC.
04:55The one text editor in the world that doesn't work with most program files is
04:59Notepad that comes with a PC.
05:01So Notepad++ here is free, and it's an excellent editor, and I recommend that
05:07one, but use whatever editor works for you - not a word processor and not Notepad
05:11that comes with the PC.
05:12So, I'm going to click on that, and here is my SID. And see down here where it
05:16says path/to/sqlite3_data? I'm just going to take all of that, press Ctrl+V, and
05:22there we have the path to the sqlite3_data.
05:24I'm going to save that and close it, and then we will go back up to
05:31ExerciseFiles, and in the CRUD directory, do the same thing with CRUD.
05:39And right here, path/to/sqlite3_data, Ctrl+V, and I want another backslash at
05:45the end of it, because it's got the path actually to a file there, and I'm going
05:50to save that and close that, and we're ready.
05:53Now, I'm going to open up the browser, and we'll test it real quick. Type in
05:59localhost/ExerciseFiles, like that, and there is my exercise files. And I'm just
06:08a big fan of bookmark, so I'm going to drag this into my Bookmark bar, so that I
06:12can get back to that easily.
06:13I'm going to come down here to SID and open up sid.php, and there is our SID.
06:20I'm going to put that in my Bookmark bar. Now I'm going to test it real quick.
06:24I'm going to bring up the test.db database, and I'm going to enter a few SQL
06:28commands here. And if you don't understand SQL, that's all right;
06:32you will by the time you're done with this course.
06:34I'm going to create a table, create table t, and I'm going to give it a couple
06:39of columns, a, b, close parentheses semicolon - the semicolon is required, and
06:47insert into t values 1 and 2, like that, with parentheses and a semicolon and then
06:55select star from t semicolon.
06:59So create table t (a, b);
07:04insert into t values (1, 2); select * from t;
07:13and press Go, and this is the result you're looking for, right down here.
07:17You have two columns a and b, with two values 1 and 2 in them.
07:21That means that we've successfully been able to open this database, create a
07:25table, insert into the table and read from the database, so that's all working.
07:30So, I'm going to take all this, and I'm going to delete that table, so that
07:33it's back in its original state, drop table t;, and that will drop the table.
07:42So, now we want to make sure CRUD works.
07:45Click on CRUD, click on crud.php.
07:49This is just a little CRUD demonstration.
07:51CRUD stands for Create Read Update Delete;
07:53those are the four basic functions of a database. And we'll want to put that
07:57down here as well in the Bookmark bar, and there we have it.
08:02SID is the one we'll be using the most throughout the course, and now you have
08:06successfully set up your exercise files to work with XAMPP on a PC.
Collapse this transcript
Using the command-line tools
00:00In this movie, I am going to show you how to use the SQLite 3 Command Line tool
00:03on both the Mac and the PC.
00:05First, we'll start with the Mac.
00:07So we are going to talk a little bit about using the SQLite 3 Command Line
00:10application on a Mac.
00:12We are going to open the Terminal application, because that's how we get to the
00:15command line on a Mac, and this will put us with a command line in my Home
00:20directory here, and so it will be your Home directory on your Mac.
00:23So I am going to try not to assume that you know very much about the command
00:27line. In UNIX, this is the Bash shell, which is a default on a Mac, and the
00:32commands here are very similar to what you might find in most UNIXes.
00:35In particular, this is based on BSD UNIX, and if you're familiar with UNIX at all, it
00:40should all be very, very familiar.
00:42So the first thing we are going to do is we are going to make a symbolic link,
00:45because getting to the exercise files in the XAMPP directory is a little bit
00:49circuitous on a Mac.
00:51So I'm going to type 'ln,' which is the command that will make a symbolic link, and
00:56that's a lowercase l and a lowercase n; UNIX command line is case-sensitive.
01:01I am going to say -s, which will make it a symbolic link, and then we are
01:05going to navigate to the applications folder, so I am going to type a slash and
01:10a capital A and maybe a couple of ps and hit the Tab key, and it will complete that for me,
01:15so I don't have to type it all out and perchance misspell it, and then a capital
01:20X and a capital A, and that's probably enough for XAMPP.
01:22I will hit the Tab again, and it will complete that.
01:25And then HT, and that's probably enough, hit Tab.
01:29You'll notice it doesn't put the slash in for me there because that itself is a symbolic link.
01:34So I am going to put in the slash for it, and then a capital E, lowercase x, and
01:39that should be enough for our exercise files.
01:41Now I am going to backspace over that last slash because I don't really want
01:46that in my symbolic link, and press the Spacebar and the period. And so you should
01:51have a command that looks exactly like this. And then press Enter, and that has
01:55now created a symbolic link.
01:57So if I bring up a listing of my directory, which is with ls - a, like that,
02:04you'll see that I have ExerciseFiles right here, and that is a symbolic link.
02:09So I am going to cd into that, and again, I can just type a couple of letters, and
02:14the Tab, and now ls-al, and I can see my entire ExerciseFiles folder right here.
02:23That makes it easy for me to get into my ExerciseFiles folder.
02:27So now I am going to navigate from here into the SQL folder, and we will look in
02:32there, and we see that we have these SQL files and these db files.
02:37The db files are already made database files, and the SQL files are actually the
02:42source SQL for creating those files.
02:45So one of the things we are going to do here is we are going to use the
02:48command line application to create one of these db files, just so we can see how that's done.
02:52So I am going to type 'sqlite 3,' like that, all lowercase.
02:57So this is the command line application that comes with SQLite 3 and is
03:01designed for managing these databases.
03:04So if I give it the name of a database, say like test.db,
03:08now I have that database open. And if I want to see what's in it, I can type
03:13select * from sqlite_master;
03:19all SQL statements are terminated with a semicolon, and there is the schema for this database.
03:26It's got a table named customer, and here is the create statement that made that
03:31table, and here is the one that created the item table, and here's one that
03:34created the sale table.
03:36So we have all that in there, and if I want to say select * from customer;,
03:42then I get the contents of that.
03:44At anytime, you can type help with a dot in front of it, .help.
03:48You have to scroll back to see the whole thing.
03:50We get all the dot commands that are available, plus you can just type any SQL
03:55directly into the command line application.
03:57So I am going to Exit and type .quit, to get out of this and show you one
04:02other way to use this.
04:04If I want to type sqlite3, like that, and give it the name of a database, I can
04:09then just put a command in quotes.
04:12I can say, select * from customer, and I get the listing right there,
04:19without having to go into the application and type it on the command line
04:22inside the application.
04:23You can also redirect commands from a file. So if I want to create a new file
04:28that's just like test.db, I can say sqlite3 newtest.db and use the redirect
04:35operator from the command line, which is the left angle bracket.
04:40If you press Shift on the Comma key, you will get that left angle bracket, at
04:44least on an American keyboard, and then type test-sqlite3.sql, and this is the
04:50source SQL for the test.db file.
04:54So if I do that, it has now created a new database file.
04:57If I look at the listing of the directory here, we have this new test.db.
05:04That's a new file created from the test-sqlite3 sql.
05:08So we can look inside that test-sqlite3. sql and use the less command, and we can
05:16see what's in there.
05:17I am scrolling with j. You can scroll with j and k in here and just press q to
05:24get out of it, and so you can see all the SQL in there that's used to create the test.db.
05:30So that's how you use the SQLite 3 command line application on a Mac.
05:34Now I am going to show you how to use the SQLite 3 command line utility on the PC.
05:41First, we are going to locate the utility, and so I am going to open my Computer
05:46and go in the C drive, and I have installed XAMPP in the default location, and
05:52so I am going to go in the apache directory.
05:55This is not intuitive where it is. And it's in the bin directory under
05:58the apache directory.
06:00Scroll down here, and there it is.
06:04So what I want is this path up here.
06:07So I am going to do my little trick here that works on Windows 7 -
06:10I believe it also works on Windows Vista, where I put my mouse in this little box
06:15up there, and it will give me that path, and I can just press Ctrl+C and copy
06:20it. And this is going to come in really handy here in a minute.
06:24Now I am going to right-click on the desktop and say New > Shortcut and type
06:30'cmd.exe,' and that will give me a shortcut to a command shell.
06:34When I press Enter, it will ask me for a name, and I am just going to say, SQL,
06:39because what this is going to be is it's going to be a command prompt in the
06:43folder that has the default SQL stuff in it.
06:47Press Enter for finish, and now we have that.
06:49But before you run it, I'm going to right-click on that shortcut and bring up Properties.
06:53I am going to do a couple of things first here.
06:55I am going to just move this out of the way, and I am going to get another
07:00Explorer window, that's computer over here, and I am going to go to my
07:04ExerciseFiles and click on SQL, and bring my cursor up there and copy that path.
07:11And that's the path that I am going to paste in here.
07:14So the Start in directory is going to be there, because that's where we have our
07:18SQL stuff, and that's where we're going to want the command line utility.
07:21So I will say OK, and now we have this shortcut that brings us right there, and
07:27there is that directory.
07:28So I will quit that, and then I am going to put this back in my Copy buffer.
07:34And now I am going to add this to the command execution path, so I can just type
07:38sqlite3, and it will bring that up.
07:41So I am going to go to my Control panel, and System and Security, and down here
07:48to System. Then I am going to click Advanced system settings.
07:53For some reason, that's where they stuck this.
07:55There is a button down here that says Environment Variables, and I am going to
07:58scroll down here to the one that says Path, and I am just going to
08:02double-click on that.
08:03And then all the way out at the end, I am pressing my End key,
08:06I press a semicolon and paste my copy buffer there. Press OK and OK and OK and
08:15red X. And now when I bring this up, I am there in my SQL directory, and I can
08:20type 'sqlite3' and press Enter, and I have got the SQLite command line utility.
08:28So all that setup stuff, you don't ever need to do that again; all you need to
08:31do is double-click on this shortcut icon, and type in sqlite3 up here, and there it is.
08:37Now in this utility, you can type .help, and you get a full bunch of help here.
08:43I can scroll up and see all of that, and that's all the dot commands that work
08:49inside the SQLite 3 command line utility.
08:52I am going to type '.quit' and get out of it, and dir, and we see our databases.
08:58So the way this works is you type sqlite3 and the name of a database.
09:02So in this case I am going to bring up the test.db database and press Enter, and
09:07that's been open now and just select * from sqlite master, sqlite_master;
09:16and so there is the sqlite master pseudo table for the test.db database.
09:21So if I want to, I can say select * from customer;,
09:27and I get the customer table, and .quit.
09:31I can also do this from the command line without actually going into the
09:35utility interactive mode.
09:36I can say sqlite3 test.db and then put in quotes "select * from customer;"
09:47and I can get it right there as well.
09:49One more thing that I can do that's useful with this, if I want to create a
09:53new database, what I have here is I have these little SQL scripts, if I bring one of these up
10:01in my editor, you can see here's test sqlite3.
10:06You see this is the actual script that created the database. And if I want to
10:10recreate the database, I can say sqlite3, and give it a new name, so
10:16testworking.db and redirect from, which is the left angle bracket and then
10:23the name of the file,
10:24so test-sqlite3.sql, and now I have created a new database, test-working.db, and
10:34that one will work just like the other one.
10:36Sqlite3test-working.db and "select * from customer;" and there we have the same data.
10:48So I am just going to delete that file, test-working.db, and that's gone now.
10:57In this movie, I have shown you how to use the SQLite 3 command line utility
11:01on both the PC and the Mac, so that you're able to work with your databases
11:06from the command line, and even recreate the databases from the raw SQL,
11:10should you need to do that.
Collapse this transcript
3. Creating a Database
Understanding databases and tables in SQLite
00:00SQLite is different than other database management systems in two significant ways.
00:04First, SQLite is a database management system in a driver, and two, SQLite
00:10stores an entire database in one file.
00:13So SQLite itself, the code base that comprises the database management system,
00:18lives inside your application as a driver.
00:21So when it needs to access its database, which is contained in a file,
00:25SQLite must first go through the application, and then the application accesses the file.
00:31What this means is the directory where you're creating your files, and the files
00:36themselves, must be accessible by the application.
00:40So the application, whatever user it's running under, has to have access to that
00:44file, read and write access to the file, and read and write access to the
00:49directory the file is contained in, because SQLite creates and destroys a few
00:54temporary files along way, as it's managing your database.
00:59This becomes a little bit more complicated when your application is a web application.
01:05What this means is that the application itself lives inside the web server.
01:09So when SQLite needs to access the file, it must first go through the
01:13application, and then the application must go through the web server, and then
01:18the web server accesses the file.
01:21So this means that the database file itself, and the directory of the database
01:25file it's living in must have permissions that will allow read and write access to the web server.
01:31So, especially if your web server is a shared UNIX web server, or it's
01:36running on a Mac, the web server itself is probably running as a user
01:42different than your login user.
01:45This means that your directory and your files must have read/write access to everyone.
01:50Keep in mind also that SQLite stores all of the data for a given database in one file.
01:57This means that all of your tables are stored in this one file.
02:01This can be very convenient for small and medium-sized applications, but it also
02:06is significant when you're planning the permissions for your files, and we'll
02:11talk more about this as we go on.
02:13So, as you create your databases and tables, be aware of how SQLite creates and
02:18accesses database files.
02:20Maintain permissions so that the SQLite driver, and its host application, and
02:25even the web server has access to the directories and files that it needs.
02:29This will keep your development and maintenance cycles smooth and stress free.
Collapse this transcript
Creating a database
00:00Creating a database in SQLite is very simple.
00:03A database is created automatically when you open it, if it doesn't already exist.
00:07Let's take a look at how this is done in PHP.
00:10First, we'll make a working copy of start.php, in the Chap03 of the exercise
00:15files, and we'll name it createdatabase.php.
00:20Open that up in our text editor, and we'll go up here to the top, and
00:24we'll define a file name.
00:26Define ('DATABASE' , '), and we'll put in the path, which in the case of this
00:35machine, is in my user directory, under sqlite3_data.
00:43We'll call it chap03.sqlite.
00:47So this is the folder location and the file name for the file that will contain
00:52the SQLite database.
00:53You'll notice here, in my file system, that this is the folder, sqlite3_data, and
00:59that's in my home directory, and that's where the database will end up.
01:03Now, we'll come down here, and we'll check to see if the file exists. And we'll
01:08use the file_exists built-in function in PHP, and the file name is this
01:14constant DATABASE, and there will be an else here, and we'll just put the
01:20structure in place.
01:23So, if the file exists, we'll just put on a message that says Database and give
01:29it the file name 'already exists,' and give it the file name here.
01:38If the file doesn't already exist, we'll create it.
01:40So we'll use a try block and catch(Exception).
01:46I am going to be using the SQLite 3 interface, so we use regular exceptions,
01:51instead of PDO exceptions.
01:56If we get an error, we'll just print that error out, and the error message is in $e getMessage.
02:14Now here's where we actually create the database. $db, that will our variable
02:20for holding the object.
02:22New SQLite 3, that's the class for the database management interface and the file name.
02:31After we've created it successfully, we'll go ahead and put on a message that
02:35say that we've done that.
02:47That's all there is to it.
02:49So, really, the database is created in this one line of code right here.
02:53The rest of this is telling us what we've done, deciding if we are going to do
02:56it, and handling errors.
02:59We'll go ahead and save this, and we'll load it up in the browser, and see what
03:04happens. So this is in Chap03 and this createdatabase.php, and there it says that the
03:10database was successfully created.
03:13So that's this message here, and that means that we've successfully created the database.
03:18Let's go ahead and look in the file system, and there it is.
03:21Now, there are, of course, a couple of other interfaces, and let's just a
03:25quick look at those.
03:26We can do this with the PDO interface, which is the way that I normally like to do it.
03:30So, we'd used PDO here for the class, and PDO requires what's called a DSN, which
03:37is basically a way of defining which database system it's going to be using as
03:42well as the file name, and now it looks likes this.
03:49Now, we want to report that it's PDO that we are working with here, and we want
03:53to use PDOExceptions.
03:55So I'll go ahead and save that and reload.
04:00Now, we know that the file already exists, so it should tell us that, rather than
04:04creating a database, and there it's says the file already exists.
04:07So we need to go over here into our file system and delete the file. There we go.
04:13Now when we reload, it's says Database is successfully created.
04:17So, now PDO has successfully created a database, and there's the database there.
04:22Finally, using the CRUD interface, the bwSQLite 3 interface, that look like this.
04:29Again, this is all just very simple, and we don't need all of that,
04:33just the file name there.
04:35We are still using PDO exceptions.
04:40In this case, we're going to need to bring in the library.
04:44So that's done with require_once and the path name for the library, which is in
04:52the lib folder, like that.
04:57So we'll save that, and we'll go ahead and delete the database from the folder
05:03there and reload, and there it is. It's successfully created.
05:07Now it already exists, because we just created it again.
05:10So if we delete this, and there we go, successfully created.
05:17So, those are the three different interfaces that we have here on this system,
05:21and you can see that creating a database is a very simple.
05:24The database is created by SQLite, by default, if it doesn't already exist when it's opened.
05:29Keep in mind that your host environment, that is the web server in this case,
05:33must have read, write, and create permissions in the folder when you create the database file.
05:38So, for more information about permissions, take a look at the database
05:42permissions lesson in this chapter.
Collapse this transcript
Setting permissions for a database
00:00One of the defining characteristics of the SQLite database management system is
00:04that it keeps each database in a single file.
00:07If your database is going to run in a UNIX-based environment, which includes many,
00:10if not most, web servers, then you'll need to understand how to set file
00:15permissions so that your application can access the database.
00:18Here we are working on a Mac, running Mac OS X, which happens to be UNIX-based,
00:23and so we have this issue of file permissions.
00:26This is the directory where the data is being kept, and if I right-click on this
00:31and bring up Get Info - if you also on a Mac, follow along if not, just watch -
00:36you'll notice down here at the bottom we have the permissions for this folder.
00:39It says Read & Write permissions for Me, and it says Read & Write
00:43permissions for everyone.
00:45That's what allows the web server, which runs as a different user, to be able to
00:49create files in that directory.
00:51If I change this to Read only, which is the default,
00:54when I created that directory this was the way that it came up, and then I come
00:58in here and tried to run my createdatabase database script, you'll notice that it fails.
01:02It says "bwSQLite could not create database," "unable to open database file."
01:08That means that the permissions on the directory were not allowing the web
01:11server to create a file in that directory.
01:14So, if I come back over here, and I change this to Read & Write for everyone and
01:21then I run this program again,
01:23you'll see that it successfully created the database, and there it is in the folder.
01:28You'll also notice that when I Get Info on that files that it's owned by nobody.
01:34The web server is running as a user called nobody, and this is typical of shared web hosts.
01:40So, in order for that nobody user to be able to create files in this directory,
01:45the permissions on the directory itself which is owned by Me, must allow Read &
01:51Write permissions for others.
01:53On the Mac, that's called everyone; on some operating systems it's called others, or world.
01:58Now, if you're not running on a Mac, or even if you are running on a Mac and
02:03your script is going to be running on a server some place that happens to be a
02:06UNIX-based server, like many of them are, then you'll need to know how to set
02:10these permissions on the remote server.
02:12So let's take a look at how we do that.
02:15Here I have a program called Transmit which is the file transfer program, the
02:18FTP program that I'm using.
02:21It doesn't matter what FTP program you're using or if you are using something
02:24else on a different operating system;
02:26the techniques are going to be the same. The actual keystrokes and button
02:30presses might be a little bit different, but here is what you are going to look for.
02:33This is the folder where I am keeping the data on that server.
02:37When I right-click on it, and usually if I am in a program that I'm not familiar
02:40with, and I am looking for how to do this,
02:41that's going to be the first thing I'm going to try, so I am going to right-click on
02:44it, and I am going to select Get Info here.
02:47You'll look for something in this pop-up menu, or in one of the menus at the top of
02:51the program that says something about setting permissions or getting info or
02:56something like that on the file, where you might expect to find this dialog box.
02:59Here is the dialog box that we are looking for.
03:02This is the information on that file.
03:03It's got its file Size.
03:05It's got the Modified date.
03:06It says who the owner of the file is.
03:09That's the User account that created it, and then it's got the
03:12permissions themselves.
03:13The permissions are often displayed in several different forms.
03:17Typically, if you're a UNIX person like I am,
03:20you'll recognize this;
03:21this is the octal bits of the file.
03:24Anybody who has worked with UNIX for a long time knows with that means.
03:27If you don't know what that means, don't worry about it.
03:29It's automatically updated.
03:31You are going to use these check boxes here, and you'll notice that there are
03:34permissions for User.
03:36In this case, that's the same as Owner, billw - that's me.
03:39There are permissions for Group, and you are not going to worry about that and all.
03:43In this case, that's also me because this is owned by me without being set to a
03:47group. And then there are permissions for World, which is the same as everyone on
03:51the Mac or other on some other operating systems, and here it says world.
03:55So, that's the one that you are concerned with, because the web server is running
03:59as a different user than the owner of the file.
04:02So, here it's checked Read, Write and Execute.
04:04In the case of the directory, or a folder, the Execute bit serves a different
04:09purpose than it does with regular files.
04:11The Execute bit is the bit that says that it's a directory, so you need to
04:14leave that checked.
04:16But if I were to uncheck this one here, which is Write permissions for World -
04:21you'll notice that these other displays get updated;
04:23you need to worry about that -
04:25and I press Apply, and now I go in, and I run the createdatabase script on the
04:30server which I have loaded up over here,
04:33you see, I get that same error;
04:35"could not create the database, unable to open database file."
04:39Now, if I come back here and bring this up again, that's still up there actually,
04:43and I checked that right and I press Apply,
04:47now I go back over to the web browser and run the script again,
04:50you see, it successfully created the file.
04:53So, we'll close this, and we need to reload the directory because it's on a
04:57remote server so it doesn't do that automatically, and there is the file that we
05:01created from that script.
05:03So, understanding file permissions is going to save you a lot of trouble and
05:07headaches if your database application is going to run on a UNIX-based system.
05:11Keep in mind that the host application, that's the web server in this case,
05:16contains the SQLite driver, and it must have Read, Write and Create permissions
05:20in the folder where you create the database file.
Collapse this transcript
Defining a table in SQL
00:00To create a table in SQLite, you simply open a database and use SQL to create the table.
00:06Here on the screen, I have SID, which is the SQL Interactive Demonstrator that I
00:09created for demonstrating and teaching SQL in this, and my other SQL-related
00:14courses on lynda.com.
00:16You don't have to use SID;
00:18you have it in your exercise files if you wish to use it.
00:22You can really use any environment that'll allow you to type in SQL and see
00:26the results using an SQLite 3 database, including, if you like, the SQLite 3
00:32Command Line interface that comes with SQLite 3.
00:36So let's select the test.db, and we're going to start by looking at the tables
00:41that are already in the database, and you do that with a special table in SQLite
00:45called SQLite Master.
00:48If we SELECT * FROM SQLITE_MASTER and then press Go, you'll notice that there's
00:56no table called SQLite_Master.
00:58It's a virtual table for the purpose of seeing what's in the database, and so
01:02here we have a column for type, and these are all tables.
01:04There are other things that may be listed in here, like indexes and such, and the
01:09name of the table, and the SQL that was used to create the table.
01:13So let's go ahead and create a new table.
01:15I am going to use the SQL:
01:18CREATE TABLE, and we'll just call it t. We'll have three columns.
01:24The first one will be an INT, and the second one will be a REAL, and the third one will be TEXT.
01:32Now these type definitions are a little bit different in SQLite than they
01:36might've been in other databases that you are used to.
01:39For now, we'll use these, and later in the course, we'll talk in a lot of detail
01:42about SQLite's unique type system.
01:45So we'll go ahead and execute that, and then we'll take a look again at the SQLite Master,
01:52SELECT * FROM SQLITE_MASTER, and we will see that our table is now there,
02:00table t, and there is the SQL that we used to create the table.
02:04So that's really all there is to creating a table in SQLite.
02:08You just open the database and use SQL to create the table, and see the chapter
02:13on SQL datatypes for more information about type definitions, and the unique way
02:18that they're used in SQLite 3.
Collapse this transcript
Creating a table in PHP
00:00When you run your database application in PHP, it can define its own tables to
00:04make installation easier.
00:05Let's take a look at how that's done.
00:07So we'll start by making a working copy of start.php, in our Chap03 folder, in the
00:12exercise files, and we'll name that createtable.php, open that in the editor.
00:20The first thing we want to do is define the name of the table.
00:26That's in my Home folder, in sqlite3_data on this Mac here.
00:31Of course, you'll use whatever location is going to work on your computer where
00:38you've got the data files for your exercises.
00:41I am going to use the test.db database, and that way, we can look at it, and see
00:50it, and see the results of what we're doing. Now, we'll go ahead and open the database.
00:56We start with the try block for catching our exceptions.
01:01We're going to be using the PDO interface for this.
01:05So we'll catch PDO exception, and we'll put in an error message for that.
01:13Now, we'll open the database, and that's how we open a database in PDO.
01:34Now, we create the table, and we'll use SQL for this, and this is not a select
01:41statement, so we can go ahead and use the exact function from PDO, and that'll
01:45allow us to prepare and execute all in one step.
01:49CREATE TABLE IF NOT EXISTS t a b and c, and that's our column names.
01:59So we're using the if not exists clause to create a table.
02:02That way if the table already exists and already has data in it, we're not
02:06erasing the table and erasing all of our data, and that's a good thing.
02:11I'll just put up a message that we have successfully created the table;
02:15Table t successfully created.
02:21So that should create a table in our test database.
02:25So let's start by looking at the test database, and we'll select the test
02:30database from our Database dropdown and look at the SQLite_Master table,
02:35and that shows us that we just have the three tables
02:44originally defined in the database.
02:46So now we go over here, and run our createtable.php script.
02:53We see Table t successfully created, and when we look in SID, we can just press
02:57Go again, and we now have that table created.
03:00So with this technique, when you write your script and put this in your script,
03:04in your init function or some place like that, every time the script starts up,
03:09it make sure that it has the tables that it needs.
03:12So if the script has never been run before, it will create an empty database and
03:16an empty table, and allow you to get going without you having to distribute a
03:19separate file with the database in it along with the script,
03:22so that's a nice and handy convenience.
03:25So when you write a database application in PHP, you can use this technique to
03:30create your own tables and make installation easier.
Collapse this transcript
Creating indexes
00:00Indexes are useful for enhancing performance for searches, joins, and ordered lookups.
00:05The cost is slower inserts and updates and greater storage space requirements.
00:11SQLite uses standard SQL for creating indexes.
00:14So let's take a look at how this works in SQLite.
00:16We will use the world database here, and first let us take a look at the database
00:22itself, using the SQLite MASTER TABLE.
00:30SELECT * FROM SQLite_MASTER.
00:33This is a special table in SQLite.
00:34It is a virtual table.
00:36It is not actually stored in the database, and it gives us information about
00:41the database itself. And so here we see, we have three tables City, Country, and
00:46Country Language, and this is their definitions, and it has an index already in
00:51the country and language table. And that is because we had over here PRIMARY
00:55KEY in the definition.
00:58So let us enter a little joined query.
01:00We are going to look at the average population of the cities in the City table,
01:03organized by country.
01:05So we will say SELECT co.Name and then use the average function, and we will
01:13aggregate on ci.population, and we will give it a name, average population,
01:20because we are going to use it elsewhere in this query, FROM city AS ci.
01:27We are going to JOIN country AS co, and we will do our join ON
01:35co.Code=ci.CountryCode, so that's how those two tables are related, and we'll group by
01:46ci.CountryCode, and ORDER BY our average population descending. Now if some of this
01:57doesn't make sense to you, that's okay. This is all going to be explained later on.
02:00But for now the point is that we are doing a joined query, and this is going to
02:06be a little bit slow, because we are looking up several things in a couple of
02:10different tables, and going back and forth between the two tables.
02:13It is one of the least efficient things you can do on an un-indexed table.
02:17So I am going to press Go here, and we see that this took 300 milliseconds, roughly.
02:22Now, I am going to create some indexes and make this query go a lot faster.
02:32CREATE INDEX IF NOT EXISTS, and that will allow me to run this create index. Even
02:38if the index does exist, it just won't do the creating of the index.
02:42And I am going to call this one co code ON Country Code.
02:49So that is the code column in the country table.
02:52I'm going to create another index,
02:58call it ci Code, and this will be on the city table, and it is the Country code
03:05column in that table.
03:07So this is where the join is.
03:09The join is on these two columns, in these two tables.
03:14It is on the code column in the country table, so this is the country table, and
03:18the code column, and the country code column in the city table.
03:23So I am creating indexes for both of those columns, and that should make this
03:28query go a lot faster.
03:29So here we've got 232 rows in 295, almost 300 milliseconds.
03:34So when I run this query again, and this is with the time it takes to
03:38even create the indexes,
03:40see, it just took 32 milliseconds.
03:41It takes a 10th as much time.
03:44And now that the indexes are created, I am just going to comment these out with
03:48SQL comments here, which are two dashes, and now those two lines won't run.
03:52When I run this again, you see now it just takes 15 milliseconds.
03:56So that's 5% of the time that it took to run it without the indexes.
04:01So indexes are very powerful.
04:03They can slow things down a little bit on inserts,
04:06so you want to take that into consideration as you are designing your indexes,
04:09that it increases the amount of time it takes to do inserts.
04:12So if you have got a table where you're more inserts than reads, the indexes aren't
04:17going to buy you as much, and they may actually cost you more than you buy.
04:20So you want to take these things into consideration, as you are creating your indexes.
04:25Indexes are created in SQLite using standard SQL.
04:28Once created, the database will use them automatically, so be sure to think
04:33about disk space and insert update times when you are designing a database with indexes.
Collapse this transcript
Indexing ID fields
00:00SQLite has a very easy, fast, and powerful feature for implementing ID fields.
00:05Every row of every table has a sequential id, by default, called row ID, and you
00:10can access this by selecting for it, using row id or one of its aliases.
00:15Let us take a look at how this works.
00:17If I look at the country table, for example, in the world database, I will just
00:22say SELECT Code FROM Country,
00:26you see, we get 239 rows, and we have got all of these codes.
00:31Let us say that I wanted an id number for each of these, a unique number for each row.
00:36Well this table doesn't actually have one defined.
00:38If I look at the SQLite MASTER for this database, we see that the country table
00:48does not have an id field.
00:50It has the code field, and that is really its master index, and name and
00:55continent like that, but there is no id field.
00:58Now, if I wanted a unique id for each of these rows, I can look at the row id like this.
01:08Instead of *, I am going to say ROWID and code.
01:15Now we can see there is a unique number for each row in the table that goes
01:20all the way to the end.
01:23And so this is a feature built-in that exists in every table, whether or not you
01:28have defined it, and by default, it is called ROWID or OID, like that, or _ROWID_
01:38with underscores, and these are simply there for compatibility with other
01:43database functions.
01:44SQLite allows you to give an alias to this field, and you will notice when we
01:50looked at the SQLite MASTER for this database, that the city table has ID
02:02INTEGER PRIMARY KEY, like that, and this is how you create an alias for that row id.
02:08So let us take a look that how this is done.
02:09We are going to use our in memory database now.
02:12Its SQLite MASTER is empty because it is an in memory database.
02:16Every single time we press this Go button, we get a brand-new empty database,
02:19and it is in-memory.
02:21So I am going to create a table here.
02:22I am going to CREATE TABLE, call it t. I am going to give it a field called id,
02:28and I am going to say INTEGER PRIMARY KEY, like that.
02:33Now, it needs to be all spelled out, just like that.
02:36If you say int primary key, it will not work.
02:41And now, I am going to insert some values into it.
02:43Remember, every time I press the Go button, I will get a brand-new blank database
02:48because this is an in-memory database.
02:49So I am going to INSERT INTO t, I am just going to insert into the a, b and c
02:56columns, and I am going to give it some values.
03:01I am just going to say a, b, and c, like that.
03:04Now we will go ahead, and we will do a few of these,
03:06so I am going to just copy and paste.
03:08I will give it five rows, and then we will SELECT * FROM t, and we will take a
03:14look at the SQLite MASTER as well.
03:16We'll say Go, and you will notice that SID has some special things that it does
03:26when there is multiple statements. It tells us how many queries total, how many
03:29non select queries affected, how many rows.
03:32This is all happened in less than a millisecond because this is an in-memory database.
03:36Now you will notice that I inserted into these, but I only inserted the a, b and
03:40c. I did not insert anything in the id field, and yet the ID field got populated
03:46with sequential numbers.
03:48So the way this works is that the database engine will assign the next highest
03:53integer to the id field.
03:55So if I were to come in here and delete one and then insert another row after it,
04:07so we will delete number 3, and we will insert one more, the next one should still be 6.
04:13It does not reuse that number. If it runs out of numbers, and this is a 64 bit integer,
04:19so it would take a while for it to run out of numbers,
04:22it will try to find some spaces where it can still insert a unique number. And if
04:28that fails, and it just fails to insert anymore rows.
04:31Now if I were to have deleted number five, let us see what happens then.
04:36You see, we still get a 5.
04:40There is another behavior available.
04:42If I use the keyword AUTOINCREMENT when I am defining this table, now the next
04:49one will be 6 instead of 5.
04:51The reason for that, and you will see that we got another table in here
04:55called SQLite Sequence,
04:57this changes the behavior of how it assigns the ids.
05:01This will make it one more than the largest number that was ever in that column,
05:06and it keeps track of what the largest number was in this special SQLite
05:11sequence table, and that is just a special table with one row that just says
05:16'this is the largest number that has ever been in that column in that table.'
05:20Let us go ahead and copy both of these rows.
05:24If I delete this 6 and insert yet one more row, then it will have a 7 there,
05:31because it is remembering that six was now the largest number that had ever
05:35been in that column.
05:36So that behavior is available with the autoincrement.
05:39Most of the time, you are just going to use it without the autoincrement,
05:42and it will just use one larger than the large number that is already in that
05:46column, and it won't create that extra special SQLite sequence table.
05:51Now ID fields can be extremely useful, especially in applications with several
05:55tables that interact frequently.
05:57We use SQLite's integer primary key feature, and it costs virtually nothing, because
06:02that row id is already part of the structure of the database.
06:06So SQLite does not actually have to create space for another column;
06:10it simply aliases the row id to act like a column in the table.
06:15So it costs virtually nothing because of that.
06:18I tend to just include an id INTEGER PRIMARY KEY column on every table that I
06:23create, and I strongly recommend that you do the same thing.
Collapse this transcript
4. SQLite Data Types
Taking care with data types in SQLite
00:00Data types in SQLite 3 are significantly different than they are in the other
00:04database management systems that you may be familiar with.
00:07Most database management systems use static typing.
00:11That means that the type of a value is determined by the type of the column that contains it.
00:16So if you put a number in a TEXT column, that's still going to be TEXT.
00:23On the other hand, if you try to put text in a numeric column, it's probably not
00:27going to work, and you are going to get an error of some sort.
00:29SQLite is different.
00:31SQLite uses something called manifest typing;
00:35this means that the type of the value is determined by the value itself.
00:39Now, SQLite is syntactically compatible with statically typed SQL.
00:46It just manages the data types differently.
00:49SQLite manages data types with the concepts of storage class and Type Affinity.
00:55Every value stored in a table has a storage class.
00:59Now, a storage class is more general than a data type.
01:03SQLite has five storage classes:
01:05NULL, INTEGER, REAL, TEXT, and BLOB.
01:10BLOB stands for Binary Large Object.
01:13Each storage class may support more than one storage type.
01:18The NULL storage class is only used for the NULL value.
01:22The INTEGER storage class is assigned an integer value;
01:26it may be stored as 1, 2, 3, 4, 6, or 8 bytes.
01:32The REAL storage class is for a floating point value, and it's always stored as
01:36an 8-byte IEEE floating point number.
01:39The TEXT storage class is a text string, and it's stored using the database encoding:
01:44either UTF-8 or UTF-15.
01:47The BLOB storage class is for a blob of data, and it's stored exactly as provided.
01:52SQLite has no Boolean class.
01:55Booleans are stored as integer values, either 0 or 1.
01:59Likewise, SQLite has no date or time class.
02:02Dates and times are stored as TEXT, REAL, or INTEGER, depending upon
02:07the representation.
02:08SQLite also has this concept of Type Affinity.
02:12Type Affinity is a type recommendation for a column in a table.
02:16So Type Affinity creates a preference.
02:18It does not create a requirement.
02:21Any type may still be stored in any column.
02:24When you define a column with a Type Affinity, you're simply creating a
02:28preference for the way that SQLite looks at the data when it's deciding how to
02:33store it in the table.
02:35If there may be some room for ambiguity, it will favor a certain type over
02:39another type, based on the Type Affinity.
02:42Type Affinity may be NONE, TEXT, NUMERIC, INTEGER, or REAL.
02:47And Type Affinity is based upon the column definition.
02:50When you define a type in a column, you are defining a Type Affinity.
02:55The TEXT Affinity stores all values as NULL, TEXT, or BLOB.
03:02If you try to store a number in a column defined with a TEXT Affinity, it
03:07will store it as TEXT.
03:09You define a column with TEXT Affinity by defining your column type with
03:14anything that has the characters C-H-A-R, C-L-O-B, or T-E-X-T in the type.
03:20For example, if you say VARCHAR, that contains the letter C-H-A-R, and so that
03:27column will have the TEXT Affinity.
03:30With NUMERIC Affinity, any text that's stored is converted to INTEGER or REAL,
03:36if the conversion may be done in a lossless and reversible way.
03:39In other words, the text would have to have digits and nothing but digits in
03:44it in order for it to be converted to INTEGER or REAL, and a decimal point for the case of REAL.
03:50In order for this conversion to happen, 15 significant digits must be
03:53preserved and reversible.
03:56If lossless conversion is not possible, then the value is stored as text, even
04:00though the column is defined as NUMERIC.
04:02It's a NUMERIC Affinity;
04:03It is not a NUMERIC type, and so it will prefer NUMERIC, but remember, you can
04:07store any type in any column.
04:10If no other Affinity rules are satisfied, then this is the default Affinity: NUMERIC.
04:17The INTEGER Affinity works just like the NUMERIC Affinity, but it prefers
04:21INTEGERS over REALS.
04:24Any column type that contains the string INT will be given the INTEGER Affinity.
04:30The REAL Affinity works like the NUMERIC Affinity, but it prefers REAL storage.
04:36Any column type that contains R-E-A-L, F-L-O-A, as in Float, or D-O-U-B, as in
04:42Double, will have a REAL Affinity.
04:46And finally, the NONE Affinity, if the column type contains the letters B-L-O-B
04:51or no type is specified at all, then it will have NONE Affinity, and there is no preference.
04:57It will just do its best to guess at how to store the data.
05:01So let's look at some examples, so we can get an idea of how this actually works.
05:05And we are using the in-memory database here, so we'll go ahead and create a table.
05:12CREATE TABLE t, and we'll give it some columns;
05:16we'll say a is INT, and b is REAL, and c is TEXT.
05:26And now we'll insert some values.
05:31We'll try some numbers, and we'll try some letters, and we'll try some words,
05:51and finally, we'll take some numbers, and we'll just put them in quotes.
06:00Now let's see what we get out of this table.
06:01We'll start with SELECT * FROM t, and then we're going to use the TYPEOF
06:06function to actually get a representation of what type these results are.
06:11So we're going to select TYPEOF (a), and I'll just copy and paste to make this
06:19easier to type, and that's a c, and that's a b, FROM t. Now, this is an
06:27in-memory database, so it will just run all of this. There we are.
06:32So we have created these three columns in a table, and the first one has a Type
06:36Affinity of INTEGER, and the second one has a Type Affinity of REAL, and the
06:41third one has a Type Affinity of TEXT.
06:43But of course you can store anything you want in any column, because this is
06:46SQLite, and it's got manifest typing.
06:49So first we store these three numbers:
06:511, 2 and 3, and so this first row has the 1 as an INTEGER, and 2 is REAL;
06:59you see that its got the 2.0, and 3;
07:02well, it looks like a 3, but if we look down here, we have the TYPEOF.
07:05The first one is INTEGER, the second one is REAL, and the third one is TEXT.
07:09It has actually stored that 3 as text, and when we display it on the screen, of
07:13course, it looks exactly the same as the integer representation.
07:17So the TYPEOF function is actually pretty useful for finding out what you are
07:20getting out of a table.
07:22The next one, we stored three characters:
07:25a, b, and c, and we see that they got stored and retrieved from the table just
07:29fine, and that they all came out text.
07:32Even the column with the INTEGER Affinity and the REAL Affinity, they came back as text.
07:37And that's because there was no way to convert these and to be able to convert then back.
07:42In other words, there's no way to convert an a into an integer and still convert it back.
07:48So it stored it as text, and it stored it as text, and stored it as text.
07:53This next one, we have three words:
07:541, 2, and 3, and those work exactly the same way:
07:57text, text, and text, and there they are, the results.
08:00And so you can see that we're successfully storing text in columns that are
08:05defined with an INTEGER and REAL Affinity.
08:07This is an important point.
08:09This is manifest typing.
08:11And finally, we have numbers, and we passed them as strings in quotes, and
08:18watch what happens here.
08:19We get back the number 1 as an INTEGER, the number 2.0 as REAL, and the number 3 as TEXT.
08:26So that worked exactly the same as this way.
08:30So SQLite 3's manifest typing is very different.
08:34If you are familiar with other databases, it's very different than how other
08:37databases work, and yet, it is syntax-compatible.
08:42You can define these columns with INTEGER, REAL, TEXT, and other kinds of affinity.
08:47You can store the same kinds of values that you would expect to, using other
08:51database engines, and it will work in exactly the same way.
08:56The advantage here is flexibility.
08:58For a lot of small to medium-size business applications, it's very easy to just
09:04define a table and say, a, b, and c, like that, and it will still run, and it
09:11will still work exactly like you expect it.
09:14If you really want those numbers to be stored as Floats, then you can come in
09:17here, and you can say REAL or Float, or whatever, and you are getting the
09:22results that you expect.
09:23So you have this added flexibility, and that flexibility gained is well worth
09:29the effort in learning how this new paradigm works.
Collapse this transcript
Storing numbers with INTEGER
00:00Columns defined with INTEGER affinity tend to store data using integers.
00:04Like every column in an SQLite database, they can store any data, but they will
00:08prefer to store integers.
00:10Let's create a table with a couple of INTEGERs.
00:20Now we will go ahead and insert some data into it.
00:23We will make that a few rows, and now we will take a look at what we have got.
00:43So we are using the in-memory database here, so all of this will happen
00:49on the fly and each time we run it, it will create an entirely fresh database.
00:55So there we have all these numbers.
00:57Now, we'd like to see their types, so instead of selecting star, we can select
01:02individually - we can say a and TYPE OF (a) and b and TYPE OF (b).
01:11It looks like I have a syntax error here some place.
01:14I am missing a comma right there, and we will go ahead and run that.
01:20So we can see here that all of these values are integers,
01:24so that's pretty useful.
01:25Now we know, for example, if we were to put quotes around this 4 that in that
01:31third row the 4 will still be an integer, because that can be converted to an
01:36integer and converted back without any loss.
01:39We also know that if we were to spell out the word four, that because this is an
01:44affinity, and it's not actually a hard fast type,
01:48that it will store that four as text.
01:52But let's presume for a moment that our intention here is just to put integers
01:57in this table. Now, there are a lot of good uses for integers in a database.
02:02It's a small, efficient, and fast way to store data and so there are many, many
02:06things you are going to store this way.
02:08One of the things you will notice, however, is that there is no Numeric types
02:13specifically designed for storing monetary data.
02:16A lot of databases have that.
02:18you don't want to store monetary data as a REAL number, but if you use a decimal
02:25point in any of these values, like for instance if I say here 4.25, that will be
02:31stored as a REAL number, and a REAL number has loss to it.
02:36It's a IEEE floating-point number, and if you add them together and subtract
02:41them back apart you don't always get exactly the same value, so you cannot use that for money.
02:48What you can do, however, is you can do something like this.
02:52I can say, I am going to call this representation of, say this second column.
02:57I am going to call this dollars and cents, and I am going to say that this is
03:00the number of cents.
03:01So if I wanted o store $4 and 25 cents I would put in 425.
03:06Then when I retrieve the data from the database, I am going to want to divide
03:11that by a hundred which is the number of cents in a dollar, and I am going to
03:15want to display it with a decimal point.
03:17In other words, I am going to display it as a Float,
03:20but I am not going to store it as a Float, and I am not going to do
03:22arithmetic as a float;
03:23I am going to do all of that as an INTEGER, and just whenever I look at it, I am
03:27going to look at it as if this is the number of cents, and this works fine.
03:30This is an excellent way to do this.
03:33So the way that I would do that, and I am just going to add a column to my
03:35results here is I would say CAST (b AS REAL) / 100, and I will call it Dollars.
03:47So I will go ahead, and I will run that, and you will see that here we have two
03:51cents, three cents, four dollars and 25 cents, five cents and six cents, and
03:56that works just fine.
03:58As long as all of the arithmetic that we do on this column is done with the
04:02integers, that will work fine.
04:04Now you will notice that I had to cast it as a REAL before I could do the
04:10division, and the reason for that is, if I just did it like this,
04:14b / 100, then what I get is an integer result, and I get integer division, and I
04:20don't get those cents.
04:21This is sometimes called floor division.
04:23It actually just ignores the remainder entirely.
04:26So you need to cast it first before you do the division, and that's okay.
04:31You are just dividing it by a hundred once, and you are just doing it for the
04:34purpose of display, and so it's always going to come out right.
04:39So the syntax is CAST and then your column name, AS, and then the type, you can
04:46FLOAT, or you can say REAL, and then you do the division, and then you give it a
04:50name, and it will give it that name and that column.
04:53This is the kind of a thing that you would put in a view, and we will talk
04:56about views in another movie in this course, and it's an excellent way to
05:00handle money in a database.
05:02So SQLite uses affinity for its manifest typing scheme and INTEGER columns
05:08prefer integers but may store any type of data, and they are an excellent way to
05:13handle money in a SQLite database.
Collapse this transcript
Storing numbers with REAL
00:00Columns defined with the REAL affinity tend to store numbers using
00:03floating point representation.
00:05If a number is inserted in a REAL column, it will store it using
00:09floating point representation.
00:10If something other than a number is inserted, it will store it as TEXT or BLOB.
00:15So let's go ahead and define a table and take a look how this works. Call it
00:18t and put it a couple of columns and define it with REAL affinity.
00:25And now, we will insert some data.
00:34If we insert numbers, they will be stored as REAL numbers, even though the numbers
00:39being inserted are integers. Of course, if we insert floating point numbers,
00:46those will also be and if we insert some text, those will get stored as text.
00:54So we will go ahead and take a look at what we have here, and these first two rows
01:02have these integers, and those got stored as REAL, and then we have some REAL
01:07numbers that stored as REAL, and then we have some text that got stored as TEXT.
01:11Let's look at the types, using TYPEOF.
01:23And we will go ahead and run that, and we can see these types.
01:27Everything here up, until this last row, got stored as REAL.
01:30We're not able to put an integer into one of these columns.
01:35In fact, even if we cast this number here as an integer, we'll use the cast operator,
01:40it will still store it as a REAL, and that's because this cast to is INT.
01:49That whole expression is really, exactly the same is just the two like that by itself.
01:55So, because SQLite uses type affinity, a column defined as REAL has a type
02:01affinity of REAL. So it will tend to store things as REAL if it can make numbers
02:06out of them, and if those things are not numbers, if they're text as in the case of
02:10this fourth row, then it will go ahead and store them as TEXT.
Collapse this transcript
Storing text
00:00Columns defined with a TEXT affinity will store all data as either TEXT, BLOB, or
00:05NULL, but mostly TEXT.
00:08In fact, it's actually a little bit difficult to get something in there as a
00:11BLOB, and a NULL, of course you can store it all directly, and it'll be stored as a NULL.
00:16So most of the data that goes into a column with a TEXT affinity will be stored
00:20as TEXT, even numbers will be stored as TEXT. Let's take a look.
00:24We'll create a table and give it a couple of columns and give them text
00:30affinity, and we'll insert some data.
00:46We'll try inserting numbers, with and without quotes, and maybe we'll just
00:52spell the numbers out.
00:57We'll store some NULLs, and I'll like to write in caps(NULL).
01:00You don't have to; they work just as well either way. And maybe we'll cast some
01:05BLOBs and maybe try casting an INTEGER, see what that does.
01:20And we'll look at the column and the TYPEOF, and the other column and the TYPEOF
01:25that, and we'll run that.
01:35So, we see that the first two rows came out as text, so the numbers were stored
01:40as text, the quoted numbers were stored as text, and we have the text here, and
01:45the text there, and the spelled numbers, of course, got stored as TEXT.
01:49When we store NULL directly, either a capital or a not capital, it comes out as
01:54NULL, and SID here displays NULL values in red so that we that they're NULL
01:59values are not the letter are not letters N-U- L-L, and we can see the TYPEOF is NULL there.
02:05We did get a BLOB by casting a BLOB directly so it is possible to store a BLOB,
02:10but it's not possible to store an Int, even if we cast that number as an Int,
02:14because we know that 3 cast as Int is no different than a literal 3, or a literal
02:21number, so that got stored as TEXT as well.
02:24You'll notice that I tend to type my SQL in capital letters for the keywords and
02:30use lowercase and mixed case for the identifiers, and this is tradition.
02:35It's often done that way.
02:36It doesn't have to be.
02:38SQL is case insensitive.
02:40If I were to type this select here in lowercase, it'll still work just fine.
02:45But I tend to do that because it's traditional.
02:48It tends to set off the keywords and make them very obvious, especially in an
02:53environment here without syntax highlighting, as is this text input box and
02:57HTML in this browser.
02:59So Text columns may hold TEXT, BLOB or a NULL data, and that's a type affinity,
03:04not a type, so they will tend to store data as text, even if the data is numeric
03:10and numeric data, of course, will always be stored as TEXT in a column with
03:13Text affinity.
Collapse this transcript
Storing large data with BLOB
00:00BLOB columns in SQLite do not have an affinity.
00:03They are treated as if they had no type definition at all.
00:06BLOB stands for Binary Large Object.
00:08In most databases, a BLOB column will always store data precisely as it's given,
00:13without any interpretation, or otherwise manipulating it.
00:16SQLite does that for everything, and so the BLOB definition is
00:21almost superfluous.
00:23For most purposes, a BLOB column will store data as TEXT unless otherwise told
00:28to, and for most purposes that's okay.
00:30We are going to demonstrate this in a script, because binary data is a little
00:35bit difficult to represent in a text box.
00:38So we are going to start with this start.php in the Chapter 04 folder, inside
00:42your exercise files, and I'll make a working copy of start.php, and I will name
00:48it blob.php, and we'll open it in the text editor.
00:53And for this script, we are going to use the bwSQLite 3 library, so we'll
00:57import that using require_once, and we'll come down here in main(), and we
01:12don't need that, actually.
01:13We'll go ahead and define some constants, our database, and the path here is my
01:23user folder, sqlite3_data, and we are going to use the test.db database.
01:31What we are going to do is we are going to read in an image file. And you'll
01:35notice in this folder here, there is an image file called olives.jpg, and it
01:40looks like that. And we are going to read that in, we are going to store in the
01:45database, and then we are going to read it from the database, and display it.
01:48And that's the kind of an application that you'll normally have for BLOB data.
01:52So we'll define another file name, and that will be the olives.jpg. Go ahead and
02:03open that file, and we'll use 'rb' for the open in PHP.
02:15That will make sure that we read in binary mode.
02:17If you're using a computer that distinguishes between text and binary files, like
02:24a Windows-based computer, then you require that b. It won't work without it.
02:28Now we'll go ahead and read that file into a variable called BLOB, usually fread.
02:40And fread takes this as a second argument, the number of bytes to read, and so we
02:44just get the file size of the file.
02:46Now we'll put it in our try block for the database operations, and here goes
03:09our database stuff.
03:11So we'll initialize the database, and we'll create a table.
03:20Let me go ahead and drop the table if it exist, so that we can create a fresh
03:31table each time, just for testing purposes; you are not going to want to do
03:35something like this probably in production.
03:42And we are going to use a couple more of these sql_do, so I will just copy and
03:46paste, couple of stray key-presses there, so go ahead and create the table.
04:01I am going to have an ID field in here anyway, even though I probably won't be using it.
04:06It doesn't cost me anything, and it's just a good habit to get into.
04:10So that it is the create table.
04:13Now we'll insert the data into the table.
04:25So we've created our table with one BLOB column.
04:29A BLOB column in SQLite has a BLOB affinity, which is really no affinity at all.
04:34And so we're going to insert into the table, into that one column, this BLOB
04:40value, which was read up here on line 26 from the file, and so it has that image,
04:46that picture of the olives in it.
04:48Now we'll go ahead and read that right back out of the table.
04:52Now we are just reading one value so we can use sql_query_value.
05:02Now even though I am doing an unqualified select here, I know that there is only
05:11one row in the table, because I dropped any old table if it existed, created a
05:17new table, and inserted one row right above this. And so I know that I will be
05:21getting just one value, and that allows me to use sql_query_value, like that.
05:27Now that we've read the image back out of the database, let's go ahead and display it.
05:30Now PHP, by default, displays HTML, so if we want it to display something other
05:35than HTML, we need to tell it that we are doing that. And we do that with the
05:38header function, and we give it a MIME header for the type that we are going to
05:44be displaying, which in this case is an image JPEG. So we say Content-type:
05:47image/jpeg, and we go ahead and display it with echo.
05:57And finally, because we are going to be displaying an image here, we are not
06:01going to be using our framework.
06:03We want to get rid of this page line up here.
06:05All right, now we'll save the file and go over here to the browser.
06:12And the first thing I want to do is I want to bring up SID, and come over to the
06:15test database and take a look and make sure that we don't have that BLOB test
06:20table already in here.
06:22So we'll SELECT * FROM SQLITE_MASTER and press Go, and we see we just have these
06:29three tables: customer, item and sale.
06:31We do not have the BLOB test table in here, so I am going to press on this
06:36other tab and go to ExerciseFiles in Chapter 04, and click on blob.php, and
06:42there we have our image.
06:44So we've read the image from the file,
06:45we have stored it in the database,
06:47we've retrieved it from the database, and displayed it on the screen.
06:50So this is the image that was stored in the database and retrieved.
06:54Now if we go over here to SID and run SELECT * FROM SQLITE_MASTER again, we'll
06:58see we now have this blobtest table.
07:00The table got created.
07:02So let's take a look at the type that got stored. Remember, it had a BLOB
07:06affinity, and we'll remember that a BLOB affinity is just like no affinity at all.
07:16So we would expect that data to be stored as TEXT, not as a BLOB.
07:21So if I say Go here, we'll see the type of b is TEXT; it is not BLOB, and
07:28that works just fine.
07:29It displays the image just fine.
07:32The reason for that is that a BLOB type means Binary Large Object, and the
07:38meaning of that is that it's an object that will be stored and retrieved exactly
07:43byte for byte, as it was stored.
07:46Well, SQLite does its best to do that anyway, and even in a TEXT column, it's
07:51going to do its best to do that anyway.
07:53Under some very rare circumstances, it may do some interpolation of Unicode text.
07:59It is not guaranteed that it will never do that as a TEXT type.
08:03So in a case like this, with actual binary data, we want to ensure that it's
08:07actually getting stored as a BLOB, and the way we do that is with a CAST.
08:12So here we have this question mark.
08:14This is the placeholder for that BLOB data in our INSERT statement.
08:19We are going to use a CAST.
08:21We are going to say CAST that value AS BLOB, just like that.
08:28Now when we save this and go over here and run it, I am going to reload, and
08:34that drops the table, creates a new table and inserts that value into the table,
08:40this time cast as a BLOB, and then retrieves it from the table and displays it,
08:45and so all of that happened there.
08:47Now when I come over here to SID and I run this query again, selecting TYPEOF
08:52FROM blobtest, you will see that it's now a BLOB.
08:56So it worked fine as TEXT, and it would probably always work fine as TEXT, but
09:01if you want to actually ensure that that data is stored byte-for-byte and
09:05retrieved byte-for-byte exactly as you put it in there, then you want to cast it to a BLOB.
09:11So BLOB columns do not have an affinity.
09:13Using a BLOB type affinity is just like using no type affinity at all.
09:18They tend to prefer TEXT storage, and this actually works fine most of the
09:23time, but if you really need BLOB storage, use the CAST operator to ensure that
09:27you get the storage type that you actually need.
Collapse this transcript
Storing booleans
00:00SQLite has no Boolean type. Booleans in SQLite are simply integers with the value 1 or 0.
00:07So let's create a table and take a look at how this works.
00:17So we'll create a table with two integer columns, a and b, and we'll insert into them 1
00:29and 0, and so that is true and false, respectively.
00:34So we select from booltest.
00:38We will see these values, 1 and 0.
00:42So how do we use them as Booleans?
00:45Well, in SQL, there is no If-Then, but there is CASE, WHEN, which is used like
00:50If-Then, so we can say select, and I'll just put this on the next line, since it's
00:56going to get a little bit long, CASE WHEN a THEN.
01:01This is a logical Boolean expression.
01:04It's either going to be true or false, and so WHEN a, THEN we'll return a
01:09true, else false END as boolA, and we'll do the same thing for b: WHEN b
01:24true else false END and call it b. And we don't need a comma here, and we
01:30just say from booltest.
01:34So what this will do is it will say true if there is a 1 and false if there is a 0.
01:41So here for a, we have True, and for b we have False.
01:44So if we were to put a 0 in a, then we would have False for both of them, and if
01:52we put a 1 in b, then we have True over there.
01:54You can also experiment, and you can say well, does it think a 3 is True?
01:59And Go and yes it does.
02:01It thinks a 3 is True, but does it think a string is True?
02:08And no it doesn't. It really only thinks that numbers that are not 0 are True and
02:13numbers that are 0 are False.
02:15SQLite uses integers to represent Boolean values: 1 represents True and
02:210 represents False.
Collapse this transcript
Storing dates and times
00:00SQLite does not have a type for storing dates and times; instead, it stores dates
00:04and times usually as TEXT or sometimes as REAL, or INTEGER, depending on the
00:08representation of the time.
00:11Let's take a look at how this happens.
00:12We'll create a table here, and we'll start with the TEXT representation.
00:18So we'll say d1 TEXT and d2 TEXT.
00:22Then we'll insert some values in there, and we will use the DATETIME functions.
00:31We'll get into more detail about the Date and Time functions in the chapter on
00:36Date and Time functions.
00:44So now it defaults to UTC, or Greenwich Mean Time, but if we say localtime, then it
00:50tries to find our local time.
00:51Where I am right now is on the west coast of the United States. Then we'll select
00:56d1 and the TYPEOF d1 and d2 and the TYPEOF d2 FROM t.
01:08We'll go ahead and look at that. And so what we get here is we've got the
01:12Greenwich Mean Time, which is actually tomorrow the 14th and the local time here
01:18and these are both TEXT strings.
01:20So that's the most common way for dates and times to be represented is using
01:25this format: year-month-day hour-minute- second, and so this DATETIME function,
01:30it's a little shorthand that's built into SQLite that represents the time, like that.
01:36It's also capable of storing times as a REAL, using a special Julian date format,
01:45or as an INTEGER, using a Unix Epoch. And Unix Epoch, for those of you that aren't
01:51familiar with it, is the number of seconds since the beginning of the year 1970.
01:56So if we insert these values, we would say JULIANDAY, and that's a special
02:03function that gives us that. And there isn't a special function for the Unix
02:07Epoch, so it's done with STRFTIME and all of these other special functions for
02:13representations are actually aliases of STRFTIME with specific formats. And the
02:18format for the Unix Epoch is '%s', and let's say 'now'.
02:24So if I run this, we'll see that we have a REAL, and we have an INTEGER. And the
02:29REAL is this really long number which represents the Julian day, which is the
02:33number of days since noon in Greenwich on November 24 4714 B.C., according to the
02:41proleptic Gregorian calendar, and that's the definition of Julian day.
02:46The other one, the Unix Epoch, is a lot easier to remember.
02:49Now these actually do represent dates and times, and they can be converted very
02:54easily into a readable format. And so we can say SELECT DATETIME(d1) and
03:03DATETIME(d2) FROM t. The DATETIME(d2) actually has to be formatted with the
03:13special modifier unixepoch, because that one's not otherwise handled
03:18automatically. So if you are storing your dates as an INTEGER using the Unix
03:22Epoch, which is a convenient number that's easy to get from a lot of programming
03:26languages - including PHP and Perl, then you want to remember to use this Unix
03:31Epoch. So we'll go ahead and look at that, and we actually get that nicely
03:35formatted date and time using the Date and Time function from these values in
03:40REAL and INTEGER format.
03:41Now most of the time you're going to just use the TEXT format because
03:45that's very convenient.
03:46It's very common in SQL database engines. But for those cases where
03:50you want a higher resolution, and you want to use the Julian day, or those cases
03:54where it's convenient to be using the Unix Epoch, these other formats are
03:58available, and they can be stored as REAL, or INT, and SQLite just uses them like
04:03it does any other date and time.
04:05SQLite stores dates and times as TEXT, REAL, or INTEGER. For most purposes, you'll
04:10use the TEXT format, and for more detail on the Date and Time functions in SQLite
04:16see the chapter on Date and Time functions in this course.
Collapse this transcript
5. Storing and Reading Data
Storing data with INSERT
00:00The insert statement is how you add rows to a table. So, we have SID open
00:05here and using the in-memory database, I'll create a couple of tables. We can
00:10see how insert works.
00:15So I'll create a table called a, and it has three columns: a, b, and c, and I'll
00:20create a table called b, and give it three columns, and call them d, e, and f.
00:25Now, I'm going to INSERT INTO a, and this is how the insert statement works.
00:33You say insert into and the table name.
00:36You can give it the names of the columns: a, b, and c, then the values with the
00:43keyword VALUES and then give it data, and I'll just say 'a', 'b', 'c' like that.
00:51And then we can SELECT FROM the table, and we should get one row with three
00:58columns in it: a, b, and c, and the values are a, b, and c because we inserted
01:02those strings a, b, and c.
01:03Now, because we're putting values in all the columns, and they are in the
01:07same order that they were defined, we can omit this column definition here,
01:13and just say INSERT INTO a, VALUES a, b, and c, and it will put them in the
01:18columns as they were defined in the table definition. And so we'll get exactly the same result.
01:24On the other hand, if I wanted to just insert data into a couple of the
01:28columns, I could say b and c here and just give it two values over here and
01:36then a will be NULL.
01:37But for our purposes right now, we're just going to go ahead and put some values
01:43in here, and we'll insert a few rows. Maybe we'll give it 1, 2, and 3
01:53and 'this', 'that' and other' and maybe 'x', 'y', 'z'.
02:06Now, we select, and we have these four rows.
02:11And we did that because with table b, we're going to do something different.
02:14I'm going to show you another way that you can insert data.
02:17You can insert data into a table and get that data from another table.
02:22So I can say INSERT INTO b SELECT * FROM a and then if we SELECT FROM a, and
02:34we'll also SELECT FROM b, you'll see that the two tables are exactly the same.
02:41This is a, and this is b. If I'd like, I can specify that the data is going to go
02:48in a particular order into table b. I can say, insert into b and then give a
02:55column names, and I can say f, e, d reversing the order. And so column a from a will
03:03go into column f from b, column b from a will go into column b from b, and
03:09column c from a will go into column d from b and so these will be reversed in
03:15the second select: c, b, a, 3, 2, 1, other, that, this, z, y, x. Or of course I
03:23could do this the other way and instead of specifying them that way, I can say
03:28SELECT c, b, a FROM a, and they will go in the defined order in b, and we'll get
03:34exactly the same result.
03:36So this is how insert works.
03:38You insert into a table.
03:40You can either use the values keyword, or you can use a select from another
03:43table, or you can really use any expression for where the data comes from.
03:48So that's how insert works.
03:50The insert statement is simple to use; it's flexible.
03:53You may insert values directly or from another table using select.
03:56The syntax is simple, and it can be extremely powerful.
Collapse this transcript
Updating data with UPDATE
00:00When you want to change the values in a table,
00:01you use the update statement.
00:04To demonstrate this, we're going to use the test database. And I'm just going to
00:09copy and paste some data in here, and we will create a table, and insert some
00:16data into it, and then select from it.
00:19And now this data is in the test database, and so we don't need this part
00:25anymore, and you can see that the data is still there.
00:29So let's say that I wanted to change one of these quotes.
00:32Let's say number 4 here, The Terminator, and he says, "I'll be back" and maybe
00:38instead, we want him to say, "Hasta la vista, baby!"
00:41So I say UPDATE t, which is the name of the table, so I'm updating the table SET
00:48quote =, and I'll put my new data in there and I say WHERE id = 4.
00:55So here is the id column, and you see its number 4 is the one with The Terminator.
00:59Now, if you omit this WHERE it will update all of them, and that's probably
01:04not what you want.
01:05So we're going to go ahead and leave the WHERE in there, and I'll put something
01:09else in the quote here. I'll say "Hasta la vista, baby," and I'll say Go.
01:17And now when we SELECT the data again, we see that that fourth one, which says
01:24"Hasta la vista, baby," it only updated the column that we told it to.
01:28And it only updated the row that we selected in the where clause.
01:33And of course we can update more than one column at a time.
01:35And I want to leave this in here this time. We'll say UPDATE t SET quote = ,
01:46byline = and WHERE id = 4.
01:52So this time we're going to give it a difficult quote from a different movie.
01:55We're going to say Rosebud, one of the great lines from any movie ever, and that
02:00was of course spoken by Charles Foster Kane.
02:05And then we'll also SELECT FROM the table at the same time, so we're updating
02:09the row where the id = 4, which is this one here that currently has the
02:12Terminator's quote in it, and we're setting both the quote and the byline, and
02:17these don't have to be in the order they were defined; they can be in either
02:19order, and they're separated by commas.
02:22So we'll say Go, and there we have it: the row with the id number 4 says Rosebud,
02:29and it says Charles Foster Kane, and we select it again so we have that.
02:34So really that's all there is to the update statement.
02:38The update statement is a simple and powerful tool for updating data in a table.
02:43Columns are specified with the SET clause, and rows are selected using where.
02:47Take care that you don't inadvertently omit the where clause, and I am going to
02:52just show you what happens if we do that.
02:53If I take this WHERE clause out and run this with the UPDATE SET like that
02:59without a where clause, you'll see every single row in the table gets
03:03updated with that data.
03:04So you want to take care that you don't inadvertently omit the where clause, or
03:09you may overwrite your entire table.
Collapse this transcript
Reading data with SELECT
00:00The select statement is easily the most common statement in SQL.
00:04It is how you read data.
00:06So we are going to look at the select statement a little bit here, and
00:10it's really a huge topic and in some ways the entire course is about the select statement.
00:15So you will see a lot of examples of a lot of different ways that you can use
00:19select throughout this course. And in this movie we are just going to hit the
00:22high points a little bit.
00:24The most common thing that you'll see done with select is SELECT * FROM, and we
00:29will give it a table name. We'll say album here, which is in the album database,
00:33so I will just select that from my Database dropdown and Go.
00:37There we have all of the albums in the album database.
00:41Now you'll often see it done this way,
00:44where instead of saying SELECT * with the asterisk which selects all of the
00:49columns, I will list some.
00:51I will say title, artist, and label, and if I say Go, then I just get the title,
00:58artist, and label columns.
01:00So, the first clause in select is what I'm selecting, and the second is the FROM.
01:06In fact, you can omit the FROM, and you can just put literals over here. I can
01:10say SELECT 'Hello, World' like that I will get 'Hello, World', and it actually
01:16titles the column 'Hello, World' with quotes, because that's the literal
01:20expression that it was given.
01:21So it gives the expression as the name of the column, and that actually is what
01:26gets passed back if you're doing this in PHP or in another language. What gets
01:31passed back in the results there as the name of the column is that expression
01:36and so in this case we have the quotes, and then there is the value.
01:39So I could also say SELECT 'Hello, World' and use the AS key word and then give
01:44it a name. And if I say AS 'Bob,' then this title up at the top there will be Bob.
01:51So whatever goes after the AS is what the title of the column is going to be,
01:56and that's also the key that will be given to the associative array.
02:00If you're using associative arrays in your PHP or hashes in another language,
02:06that will be the key that string there.
02:08So you can name it like that.
02:09Now this comes in handy, and you will see it is used a lot in joins and views
02:14and subselects and things like that later on in the course.
02:18Another way that select can be used is select can actually be used as a source
02:23for data. This is called a subselect.
02:25So if I say SELECT say id FROM album WHERE artist is IN, and I will give it a
02:34list: 'Jimi Hendrix', or 'Johnny Winter'.
02:45If I give this SELECT statement, you will see we will get 2 ids back: 11 and 16.
02:49Now I can use that as a source, and I am going to put all of this in parentheses
02:54here, and I am going to say SELECT * FROM track WHERE album_id is IN, and then I
03:05give it this expression. And I'll often indent this a little bit; sometimes I will
03:09even do it like this here, so that it's clear that this is a subselect, and that
03:16this is related to that, and it's not another statement.
03:19So I will put a semicolon there, and now we have all of the tracks from the track
03:25table related to albums from these two artists. And so this first one here, that's
03:33the Jimi Hendrix in that West album, and the second one here is the Johnny
03:36Winter And Live album, and I happen to know that.
03:39Now if I really want to know how they're related, then I can do what I call a
03:44pseudo-join. Some people consider this a join, and we will cover joins in
03:48another movie in this chapter, but I can say something like this. I can say
03:52SELECT a.title AS album and t.title AS track. And so 'a' represents the album
04:04table and 't' represents the track table, and we will see how we get that later
04:07in the statement here, t.track_number. And I'm going to indent this under this, so we
04:14can see that its related, and I can say FROM album AS a, and so I'm giving the album
04:21an alias for use here, so when we say a.title, we know that that's title column
04:25from the album table and track AS t, and then we have a WHERE, and this is where
04:34it gets interesting.
04:35a.id, so that's the id column from the album table, is equal to t.album id.
04:42And what this does is it gives these results for conditions where those
04:49two things are equal.
04:50In actuality, and the engine does it a little bit more complicated than this,
04:54but you can think of it as if the database engine is going row-by-row through
04:59both tables in all possible combinations and only giving you the results where
05:04the condition is met, where one table has a column that equals this other column
05:09and this other table.
05:10Then we will say ORDER BY because we want them in a certain order.
05:14We don't want them all mixed up, a.title, t.track_number.
05:18So it will be alphabetical, by album title, and then track number within that sorting.
05:25So let's go ahead and run this, and so this is what I call a pseudo join, and
05:28you see how joins are done in another movie.
05:31But here we have album, track, and track number, and they are all correlated.
05:36And we actually have the name of the album, even though that's not in the track
05:40table; it's in the album table. And they are joined together by this expression here.
05:46It's like a poor man's join, and we will see how to do REAL joins in another
05:50movie, but that's another way that you will commonly see this when you read
05:53SQL written by other folks.
05:55So that's a quick tour of select; of course select is far more powerful and
06:00flexible than this, but these are the highlights.
06:03It is easily the most common statement in SQL, it's extremely flexible and
06:07powerful, and while this movie does not explore its full potential, most of it's
06:10common uses are explored throughout this course.
Collapse this transcript
Reading data from multiple tables with JOIN
00:00The power of a relational database is its ability to store data in multiple
00:03tables, and access those relationships.
00:06How you access those relationships is with the join clause.
00:10As an example, we will use the world database here.
00:13Let's just take a look at those tables,
00:14the SQLITE_MASTER table, and here is a list of the tables in the world database.
00:21You see there is City, Country, and CountryLanguage.
00:24So this CountryLanguage table, you will notice it has a CountryCode, and you
00:28will notice in the Country table, there is a Code field, and that's the index
00:34into the Country table. So that's the three-letter international country code.
00:38So the CountryLanguage is a list of all the languages in the database, so if I
00:44say CountryLanguage, you'll see we get 984 rows, so that's a lot of rows.
00:52And this has all of these languages, so there are several perk country, and there is
00:56a percentage of how many people in that country speak the language, and then
00:59there is this CountryCode.
01:00So if I want to know where this language that I can't pronounce is spoken, it's
01:05in this country that I don't know that code.
01:07So if I want to know what that country is, I would have to look that up in the
01:10country table by code. Sounds confusing?
01:13There is an easy way to do it, and that's what join us for.
01:17So let's do a join.
01:18We will select c.Name, so that's the name column from the country table, and
01:25l.language, so that's language column from the CountryLanguage table. And so we
01:33will say, FROM CountryLanguage AS l, and we will join Country AS c. So how this
01:47join works is you have to have something to join on, and so we will join ON
01:51l.CountryCode = c.Code.
01:58So we are selecting from the CountryLanguage table, and we are calling it l for
02:03reference purposes, and we are joining on country as c where - and this on clause
02:11is like where in the pseudo joins, and so when l - that's the CountryLanguage
02:17table.CountryCode, so that's the CountryCode column in the CountryLanguage table -
02:22is equal to the Code column in the Country table, then these two tables will be
02:27joined, and so that's how they are joined.
02:29They are joined on that intersection.
02:31So if I say Go, I now get a list of all of the countries, in the order that they
02:37are in the country table, and the languages that are spoken there.
02:41So if I like, I can say ORDER BY c.Name, and we will get it ordered by the
02:48country name in alphabetical order.
02:50Now we still have a lot of data here.
02:52Let's just say that I want to know what languages are spoken in the United States.
02:58So then I can just say WHERE c.Name = 'United States', and I can see just what
03:08languages are spoken in the United States.
03:10So this is how we access that relationship between the two tables.
03:14The relationship is joined on the CountryCode, and so that's called CountryCode in
03:19the language table, and it's called code in the country table. And so we just
03:23use join and on, and that allows us to access both of those tables in a coherent
03:29manner, and to be able to see the name of the country from the country table and
03:34the name of the language from the language table.
03:37Just as one more example, let's switch to the album database, so you get an
03:41idea of how this works.
03:43We will look at the relationship between tracks and albums.
03:47So if I want to get a list of all of the tracks and on all of the albums with
03:52the names of the artists and the names of the titles and the names of the
03:54tracks, I can do that like this.
03:58Select a.artist, so that's from the album table, a.title, AS album, so that's
04:07the title of the album.
04:08I am just going to call it album in the column headings.
04:10t.title as track, so that's the title of the track, and I am going to call it
04:16track in the title headings, and t.track _number, FROM track as t and JOIN
04:26album AS a ON t.album_id = a.id.
04:37The join intersection is where the album_id is equal to the album_id column in
04:45the track table, and we will ORDER BY a.artist, album and t.track_number. So here we have it.
04:58It's ordered by the artist and so f for Frank Zappa comes first before j for Jimi Hendrix.
05:05We have 63 rows, and there they are, and that's all of the tracks in the
05:09database and their associated album and artist, from the album table.
05:16So we will see a lot more examples of joins throughout the course.
05:19Use join to combine data from two or more tables into one query.
05:23Join is how you access the relationships between tables in a
05:27relational database.
Collapse this transcript
Deleting data with DELETE
00:00The DELETE statement is used for deleting rows from a table. As an example let's
00:04take a look at the album database and we'll SELECT * FROM track, and you'll notice
00:12there are 63 rows in the table right now, and we'll need to know that for later.
00:17If you notice down here at the end, there is an extra track. It's called Fake
00:20Track, and its actually part of the Jimi Hendrix album.
00:23If you look up here, this is Jimi Hendrix live album, called Hendrix in the West,
00:28which is one of my favorite Jimi Hendrix albums.
00:31So if you want to suggest that row, you can say SELECT FROM track WHERE title =
00:38'Fake Track', and we will say go, and then we have just that row in the table.
00:44And if you want to delete that row, you use exactly the same syntax with the FROM and
00:48the WHERE, just instead of the SELECT star, you just say DELETE, like that.
00:54And I'll press GO, and then we will try and look for it again with SELECT, and we
00:58will see that the row is not there.
01:01Now it's really important, and I can't emphasize this enough, that when you use
01:06the delete statement, you want to always have a WHERE clause.
01:10If you omit the WHERE clause, then you can delete every row in the table.
01:16So before we do this, and I want to demonstrate this for you, but I want to make
01:20sure that you're operating on a copy of the database.
01:24In your exercise files, in the SQL folder, you will see these DB files,
01:30and these are the originals of the database.
01:35Now in my case I have got my sqlite3_ data folder over here, and I've actually
01:41got copies of these databases in there that I am operating on.
01:45So if I go and delete every row in this table, that's okay.
01:50I can take this file here, this album.db file, and I can copy it into my
01:57sqlite3_data folder and replace it, and I've got the entire original database
02:03again. And in fact, if I do the SELECT again, you will see that fake track is
02:07back, because I have just copied the original database back over into the working place.
02:15So I am going to demonstrate deleting the entire table for you, so that you can
02:20see what happens, rather deleting all the rows in the table, the table will still
02:24be there and just be empty.
02:25But I just want to make sure, if you follow along, that you're working on a copy
02:28of the database, so that you can get it back.
02:31Of course, if you aren't working on a copy of the database, you can probably
02:35still download it from the lynda.com web site.
02:37So if we say DELETE FROM track and GO, this one query affected 63 rows because
02:48I had put the original database back, and so I have the 63 rows again instead of 62.
02:53If I say now SELECT * FROM track, you'll see that it's completely empty, elapsed
03:00time 0.32 milliseconds, and there are no rows in the table.
03:05If I SELECT COUNT from track, this is what often happens if I accidentally
03:10delete all rows in a table, I go "What? there can be nothing in it," and also I
03:14COUNT and COUNT will come up with zero.
03:17So that's a really easy mistake to make.
03:20If you use the DELETE statement and you forget to include the WHERE, you will
03:24delete all of the rows from the table.
03:27Now I am going to just go back over here, and I'm going to restore my database
03:34and do my SELECT again, and now I have 63 rows again.
03:38So the DELETE statement is used for deleting rows from a table. Rows are
03:42selected using the WHERE clause. Take care that you don't inadvertently omit the
03:47WHERE clause, or you may delete all the rows in your table.
Collapse this transcript
6. SQLite Expressions
Understanding expressions in SQLite
00:00An expression is anything that returns a valid value.
00:04A literal value is an expression, a function that returns a value is an
00:07expression, and expressions can be used anywhere that a value can be used.
00:12For example, in this SELECT statement, five times five is an expression. Each of
00:18the fives is a literal value, and in either of those places, you could have
00:22another expression. Or in this INSERT statement, the value is two times two,
00:28LAST_INSERT_ROWID with the parenthesis, the function call for last insert ROWID.
00:32The literal value 'your name' in quotes, which is the parameter to the HEX
00:38function call, these are all expressions, and they all return values.
00:43Expressions use values and operators,
00:46so the values can be literal integers, floating point, strings, BLOBs, NULLs or
00:52other expressions, and the operators include string concatenation operator, and
00:58this might look odd if you are familiar with other programming languages. The two
01:01vertical bars are often used to represent 'or,' but in the case of SQL, that's used
01:05for string concatenation.
01:07There is the arithmetic operators, the comparison operators, and Boolean operators.
01:15SQL also has some special operators, including IS and IS NOT for
01:20comparisons, LIKE and GLOB for pattern matching, and GLOB is specific to SQLITE -
01:25that's not a standard SQL operator, the BETWEEN operator for pairing
01:30 comparisons, as in x BETWEEN Y AND Z,
01:35the IN and NOT IN operators for testing inclusion in a list or query result.
01:40Then there's the CASE expression, and the CASE expression is, again, a little bit
01:45different. SQL was designed by engineers at IBM that were familiar with COBOL
01:50and IBM's languages, which tended to be very verbose and tended to be very
01:54different than the languages that were being used by microcomputer operators.
01:59So most of us today are used to CASE being used in SWITCH expressions, and in the
02:05case of SQL, it's really much more just like IF/THEN/ELSE.
02:10So there's two forms of case: there's the case X when THIS/THEN/THAT, and there's
02:16the case when expression, then result, and we'll take a look at both of these
02:21in our examples later in this chapter.
02:23So expressions are used for many purposes in SQL. Anything that has a value or
02:28returns a value is an expression.
Collapse this transcript
Comparing values with comparison operators
00:00Comparison operators are used for comparing the values of expressions.
00:04For example, in the world database, if you want to see all the countries with a
00:09country code of GBR, which is Great Britain, you could do something like this.
00:14
00:24Here I've used this comparison operator, the Equals operator, which is the most
00:29common comparison operator.
00:31I've used this to select just the rows where country code is equal to GBR. So I'll say, Go.
00:37You get 81 rows, and these are the cities in the city table where the country
00:44code is GBR, and so these are cities in Great Britain.
00:48Another simple example would be if I wanted to see countries where the
00:52population is greater than 100 million. I could do something like this.
00:57
01:14So this will select those countries where the population is greater than 100
01:17million, and it'll put them in order by population, with the larger
01:22populations at the top.
01:24And there we have the results, and there are just 10 countries in the database
01:29that have populations greater than 100 million.
01:31What if we wanted to see those where the population is in a range.
01:36Here, instead of the greater than operator we could use the between operator,
01:40and we could say between 5 million and 10 million, and we get 30 rows.
01:53Now the interesting thing about the Between operator is that it's just like
01:58saying greater than or equal to 5 million and less than or equal to 10 million,
02:04with the exception being there are the population value is only evaluated once.
02:09And there are some circumstances where evaluating an expression would have a
02:13side effect, and so you'd only want to evaluate it once.
02:16So where you mean to say between, I strongly suggest that you use the between operator.
02:22The between operator also works with TEXT strings.
02:27So, for example, if I wanted to see all the countries where their name is
02:32between G and R, I might do something like this.
02:35
02:49So this will select, from the country table, those rows where name is between
02:54capital G and capital R, and it will just give us the name column.
02:58So we'll go ahead and press Go.
03:00And notice couple of things about this: one, that none of the countries that
03:06begin with R are actually included, and that's because this is doing a value comparison.
03:14So G by itself is before Gabon or Gambia.
03:19R by itself is before anything that might begin with R and so we're ending at R by itself.
03:28If we're put in here a bunch of Zs then we're getting up to and including
03:34anything that might be spelled Rzzzzz. And so if I select that, then we now include the Rs.
03:43So the ways that strings sort is important here, because that's really how
03:47this is being evaluated.
03:49It's also important to realize that a lowercase letter is above all of the
03:55uppercase letters, so if I put a lowercase r here, I'm going to get everything
04:00that start with a G all the way up to everything that starts with a Z, because
04:05all of these names start with a capital letter, and lowercase letters sort after
04:11all of the uppercase letters.
04:13So this is not a case insensitive sort, and at least for the purposes of
04:17selecting the betweens and between operator.
04:20The order by is case insensitive.
04:23So that's an important distinction to understand.
04:26SQL supports all of the standard SQL comparison operators.
04:30It's a simple set of operators, and you will likely use them often in your SQL.
Collapse this transcript
Matching patterns with LIKE
00:00The LIKE operator is used for pattern matching in SQL.
00:03For example, we will use the world database, and it we're looking for all the cities
00:10that begin with the letter Z, we could do something like this.
00:13
00:26This statement will select all the rows from the city table where the Name
00:30column starts with the letter Z, and we will notice the LIKE operator here.
00:36So the LIKE operator will look for cases where this column here, Name, will match
00:43this pattern, which is a Z followed by zero or more characters, so the percent
00:49sign is used for zero or more characters,
00:52so anything that starts with the letter Z, including a field that just had a
00:56letter Z in it alone.
00:57So I am going to Go here, and we'll get all of the rows with a city that starts
01:02with the letter Z. Now you notice I used a lowercase z there, and that's
01:07because the LIKE operator, at least in SQLite - and this is not necessarily true
01:13in other databases,
01:14but in this case, the LIKE operator is case insensitive.
01:17So at least for English, and it's not necessarily guaranteed to work in non
01:23western character sets, or any extended Unicode character sets for that case.
01:28It's really only designed to work with the normal ASCII characters. But those
01:33cases, it is case insensitive.
01:36Now if we're looking to match the second letter,
01:39for example, if I was looking for all of the cases where the second letter is a
01:43W, I could mark the first place with an underscore, and the underscore will
01:49match exactly one of any character.
01:53So following exactly one of any character, and where the next character is
01:58letter W, upper or lowercase, followed by zero or more anything, then it will match those rows.
02:05So press Go, and now we have everything with a W in the second position.
02:11So that's pretty much how LIKE works, and that's pretty much the extent of what
02:15you can do with LIKE.
02:17You can match zero or more characters with a wildcard, or you can match one
02:22character with a wildcard.
02:24So most SQLs, they will extend this in some way or another, and SQLite has its own
02:30proprietary nonstandard operator called GLOB, and it looks like this G-L-O-B,
02:38and it works a little bit differently.
02:40For example, if I wanted to do this match of the second letter is a W, I could
02:45do like this with a question mark in place of that underscore, and it does
02:49exactly the same thing, and an asterisk in place of that percent sign, and that
02:54does exactly the same thing.
02:55But this case, be careful because the letters are case sensitive.
03:01So this will match any lowercase W, which in all of these cases is the same.
03:05So if press Go, we will get exactly the same result.
03:08Now if I were to put in a capital W there, we would get zero results.
03:14Likewise, if I wanted to match a first letter Z, I would have to use a capital
03:19Z, and there we get those 59 rows, but a lowercase Z would not work at all in
03:25that first position.
03:27Now the GLOB operator is a little bit more flexible.
03:30The goal here is to work exactly like UNIX command line GLOBs;
03:34UNIX command line GLOBS are not really standardized,
03:36it depends on the shell.
03:38But this does a lot of the same things.
03:39For example, if I wanted find anything that started with a Z or say a K, I could
03:45do that, and put them in square brackets like that. And now, I've got all the of
03:50the Ks because they're alphabetical by name.
03:54Then after the Ks, I've got all of Zs.
03:58So that's the GLOB operator.
04:00The GLOB operator is little bit more flexible than LIKE operator, and SQLite has
04:04the facility for, if you wanted to get down inside of it and see or in some
04:09other supported language to write your own matching algorithms, it has some support for those.
04:16So the LIKE operator and the GLOB operator are used for simple pattern
04:20matching in SQLite.
04:22You'll use this operator when you need to find text in a table that matches
04:26a specific pattern.
Collapse this transcript
Building simple math with arithmetic operators
00:00SQLite supports the standard SQL arithmetic operators.
00:04For example, I can say SELECT 5 * 30, and I get a result that says 150, or I can
00:12say SELECT 7 / 3, and I get a result that says 2.
00:19Now if I want a fractional result, I can say 7.0 / 3, and I'll get it as a REAL
00:25number, that'll say 2.3333 or, I can do the integer division, but I can get the
00:33remainder with the Modulus (%) operator, say SELECT 7 % 3, and I get the
00:39remainder of 1. So, some applications for arithmetic operators in queries.
00:45Let's say that we wanted to see the population from our world database.
00:50We want to see the populations of countries expressed in millions.
00:54We could do something like this.
00:55I'll select the world.db, and I'll type in this in this query.
01:00
01:20So in this query, I am selecting the name and the population in millions, so
01:25that's population divided by 1,000,000, and I am naming it PopMM, for population in
01:31millions, and then I am using the PopMM, which is the already calculated result
01:38in both the WHERE and the ORDER BY clauses.
01:41So we'll say Go here, and we see our list of descending order, population in millions.
01:46So we have 1.2 billion from China and a little over a billion from India,
01:52and this is using the PopMM field, which is calculated using the integer division operator.
01:59As another example, in our test database, we have a little sale table.
02:05So if I select item ID and price from the sale table, you see that the price is
02:16expressed in integer cents. And so if I want to see those expressed in dollars, I
02:22can do something like this.
02:24Now keep in mind that just dividing price by 100 will not work. If I say price /
02:29100, then I'll get integer division, and I don't get the cents; I just get this
02:34for division result.
02:36So first I need to cast price with the CAST operator, and cast it as a REAL
02:45number, and then when I do the division, I will get the dollars and cents.
02:50And I can just say AS Price here, so I get a nice label on it.
02:55Now I have dollars and cents.
02:58So these are some examples of how you would use these arithmetic operators in your queries.
03:04SQLite supports all standard the SQL arithmetic operators, and they can come in
03:09very handy when you need calculated results in your queries.
Collapse this transcript
Matching values in a list with IN
00:00The IN operator is used to find values that match a list of choices.
00:04For example, if we wanted a list of all the cities in both the United States and
00:08England, we would select the world.db here and do something like this.
00:13
00:28So this will give us all the rows from the city table, where the country code is
00:32in the list USA and Great Britain. Of course, that list could be longer if we
00:36wanted it to be, and we'll order them by name.
00:39So we'll run that query, and there we have a list of all the cities in both USA and GBR.
00:47355 rows.
00:50Now, let's say that I didn't want to type these codes out, but I wanted to find
00:54the codes in the database. So IN can take either a literal list like that, or it
01:08can take the results of a select query.
01:11
01:29So this Select query will look for the country code from the country table, where
01:34the name is IN, and I gave it a list of country names - United States and United
01:39Kingdom, and so that will return a list of the country codes, and then those
01:44country codes are used for the IN operator in the outer query, this IN operator
01:49right here, that will look for country code in the city table.
01:53So this will give us exactly the same results, but it's getting that list of
01:58country codes from the country table, rather than typing it in literally.
02:03So there we have exactly the same, 355 rows, and it works exactly the
02:08same, except that it's getting that list from the table, rather than typing it in literally.
02:13So the IN operator is flexible, in that it can match results from a list
02:19or from a subselect.
Collapse this transcript
Choosing from multiple conditions with the CASE expression
00:00The case expression in SQL is like if, then, else in other languages.
00:04This is the only conditional structure in SQL.
00:07For example, and I am using the in-memory database here,
00:10if I create a table, and this table just has two columns: a and b, and then I
00:20will insert into that table values of 1 and 0, and then we will select from that
00:29table, and we get this here:
00:35we get 1 and we get 0.
00:36Now if we wanted to test those true and false values, because that's what those are,
00:42we would use the case expression.
00:44So instead of just selecting star, we could do something like this, CASE WHEN a
00:54THEN 'TRUE' ELSE 'FALSE'.
01:02So when I run this query, then I get a true because there is a 1 in the a
01:07position. And if I put a 0 there, then I get the word 'false.'
01:12So the way this works is using this case expression WHEN, and then this
01:17expression is a Boolean Expression - that would be true or false, and if it's
01:22true, then the result from the THEN clause is taken, and if it's false, then the
01:27result from the ELSE clause is taken.
01:30Now for a more practical example, we will switch over to the album database, and
01:35this is actually an example from the application that uses the album database
01:40that we will be looking at later in the course.
01:42I am just going to cut and paste this in.
01:45It's a bit long, and most of it here is this subselect. So I am going to explain
01:50it, and then we will see why and how we are using this case expression up here.
01:56So we are going to start at the inside and go out, and that's the way that you
01:59want to look at subselects.
02:01There are actually two nested subselects here;
02:04the first one is getting ID from the album table where the artist is either Jimi
02:09Hendrix of Johnny Winter -
02:10so that's just selecting two albums from the album table, and then inside that,
02:14we are selecting a join select from the album and track table to get a list of
02:21artists and tracks and track numbers and such. But also, more importantly, right
02:26here, we are splitting up the duration field.
02:29Now remember, the duration field is the number of seconds. That's the duration of
02:33the track. And we want to display that as minutes and seconds in a nice way.
02:38So we take the duration and divide it by 60 with integer division, and so we will
02:44get the number of minutes out of that. And we use as to name that m for minutes,
02:49and then we take the same duration and we use the module of 60 to get the number
02:53of seconds out of it, and we make that an s with the AS s.
02:59So that subselect gives us an m and an s that we can use here, so we have
03:03artist, album, track, track number, and then minutes and seconds.
03:08So here we use the minutes, and we use the concatenation operator, and we add a
03:13colon to the end of that, and then we use a concatenation operator, and we have a case.
03:19If s is less than 10 - and remember WHEN is like f, if the number of seconds is
03:24less than 10, then we are going to use a leading 0, concatenated with the
03:28number of seconds; otherwise, if it's 10 or greater we are going to give just
03:32the number of seconds.
03:34And END ends the case expression, and then we say AS duration.
03:39So we have artist, album, track, track number, and duration, and duration will be
03:43nicely formatted in minutes and seconds.
03:46So we'll say go here, and there we have it. You would notice the duration is
03:50minutes, colon, and seconds.
03:53And that is accomplished with this case expression. And the joining and the
03:58selecting and all of that is done with the subselects, and that's actually not very uncommon.
04:02In other words, this is the kind of a thing that you will see in production
04:06databases quite often. But this little trick here, this is a little bit specific
04:12to SQLite because SQLite does not have a lot of built-in functions.
04:18It doesn't have something for easily converting number of seconds into
04:22minutes and seconds.
04:23It doesn't have something for formatting strings very easily.
04:27So as a result, we end up using this case expression to accomplish that.
04:32The case expression can look a bit awkward, but it's very useful, and it's a
04:37good idea for you to become familiar with it, so you can use case whenever you
04:42need a conditional in SQL.
Collapse this transcript
Forcing a data type with CAST
00:00There will be times when you'll require a certain value to be a certain type.
00:04The CAST operator is used to force a value to a specific type. For example, if
00:10we look at the TYPEOF, using the TYPEOF function,
00:14and look at the TYPEOF the literal value 1,
00:17we'll see that it is a TYPEOF integer.
00:21Now, if we needed that to be some other type, say TEXT, we could cast it
00:27using the CAST operator.
00:29Now, it looks like this.
00:30The CAST and then in parentheses, we have the literal value, and that's a number
00:37one, not a letter L, As and then the type.
00:41Any type that's valid in a type definition in a create table statement,
00:48any of those types are valid here.
00:50So, we say TEXT, and according to the same rules that will return the type in
00:55a create statement,
00:56we will get a type, so in this case that type is TEXT.
01:01Now, let's say we wanted it to be a floating point number, we could say REAL, and
01:06now we'll get a REAL.
01:08So, in a case where we wanted to, say divide 7 / 3 with just integers, we get the number 2.
01:16But if we take that 7 and we cast it and say CAST (7 AS REAL) and then divide that by 3,
01:27now we get the result that we are looking for.
01:30It's no longer integer division; now it's floating point division.
01:33You know likewise if we have an integer that represents a number of cents in
01:381995, and we want that to be in dollars and cents,
01:43if we just divide that by 100 we are going to get the dollar part, but we are
01:47not going to get the cents part.
01:49Instead, if we cast that first to a floating point number and then divide it by number 100,
01:59now we get the full dollars and cents.
02:02One special case, if we cast a NULL value as anything at all, we will get NULL,
02:11and the type of the result will still be NULL.
02:17So, this is actually a useful thing if you're selecting values out of a
02:22database and some of the values in a column are NULL and some of them are not,
02:27and you are casting that,
02:29you can still test it for NULL and know if you have NULL value.
02:33The CAST operator is used to force a value to a specific type.
02:37SQL uses manifest typing so there are times when the type of a value really matters,
02:43for those times the CAST operator is valuable, for making sure that the value is
02:47the right type for its purpose.
Collapse this transcript
7. SQLite Core Functions
Finding the LENGTH of a string
00:00When you need to find the length of a string, for example as a parameter for
00:03another function or as a key for sorting, the SQL standard length function is
00:08available in SQLite.
00:09For example, if I wanted to find the length of all of the names of countries in
00:14the country table in the world database,
00:17select the world database here, and I can do that like this.
00:21
00:32So, this will give me all the records in the table, and it will give me the name
00:36and the length of the name and the three-letter country code from each row.
00:41So, we have 239 rows and for each row we have the name, and we have the length
00:47of the name and the Code.
00:49Now maybe I don't want all of them.
00:51I can select them by length as well. I can save WHERE LENGTH (Name) >=10, and
01:00this will just give me those where the length is 10 or more.
01:04So, now we have 86 rows.
01:06I can also sort on it.
01:07So let's go ahead and give this length an alias and say AS Len, and that will
01:13make this easier, and WHERE Len, and I can use the BETWEEN operator, BETWEEN 10 and 12.
01:24So, that will give me the lengths between 10 and 12, and I can ORDER BY Len DESC.
01:33That will give me the big ones at the top.
01:34So, now I've just got 37 rows, and it's got the longer ones at the top and the
01:40shorter ones down further.
01:43They are ordered by length.
01:44So, the LENGTH function is useful for finding the length of a string, and it may
01:49be used anywhere an expression can be used.
Collapse this transcript
Changing case with UPPER and LOWER
00:00There are times when you may need to change the case of a string, or to treat a
00:03string as all upper or lowercase.
00:05For these situations, there are the functions UPPER and LOWER.
00:09For example, here I've created a table, and the table has two columns: a and b,
00:13and I have inserted values that are mixtures of upper and lowercase As and
00:17Bs. So when I select everything FROM an ORDERED by a, you'll notice the problem.
00:24A, B is sorted before Aa and aa.
00:31The reason for that is that SQLite defaults to a binary collation, and in a
00:37binary collation all uppercase letters appear before all lowercase letters.
00:43So, even if I had a capital Z, it would appear before a lowercase a.
00:48One thing you can do about this of course is to change the collation.
00:55So here, I've changed the collation to NOCASE collation, and that will sort it
01:00the way that we expect.
01:01Now, we have Aaa before ABA, regardless of the case.
01:07Another way you can do this though, if you don't want to use the nonstandard
01:10syntax of the collate directive,
01:13You can simply Order it by UPPER case (a), and that will get us exactly the same
01:20result, even with the binary collation.
01:23So, what the UPPER function does here is it returns a copy of the string that
01:27is in all uppercase.
01:29So, for example, if I select a, UPPER(a) then you can see the result here is
01:38that UPPER(a) is all uppercase letters. And the LOWER case function lower does
01:44exactly the same thing, except with lowercase.
01:49One thing to note here about ordering by the UPPER case function is that in
01:55my experiments, it does not seem to be any slower than simply changing the collation.
02:00In fact, changing the collation probably does the same thing.
02:04So, the UPPER and LOWER functions are used for changing the case of a string.
02:08You may also use these functions in an index or in the ORDER by CLAUSE to force
02:12case insensitive sorting.
Collapse this transcript
Reading parts of a string with SUBSTR
00:00The substring function is for situations where you need to operate on part of a string.
00:04Like, for example, here I have a table with two columns in it, and each of these
00:10columns is encoded in a way where the first couple of characters are a State
00:14code on the a column and a Country code on the b column, and then there is a
00:19number after that, all in the same column.
00:22This is very common, especially in database systems where they have a fixed
00:27amount of storage, so they're packing information into columns, and the
00:32substring function is how you deal with this.
00:35For example, if I want to get the State code out of the a column, I can say
00:40SELECT SUBSTR(a, 1, 2) FROM t;
00:48And the way this works is the first parameter is the string coming in, and in
00:53this case it's the column.
00:55And the second parameter is where you're star, and
00:57this is 1-based, not 0-based, as you would expect from a language like PHP or C,
01:02but rather, this is 1-based because SQL tends to be 1-based.
01:07And the third parameter is the length of the substring that you want.
01:10And so I'll go ahead and run this query, and you see we have these State
01:14codes from that a column.
01:17And so if we want to also get the number, we can say SUBSTR(a, 3). And I don't
01:25need to give it that third parameter, because we're just going to take the
01:27rest of the string.
01:30So there we have the number as well.
01:32And so I could name these. I could say that this one is State, and this one is
01:39StValue, and we can put the AS keyword over here.
01:44That's actually optional.
01:45I tend to always include it, because it's just a good idea.
01:49And then we can we do the same thing in the b column.
01:52We can say SUBSTR(b, 1, 2) AS Country, SUBSTR(b, 3) AS CoValue.
02:08And there we have a nice little result with all of those columns.
02:12You could use this as a subselect and pass it into a join.
02:16You can do all kinds of great stuff with this.
02:19And that substring function is very, very useful.
02:22So for circumstances where you need part of a string, for example where the
02:26string is encoded with positional data, the substring function will allow you to
02:30easily extract those parts of a string.
Collapse this transcript
Changing parts of a string with REPLACE
00:00The REPLACE function provides simple string matching replacements.
00:03For example, I've got this table here with two columns, and it has these strings in it.
00:10So if I SELECT * FROM t, we see that we get those strings in our result.
00:16Now if I wanted to change the word 'Thing' in column a, and make it say something else,
00:24I can do this.
00:25Instead of a SELECT, I can say SELECT REPLACE, and the first parameter is the string.
00:34In this case, it's the a column.
00:37The second parameter is the string to search for that will be replaced.
00:42And the third parameter is the replacement string.
00:50So now column a here has Other One, Other Three, and Other Five.
00:54It doesn't have Two; all the even numbers are in column b. So that's the REPLACE function.
00:59The REPLACE function is very simple.
01:01It only does simple string matching replacements.
01:03It doesn't do any pattern matching, or regular expressions, or anything like that.
01:07If you need something more complex, you can create your own functions in SQLite
01:12with PHP, and we'll cover that in another movie in this chapter.
Collapse this transcript
Trimming blank spaces with TRIM
00:00When you get input from users, sometimes they may enter extra spaces at the
00:03beginning or end of their input,
00:06for handling this, there are the TRIM functions.
00:09So for an example here, I've created a table, and I've entered some strings with
00:14some extra spaces at the beginning and the ends of them.
00:17In order to display these in SID, I've put these extra brackets around.
00:22You see, the problem is, I'll just show you here,
00:27if you just display a string that has spaces at the beginning or at the end in
00:32HTML - and our output here in SID is HTML,
00:36those spaces are typically folded out.
00:39So in order to actually display the spaces, I have to put some brackets around
00:44it or something, and concatenate.
00:46So this double vertical bar is the concatenation operator in SQL.
00:51I know that looks weird, but that's the way that SQL is.
00:54So I'm just concatenating a left square bracket, and then the a, and the right
00:59square bracket, and doing the same with b, so that we get these spaces, and we
01:04can actually see them.
01:06So we can see the result when I do something like this.
01:08Now I'm going to trim the spaces from a, using the TRIM function.
01:12Now you can see that a here has no spaces on either side of the string, and you
01:17can see that because of the square brackets.
01:19So there are three TRIM functions in SQLite.
01:22These are mirrored in most implementations of SQL.
01:25There is TRIM, for trimming both sides of the string;
01:28there is LTRIM, which you can see just trims the left side, and it leaves the
01:32spaces on the right side; and there is RTRIM, which just trims the right side
01:39and leaves the left side alone.
01:44So there is trimming both sides, with just the TRIM function, and that's the way
01:47that you'll most likely use it most of the time.
01:50So when you need to make sure that your data has no extra spaces at the
01:53beginning or the end, the TRIM functions can be very useful.
Collapse this transcript
Reading absolute values with ABS
00:00The ABS function returns the absolute value of a number.
00:04For example, I created a table here that has two columns in it, and in each of
00:08these columns, I've inserted some positive and negative numbers.
00:12So when I SELECT FROM it, you see the result there;
00:15we have those positive and negative numbers.
00:17Now if I want just the absolute value of the numbers in the a column, I can say
00:22ABS, and put the a in the parentheses.
00:25That will give me the absolute value.
00:28It'll take you to these negative numbers, and simply change its sign, and
00:32make it positive. And so the result then looks like that. Or if you like, you can even ORDER BY.
00:39I'll just put this one back here.
00:41You can use it in ORDER BY or in your indexes, or wherever you can use an
00:45expression, you can say ORDER BY ABS (a).
00:52Now it'll put the positive and negative numbers together, so the ones are
00:56ahead of the threes.
00:57So when you need the absolute value of a number, you can use the ABS function,
01:01the ABS absolute value function, to get that result.
Collapse this transcript
Rounding values with ROUND
00:00When you need the rounded value of a number, the ROUND function will give you that result.
00:04For example, we have this table here with some REAL numbers in it.
00:08If I select them, you see those are our results, and those are REAL
00:11floating point numbers.
00:13Now if I want the rounded value of a, for example, I can say ROUND(a), like that,
00:20and that will give me the number rounded to zero decimal places. And it will
00:25still give me a REAL number, a floating point number.
00:29If I want to get an INTEGER, then I need to cast it.
00:32So I can use CAST(Round(a) AS INT), like that.
00:40Now, I get an INTEGER, instead of the floating-point REAL number.
00:43This is a different result than I would get if I cast just the un-rounded number.
00:49For example, if I ROUND(b), and CAST, you could see I get 457 instead of 456.
00:57But if I just CAST b, if I take this ROUND function out, as an INTEGER, then I
01:02just get the integer part of it.
01:04It throws away the decimal point, but it doesn't do any rounding, so I get 456.
01:08Now if I want to round it to two decimal places instead of zero decimal places,
01:14I can use this second positional argument for the ROUND function.
01:17So I can say ROUND(b, 2), and that will round it two decimal places.
01:23Of course, I'll get a REAL number, because I have a decimal in it. So I get 456.79.
01:28So you see that it rounded that 8 up to 9, because there is a 9 after it.
01:34So when you need the rounded value of a number, the ROUND function is used to
01:39give you the results that you're looking for.
Collapse this transcript
Finding the data type of an expression with TYPEOF
00:00Because SQLite uses manifest typing, sometimes it's useful to know the type of a value.
00:05The TYPEOF function returns a string indicating the type of the value that was passed to it.
00:11For example, I have this table here, and it has these type affinities in it: INT,
00:15REAL, and TEXT for the three columns a, b, and c. So when we press Go here, we
00:21see that we get these values, and they are all different types.
00:24We don't really know which types they are.
00:27So I can use the TYPEOF function to find out.
00:29I can say, a, TYPEOF(a), and do the same for b and c. And then when I press Go,
00:46we now have the value, and the type of the value.
00:50So in our INTEGER column, we have integers, and we have text.
00:55In our REAL column, we have real, and we have text.
00:58In our TEXT column, we have all text.
01:01That's how the TYPEOF function works.
01:03So the TYPEOF function is used for finding the type of a value. Because SQLite
01:08uses manifest typing, and type affinity,
01:11this function can come in very handy.
Collapse this transcript
Finding the last inserted record with LAST_INSERT_ROWID
00:00There are times when you'll want to know what the last inserted row ID is.
00:04SQLite provides the LAST_INSERT_ ROWID function for this purpose.
00:07For example, we have a table here, and it's been created with an INTEGER PRIMARY
00:11KEY and three columns, and we're inserting values into this.
00:16If I say SELECT LAST_INSERT_ROWID, like that, I'll get the number 5, because
00:24one, two, three, four, five -
00:26there are five rows in this table.
00:29In fact, if I SELECT * FROM t, you'll see that we have five rows, and the last
00:37id there is number 5.
00:40In fact, even if I don't have an INTEGER PRIMARY KEY column, I will still get
00:47that same result, because it's using the built-in ROWID.
00:51It's not depending upon your INTEGER PRIMARY KEY column for that value.
00:57So when you need to know the last inserted row ID, even if you haven't declared
01:00an INTEGER PRIMARY KEY column, the LAST_ INSERT_ROWID function will still give
01:05you this value.
Collapse this transcript
Getting the version of your SQLite library
00:00Sometimes you may need to know the version of the SQLite library that you are using;
00:04perhaps you want to use a particular feature that's only available in later versions.
00:08For this, there is the SQLITE_VERSION function.
00:11It just looks like this.
00:12You can say SELECT, or use it in any way that you can use any expression, and
00:18say SQLITE_VERSION, with parentheses, because it is a function.
00:24There you get the version. This that I'm using here is 3.6.12, because that's
00:29what's compiled into the version of PHP that I'm using.
00:33You don't always get to choose the version of SQLite, unless you are compiling
00:37everything yourself, and you know that you are compiling in a certain version.
00:40If you get something like I did here, with XAMPP that came with the version that
00:45it came with, or if your code is running on a server at some place, or you are
00:49distributing some code that's run by different people in different environments,
00:52you don't always know what version of SQLite is compiled into that package.
00:58So the SQLITE_VERSION function is available to give you that value.
01:02It returns a string with the value,
01:04so if you need to find the version of the SQLite library that you're using, the
01:08SQLITE_VERSION function will give you a string with the value of the version of
01:13SQLite that's compiled into that package.
Collapse this transcript
Creating user-defined functions
00:00When you need to perform a calculation or a transformation on your data, you can
00:03do so in a user-defined function, sometimes called a UDF.
00:07For SQLite, UDFs are written in the C or in the host language, in this case in PHP.
00:14Here we have an example of a rather complex transformation.
00:18The duration of tracks in the track table, in the album database, are stored as
00:24numbers of seconds, and yet when we want to display them - go ahead and run this query,
00:29we want to display them in minutes and seconds like this, under TrackLen.
00:34So in order to do that, I used the subselect, which also does a join and does some
00:39other things to make the data convenient for the outer query. But you will
00:44notice that in here I have separated out the duration into minutes and seconds.
00:49I have calculated the minutes by dividing the duration by 60, and that's an
00:54INTEGER division, and I have got the seconds by using a modulo -
00:58the remainder of that division - to get the seconds.
01:01So I have m for minutes and s for seconds, and then in the outer query there
01:06is still more transformation to be done.
01:09I take the minutes, and I concatenate on a colon. And then for the seconds, I
01:15need to see if the seconds are less than 10, in which case I need to have a zero
01:19concatenated with the seconds; otherwise, use the seconds. And that allows us to
01:24do something like this, which is two minutes a colon a zero and seven seconds.
01:30So this is a rather complicated transformation.
01:32It's a bit obtuse, it's hard to read, and it works well, and it performs well.
01:37So I decided this is a great case for a user-defined function.
01:41So over here in the source code for SID, we'll notice in the _init function where I
01:47set up the database connection and here's the case SQLite 3 - because sid
01:51supports both MySQL and SQLite 3, and here's the connection to the database, and
01:58here I have this PDO function called sqliteCreateFunction. And what this does is
02:05it registers a function in SQLite, so that you can use it from the SQL.
02:12So the SQL name will be SEC_TO_TIME like that, in all uppercase. And I used the
02:16same name in lowercase for the PHP function.
02:19So this PHP function will be called every time I include SEC_TO_TIME in an SQL query.
02:27So let's take a look at the SEC_TO_TIME function here in PHP, and there it is. It is very simple.
02:33It is five lines of code.
02:35First, I check to see if the parameter is NULL, and if so I don't bother to do
02:39anything else - just return a NULL. Then I make sure that it's INTEGER using
02:43PHP's intval function, and then I split out the minutes and seconds, just like I
02:48did in SQL. And then I return, using sprintf, the formatted string, minutes and
02:55seconds very, very simple.
02:57So now over, here in SID, because that function is registered with SQLite, I can
03:04replace this query with this query, and I will just paste this in here.
03:09Very simple. I am selecting all of these columns, and I am renaming some of
03:13them: album, track, trackno, and I use the SEC_TO_TIME function with the
03:19duration and call a TrackLen and then the rest of the query, it's just a
03:23normal, simple query.
03:25You read this. It's obvious what it does. And I say Go, and there we have pretty
03:30much exactly the same results, and you will notice that all of these times are
03:34formatted very nicely.
03:36So if I am going to have a function that converts seconds to time, I might also
03:40want to have a function that converts times to seconds, so that when I update
03:44the database, I can do so easily.
03:47So here is a corresponding query that updates the track with time from a time
03:53string - and I will just change this here, so I did changes in the database -
03:56where id is 70, and I know which one that is.
03:59That's this one that's called Fake Track here, and you see it was 5:19, and I am
04:02going to make it 9:17.
04:04So I will go ahead and go, and now I will query that track, and you'll see it
04:13now says 9:17. And the duration in the table is in seconds.
04:17So there is a case where some data transformation needed to be done.
04:21The PHP code for it is very simple. Here's the seconds to time, and here's
04:26the time to seconds.
04:27We simply register these functions, when we initialize the database.
04:34Here's both of them. Very simple to do.
04:37User-defined functions are a powerful way to perform data transformation.
04:41Here we have covered scalar user- defined functions, functions that operate on
04:44single rows and columns.
04:46Aggregate user-defined functions are covered in another movie in this chapter.
Collapse this transcript
Building aggregate user-defined functions
00:00Aggregate user-defined functions are just like scalar user-defined functions,
00:03but they operate on groups of rows.
00:06For example, here we have a scalar user -defined function, called SEC_TO_TIME,
00:11which takes this duration field, which is in number of seconds, and transforms
00:17it into a form that we might want to look at, in minutes and seconds, with a colon in between.
00:22As an example of an aggregate function, we can look at this version here, which
00:28will aggregate the duration column and give us a sum of all of them in hours,
00:35minutes, and seconds. And in this case we are grouping it by album_id, and so we
00:40can see for each album that we have hours, minutes, and seconds.
00:44Now we could have just used the SEC_TO_ TIME function and aggregated it manually
00:49using SUM, and taken the sum of duration and displayed in seconds to time. And in
00:56this case, we will notice that some of these might be over an hour long.
01:00So we may want to have a separate one that gives us hours, minutes, and seconds
01:03and does the aggregation by itself, and that's what we have here.
01:10So this is written in PHP, and it's actually included in SID. And you are
01:15welcome to look at it, and use it, and change it, and do what you'd like with it.
01:19And here we have CreateAggregate, and what this does is it registers the aggregate
01:24function with SQLite, and allows us to write it in PHP.
01:29Now remember for the scalar user- defined functions, we passed it a name to use
01:34with the SQL and the name of the function in PHP.
01:39In the case of the aggregate function, we actually need two PHP functions.
01:43So this is the name of the function for SQL, and then we need a step function
01:48and a finalized function.
01:49So we actually need to write two separate functions for the PHP side of this.
01:54So we will take a look at these.
01:55Here is the step function, and here is the finalized function.
02:00The step function takes three arguments: $context $rownumber and $value. And the
02:06$value is what gets passed. And there may be more if more values need to get
02:10passed, so you can have more out here. And the finalized function takes the
02:15$context and the $rownumber.
02:17The step function returns the context each time it's called.
02:21So the step function will get called for each row in the aggregation, and the
02:26finalized function will get called at the end.
02:29So in this case, all I do is I accumulate the sum of the values, so for each value that
02:35gets passed into the step, I simply add it into the context.
02:39When it's time to display it, then I take the context and I split it apart into
02:44minutes and seconds, and I set hours to zero. And if minutes is greater than 60,
02:49then I go ahead and I adjust the minutes and calculate the hours and then
02:54display it in the string format using sprintf.
02:57So it's very, very simple, and as you can see, it works well.
03:01In fact, if I want to want to include it in a JOIN, it still works well, and it
03:06actually gives us some really useful results, like that.
03:12So aggregate user-defined functions are just like scalar user-defined functions,
03:16but they operate on groups of rows.
03:18User-defined functions in SQLite may be coded in the host language, in this
03:22case PHP.
Collapse this transcript
8. SQLite Aggregate Functions
Understanding aggregate functions
00:00Aggregate functions are functions that operate on multiple rows at a time.
00:04For example, in this query the COUNT function takes all the rows FROM the City
00:10table and returns one result, based on counting all of those rows.
00:15So that makes it an aggregate function because it's operating on an
00:18aggregation of rows.
00:20In this example, all the rows where the WHERE clause is satisfied, that is all the
00:26rows where CountryCode = 'USA';,
00:28are taken and the (Population) column from all those rows is combined and
00:33averaged and one result is returned based on all of those rows.
00:38So that makes this also an aggregate function or an aggregate query.
00:42The GROUP BY clause is used to group rows together, and then those aggregations are taken
00:49on those groups of rows.
00:50For example, in this query the GROUP BY clause is GROUP BY CountryCode.
00:56So all of the rows where the Country Code is the same are taken together as a
01:01group, and then the Population is averaged for all of those rows, the
01:05CountryCode is also displayed in the result, and you get one row in your
01:09results per group of rows where the CountryCode is the same. So you will have
01:14one row for this CountryCode, one row for that CountryCode, and all of the rows
01:18where that CountryCode is the same will be taken together for the average and
01:24for that row of the result.
01:26The HAVING clause is used to select which rows are going to be returned.
01:32For example, in this query, we are grouped by CountryCode again.
01:36This is basically the same query as above, but the returned rows will have an
01:41average population of greater than one million.
01:45So the query will have to be run for all of the groups, in order for that AVG
01:49(Population) to be calculated,
01:51but the resulting rows where the HAVING clause is not satisfied will be discarded.
01:57We will get into the details of all of this in the rest of chapter. For now,
02:02understand that aggregate functions make it possible to perform operations that
02:06require multiple rows, like sums, averages, and groupings.
02:11Aggregates are an essential part of SQL.
Collapse this transcript
Counting rows with COUNT
00:00When the question is 'how many?' the answer is usually the COUNT function.
00:04The COUNT function is probably the most common aggregate function in SQL, and
00:09it looks like this.
00:15Now we will go ahead and select the world database for this.
00:19We are selecting COUNT FROM City, and so this will count all of the rows in the city table.
00:25The asterisk inside the COUNT function is a special case, and that means
00:31to count every row.
00:32Normally COUNT will only count values that are not NULL, and in this case with
00:38the asterisk, it goes ahead and counts every single row.
00:42So this is very common, and it's very fast; most database engines are
00:46optimized to do this quickly.
00:48So we return one row in the result, and that's in fact what we have down here,
00:52but the number in that row, 4079, is the number of rows in the city table.
00:58If I want to break this down by district, I can say
01:03GROUP BY District.
01:06And if I say Go I will just get a whole lot of numbers, one per row.
01:11We will see there are 1,367 rows in the result.
01:14We know there are four thousand and something rows in the table, but it's just a
01:18bunch of numbers, and that's not really very helpful.
01:20So we want to also know which number represents which district, so we can say
01:24SELECT District, COUNT (*) FROM City GROUP BY District, and then we will get a
01:30result where we have a district with each count.
01:33This is an interesting result, and I want to show you a wrong way to do this.
01:37If we take out the GROUP BY and we say SELECT District, COUNT (*) FROM City, we
01:44will get one row in the result, and we will get one district, and we have no
01:47idea which district that is.
01:49It's just the one that it happened to pick, and I honestly don't know how it picks that.
01:54It's obviously not alphabetical, but it is wrong, because there is not one
01:58district with 4079 entries.
02:01So when you GROUP BY, then you know that each group has one district, and so
02:10whatever is displayed in that row for that COUNT is the district for that row.
02:16So you have to be careful, if you are putting something that's not aggregated in
02:19your results, that that be equal to what it is that you are Grouping By, so that
02:24you know that that result is accurate. And we will get into some more examples
02:28of how this can get a little bit tricky later.
02:31This is a little bit more useful, but it's probably not as useful as it can be,
02:36so I am going to put this on another line, and I am going to say ORDER BY the
02:42Count, so that I am going to say as COUNT here, ORDER BY the Count DESC;.
02:48So this way I will get my counts top to bottom, and I had to do this because
02:53otherwise the column is named that, and that's hard to put in the ORDER BY.
02:57So we will go ahead and run this query, and now we have the big ones at the top
03:02and the small ones at the bottom.
03:04We can further select this by saying HAVING a Count > 10, and so instead of 1,300
03:13rows now, we will get 68 rows, and that's a manageable result, and there are all
03:19of the districts that have more than 10 entries in the city table.
03:23So COUNT is a simple and powerful tool for counting rows or groups of rows
03:28in many circumstances.
Collapse this transcript
Building with the SUM and TOTAL functions
00:00The SUM function is used for adding the numeric values in a column.
00:03For example, if I wanted to know the sum total of all the population figures in
00:09the Country table, I'd select the world database and put in this query here.
00:21And now I have a very big number, which is the sum of all the population numbers in
00:26the Population column from the Country table.
00:28Now the thing about the SUM function is that in SQLite it will give you
00:34an INTEGER result if all of the values in the column can be represented as an Integer.
00:41If you want it to be a floating point value, you can use the SQLite-specific
00:47function called TOTAL, and this is not an SQL standard function.
00:51This is specific to SQLite. And if I say TOTAL instead of SUM here, then I will
00:57get a floating point, and you can see 0.0 because all of these values can be
01:02represented as an integer.
01:03So I will return this to SUM because Integer is fine for my purposes here.
01:09And now I might want to make this a more useful thing.
01:11I might want to sum the population by continent.
01:15So I can say GROUP BY Continent, and I can list the continent over here because
01:22we are grouping by it.
01:23And so I know that I will get an accurate value and Go. And now I have, broken
01:28down by continent, the Population, and if I wanted to order those by the largest
01:34ones at the top, I can say ORDER BY.
01:37And now I need to name my SUM here as something that I can use in the ORDER
01:40BY clause, so I will just call it Pop, and I will say ORDER BY Pop, and I will
01:45say descending (DESC), so I get the big numbers at the top. And there we have it:
01:50ORDER BY Population, Grouped By Continent, and you can see Asia is the most
01:55populous continent.
01:56SUM is a standard workhorse function that you are going to use often whenever
01:59you need to add a column of numbers.
02:02TOTAL is a SQLite-specific version of SUM that always returns a
02:06floating point value.
Collapse this transcript
Finding minimum and maximum values with MIN and MAX
00:00To find the maximum or minimum value in a column, we'll use the MAX and MIN functions.
00:05For example, if we want to find the country with the greatest surface area from
00:10the country table, go ahead in here and select the world database and SELECT MAX
00:17of the SurfaceArea column FROM Country,
00:26so we get a number here.
00:27Now let's say we want to know the country with the largest surface area on each continent.
00:32Then we can GROUP BY Continent, and we have a list of surface areas.
00:41So we want to know which continent that is. Because Continent is in our GROUP BY
00:45clause, we can put it over here on the left-hand side, and we know that we'll
00:49get the name of each continent for each group, because once those are grouped
00:54then every row in that group has the same value for Continent.
00:59So Africa has that number, Antarctica has that number, et cetera.
01:03If I want to know which country it is within each of these continents that has
01:09the largest surface area, that's where this starts becoming tricky.
01:13Because I am grouping by continent, if I just put over here, and I say I want to
01:18know the name of that country, the result it will give me will be a name that's
01:23in that group, but not the name that has that maximum value.
01:28So if I want to get that result, this query becomes a little bit more complicated.
01:33What we need to do is we need to take this part of it here and we need to
01:37make it into a subquery.
01:39So I am going to do that like this, put this in parentheses and say SELECT
01:45Continent and MAX(SurfaceArea).
01:47We'll go ahead and give that a name that we can use elsewhere and say MaxSA FROM
01:52Country and GROUP BY Continent.
01:55So that will be my subquery, and I'll name the whole thing AS csa.
02:00So that's Continent SurfaceArea.
02:04Now I can SELECT from here, I can say csa.Continent,
02:09so, that will give me this value for my subquery, and I can say csa.MaxSA.
02:18So, that will give me this value here from my query.
02:23I can say FROM, and I still have Name out here, because I am going to get that
02:27in a JOIN, and I am going to JOIN to the Country table, and that's where I am
02:33going to find that value AS c, and here's the tricky part.
02:38I put the c. here so we know what that is.
02:41I am going to JOIN ON c.SurfaceArea,
02:48so the SurfaceArea from the Country table equals csa.MaxSA.
02:55So where this ON condition is true, that's where it'll get this Name.
03:00So that will be the name of the country within this subquery that has this
03:06maximum value to it.
03:07Now we can ORDER it BY MaxSA DESC, so we get the biggest values at the top,
03:16go ahead and run this query, and now for each continent the maximum SurfaceArea
03:21we get the Name of that Country.
03:23So it's a little bit complicated.
03:25It seems a little bit obtuse how we do this.
03:28But this is because of the way the GROUP BY works.
03:31GROUP BY works by taking an expression and giving you a group of all the rows
03:37where that expression is exactly the same.
03:40So, that allows you to use that expression in the SELECT side of your SELECT
03:46query in the left-hand side, in the results side, and know that you will get the
03:50result that you're looking for.
03:51But when you're looking for anything else, this MAX(SurfaceArea) is only true
03:55for one of those values in that group.
03:58The database engine doesn't really have any way of giving that to you.
04:02So what you need to do is you need to group this as a subquery and then JOIN
04:06it to your table and look for the row where that maximum value is equal to the
04:11value that you're looking for, and then use that to extract the name, and that works.
04:17So this is also an excellent demonstration of how subselects work.
04:21So the MAX and MIN functions are effective tools for finding the maximum and
04:25minimum values in a column. Combined with JOINS and SUBSELECTS these functions
04:30can be effective reporting tools.
Collapse this transcript
Finding averages with AVG
00:00The AVG function, average, is used for finding average values from numeric columns.
00:05For example, if we want to find the average population of all the cities in the
00:16city table, we'll select the world database, and there we have the average
00:23population for all the cities in the city table.
00:26Now if we want to group them by district, we can say GROUP BY District, and
00:33because we're grouping by District, we can put District in the left-hand side as
00:37well, and now we have it grouped by district: 1367 districts.
00:44These seem to be ordered by district as well, so we can change that order. We can
00:49say ORDER BY and if we want this to be order by our average population, we can
00:53name this as AvgPop and then say ORDER BY AvgPop, and make it Descending so that
01:03it has got the big ones at the top. And now we have the largest districts at
01:09the top, and we still have 1300 of them so why don't we go ahead and break this apart
01:16into separate lines, make this a little bit easier, and we're going to use the HAVING clause.
01:24The HAVING clause goes between GROUP BY and ORDER BY here.
01:29HAVING is like WHERE, but for aggregates, and so I can say HAVING AvgPop
01:35greater than 100000.
01:36Now we have just got 75 rows.
01:41We've a manageable list, and there we have our averages.
01:44So you will use the Average function when you need to find the average value
01:48in a numeric column.
Collapse this transcript
Grouping results with GROUP BY
00:00The GROUP BY clause is used to create groups of rows for use with the
00:03aggregate functions.
00:05For example, I'll select the world database here for my examples, and I'll just put
00:09in a simple aggregate function, the COUNT function FROM the City table. And what
00:17this will do is this will count all of the rows in the City table, and we'll get
00:21one result here, 4,079, and we see our SELECT query returned one row.
00:26So if I wanted a separate COUNT for all the cities in each district, I can say
00:32GROUP BY District, and now I will get 1,367 rows, each of them with a
00:41different number in them.
00:43So the way this works is the database engine will typically order the table by
00:48the GROUP BY expression, and then for all the rows where the result of that
00:52expression is exactly the same, it will group those rows together and perform
00:57the aggregate functions on those rows.
01:00So in this case, our expression is just a simple column name. So for all of the
01:05rows where that column has exactly the same value, it will run this aggregate
01:11function here, the COUNT function.
01:13So because the District column is the entire GROUP BY expression, I can put it
01:18over here on the left-hand side as well, and I know that the value that will be
01:26displayed will be correct for each row in the results, because that value is
01:31going to be exactly the same for every row that's aggregated together.
01:35So there is only one choice for the database engine to give me.
01:39So if I say Go here, I now get a district for each of these.
01:42If I were to put something else over here, like say Name, that is not in the
01:50GROUP BY expression, then I'll get just some random value over here.
01:56So the name, for example, now let's find one with a good number, in this row
02:01here there's 15 different names in this group, and I have no way of knowing
02:06which one was selected.
02:09So if you're going to put something on the left-hand side, you need to know that
02:12it has a value that's the same for all the rows in the group.
02:17So the GROUP BY clause is how you group sets of rows together for use by
02:21aggregate functions.
Collapse this transcript
Selecting for aggregation with HAVING
00:00The HAVING clause is to aggregations what WHERE is to rows.
00:03For example, if I select the world database, and let's say I have a query that
00:09gives me the average population grouped by district, from the City table, so it
00:14will look like this, and I'll select Go here,
00:32now I get 1,367 rows because that's how many different districts there are in
00:38this table. Maybe I don't want that many results. Maybe I just want those
00:42results where the average population is greater than say a million.
00:46So I can use the HAVING clause here, I could say HAVING, and I can use this
00:52aggregated result, average population greater than 1000000, and now when I submit
00:59my query again, I've got only 75 rows.
01:03In fact, I can ORDER this BY as well so that I have the bigger ones at the top,
01:09and make that descending so the bigger ones are at the top, and there we have
01:14the average population in each of these groups of cities grouped by district.
01:20Now this is HAVING because we're operating on an aggregated result.
01:25If I were to put WHERE here instead, I'll get a syntax error because WHERE
01:32doesn't actually even go there.
01:34WHERE would go right after the FROM, and so I'll just take that out, and I'll put
01:39it in here instead. And now I get a misuse of aggregate because this result here
01:47is an aggregate result, and WHERE can't handle that. So really, what it needs to be
01:53is a HAVING clause, and it needs to go after GROUP BY, and that will give us the
02:00result that we want.
02:01So HAVING is for aggregates what WHERE is for rows, and the HAVING clause is
02:10essential in selecting which rows of the aggregated result
02:14you want to return from an aggregated query.
Collapse this transcript
9. SQLite Date and Time Functions
Understanding SQLite support for dates and times
00:02SQLite does not have a specific data type for representing dates and times.
00:07Dates and times in SQLite may be stored in one of three formats, depending
00:11upon the representation.
00:13TEXT strings are used to represent standard ISO 8601 values,
00:19floating point numbers are used represent standard JULIAN DATES, and INTEGERS are
00:24used to represent UNIX epoch dates.
00:27Conversion functions are available for converting between types, in particular
00:31the JULIANDAY FUNCTION for generating JULIAN numbers and STREFTIME FUNCTION for
00:37generating all kinds of formats, in particular here for generating the UNIX epoch Dates.
00:43Let's look at some examples. So, Let's create a table, and we will just use the
00:48in-memory database right now. And we will just give it a couple of columns for
00:54dates, d1 and d2, and we will insert some values.
00:58We use the DATETIME function for generating the ISO date strings, and now
01:08means the date and time right now, and for the second column, we will say
01:13DATETIME and now "+ 7" days, and now when we select * from "t" you will see we get
01:26these two date and times.
01:27This is the ISO string format, ISO 8601, and we have year, month, day, and the
01:35time in hour, minutes, and seconds.
01:37So that's very readable, and it's also very sortable, which is nice.
01:40Now, if we want JULIAN Days, where it says DATETIME here, we can just say JULIAN DAY.
01:47You can do the same thing here, and you will notice that we get these
01:56floating point numbers, and they have quite a bit of resolution. And these
02:00represent the number of days since that date, a long time ago, four
02:05thousand something years B.C.E.
02:07And finally, if we want UNIX epoch time, and this is handled little bit
02:11differently. We are going to declare these as INTEGER columns; otherwise, these
02:17INTEGER numbers will be stored as TEXT, and we're going to use STREFTIME for generating
02:25UNIX epoch numbers and %s as the format.
02:28We will get into details of STREFTIME in another movie in this chapter.
02:37And there you see, we have these UNIX epoch numbers, and if we also select the
02:42TYPEOF, say d1 and TYPEOF d1 and d2 and TYPEOF d2, then we can we see that both
02:58are integers because we have given them these INTEGER declarations here.
03:01Now, if we want to convert these back, and this is what I'm saying the UNIX epoch
03:06times are handled little bit differently,
03:08if we want to convert these back into DATETIME, we can simply say SELECT
03:13DATETIME of d1 and give it this UNIX epoch, so that it knows that that's what it
03:21is, and do the same thing with column 2.
03:35You see now, we get those nice string representations, whereas if we wanted to
03:40convert back from the JULIAN Days, I'll just make these REAL for that purpose,
03:49and just make this JULIAND Day of now and JULIAN Day of now Plus 7 days,
03:59now we don't need this UNIX epoch here.
04:02It'll know what they are,
04:03it will recognize what they are, and convert them back like that.
04:07So we have REAL numbers, and they get converted back just with the
04:10DATETIME FUNCTION like that.
04:12So dates and times in SQLite are represented in any of these three different
04:18formats: as TEXT, REAL Numbers, or INTEGERs, and they are easy to convert
04:23back and forth using the built-in functions JULIANDAY, DATETIME, and the
04:28STREFTIME function.
Collapse this transcript
Getting readable, sortable dates and times
00:00When you need a good date and time representation that is readable and also sorts well,
00:04the ISO 8601 standard representation is used by the DATE, TIME and DATETIME functions.
00:11So using our in-memory database here, we'll create a table, and here we have
00:24two columns: d1 and d2, and they are both text, and we are going to insert values into them.
00:46And then we will take a look at those values, and there we have it.
00:53So these are standard ISO 8601 date and time representations.
00:59We have the year, month, day and hour, minutes, second, and this one on the left
01:05is in UTC, which is Greenwich mean time, and the one of the right is in local
01:10time, which for my computer in Ventura, California.
01:14So that's Pacific Daylight Time right now, and these are generated by this day
01:18DATETIME now and DATETIME now local time.
01:24So what goes inside of the parentheses here for DATETIME is any date and time
01:30representation and/or modifiers.
01:34The modifiers are listed on this web page here at sqlite.org. Down around the
01:40middle of the page, you see a subheading called Modifiers, and it tells you all
01:44of the modifiers that you can use.
01:46So you can say plus or minus hours, minutes, seconds, years, months, so, for
01:52example, if I wanted to say local time +3 days,
01:58I can do that, and I'll get local time +3 days.
02:03So this is on the 19th, it would be on the 16th. And you can string
02:08together as many modifiers as you like, and that works really for all of the
02:13date and time functions in SQLite.
02:14For example, and I will just put this back to local time, and if instead of
02:21SELECT * FROM t, if I said SELECT TIME (d2) FROM t, that would give me just the time.
02:32If I say TIME (d2), '+5 hours' then it would give me that time five hours later,
02:42or I can save DATE (d2, '+5 days, and it will give me the local time +5 days,
02:51which would be the 21st, because today is the 16th.
02:55The DATE function just gives you the date in the same ISO format: year, month,
03:00day, the TIME function just gives you the time, where DATETIME will give you both,
03:09as we saw when we put them into the columns in the table. And just like all of
03:15SQLite functions, it will take any kind of modifier in any kind of sequence that makes sense.
03:21The DATE, TIME and DATETIME functions are a simple way to get readable,
03:25sortable date strings.
03:27These strings also work well with the STRFTIME function for conversion to
03:30other formats, and the STRFTIME function will be covered in another movie in
03:34this chapter.
Collapse this transcript
Getting high-resolution dates and times with JULIANDAY
00:00The Julian Day format is the standard among astronomers, and is recommended by
00:03the International Astronomers Union.
00:05When represented as an IEEE floating point number, it has good resolution and can
00:10represent thousandths of a second.
00:12To demonstrate the JULIANDAY function, we're going to switch to the test
00:16database, and I'm just going to paste in some SQL here.
00:20What this does is it creates a table, and it inserts a number of values in
00:24Julian Day format. The two columns in the table are both declared as REAL, which
00:28is the best format for storing a JULIANDAY. And then at the end, we select each
00:33of these columns along with a STRFTIME formatting representation, which formats
00:38them as ISO standard DATE strings with floating point seconds, and the STRFTIME
00:44function is covered in another movie in this chapter.
00:47So in inserting all of these rows one at a time, you'll notice that because
00:52this is a rather fast computer, we don't give much difference in the time between them.
00:56So these two came out of the same thousandths of a second, while each of these
01:00is just 1/1000th of a second apart, but it does represent how good the
01:05resolution is of the JULIANDAY function and how valuable that can be for
01:09timestamping in a log file.
01:11If you look at the SQLite documentation for the JULIANDAY function, it
01:15mentions that the Julian Day is the number of days since noon in Greenwich on
01:20November 24, 4714 B.C.
01:23And it says, according to the proleptic Gregorian calendar, and if you like me
01:27look at that, and you say wait a minute Julian day, that should be the Proleptic
01:31Julian calendar and so what I was able to find out about this is the Julian days
01:36themselves are very, very useful because they convert well to IEEE
01:40floating point, and they give a lot of resolution over a wide period of time.
01:44So they don't have some of the problems that the UNIX Epoch time has, which is
01:49common in computer systems.
01:51On the other hand, the ISO 8601 standard specifies that you must use a Gregorian
01:58calendar for ISO 8601 compliance for computer dates, and that also make sense
02:03because the Gregorian calendar lends itself much better, in consistency, to
02:08computer dates and times.
02:10So that's why there's a discrepancy there.
02:12It uses the Julian day format, and it uses the proleptic Gregorian calendar for
02:17compliance with ISO 8601.
02:21So the JULIANDAY function is very easy to use.
02:23It uses all of the same modifiers the are available in all the other functions in
02:27SQLite and STRFTIME automatically recognizes these values when converting
02:33back to other formats.
02:35So in SQLite, JULIANDAY are stored as real numbers and have better resolution
02:40than TEXT or INTEGER representations, and these values are efficient and well-
02:44suited to timestamps and other applications where resolution is important.
Collapse this transcript
Formatting dates and times with STRFTIME
00:00The STRFTIME function is useful for formatting time and date in formats other
00:04than the standard DATE, TIME, DATETIME or JULIANDAY functions.
00:08For an example, we will be using the in-memory database here, and we'll go ahead
00:11and create a table. And we will insert some values, and then we'll take a look
00:41at it with STRFTIME.
00:55Now I'll put just a simple format in here. We will use a percent capital 'Y' in
00:59this one, and that will give us the year and a percent capital 'H' in this one
01:05and only was the current hour, and when I say Go, we get 2010 for the year and 18
01:13for the hour because it's a little after 6 p.m. here in California right now.
01:17And so you can see that STRFTIME will take these DATETIME formatted strings,
01:23if I go ahead and SELECT d1 here and d2 here, you will be able to see that
01:27those are ISO formatted date strings, because those were provided by the DATETIME function.
01:33And so STRFTIME can take those strings, it can also take JULIANDAY strings and
01:37format them however you like.
01:39In fact, STRFTIME is used for creating both JULIANDAY strings, the capital 'J'
01:46there for Julian day or lower case 's' for UNIX Epoch strings. And in fact, all of
01:54the standard formatting functions, DATETIME, DATE and TIME and JULIANDAY are
01:59implemented using STRFTIME.
02:00So if I just say go here, you'll see that we have the JULIANDAY there and the
02:05UNIX Epoch time there.
02:07All of the formatting options are available online at the sqlite.org web site on
02:13this page here, and it's about that far down the page over there. And you see it's
02:18based on the STRFTIME library function from the standard 'C' library. It does not
02:23implement the entire library, and in particular any of the functions that would
02:27print out localized names, like days, or weeks, or names of months; those
02:32functions are not implemented.
02:33This list here is the complete list of functions that are implemented, and if you
02:38try to use anything else, you will likely get a NULL value or an error message.
02:44So you will use this STRFTIME function if you have special formatting needs for
02:48your dates and times.
02:49If you need a format with a month, or weekday names, you'll need to use the
02:54functions in PHP, or your host scripting language.
Collapse this transcript
10. Sorting and Indexing
Understanding collation
00:00Collation order, sometimes called collation sequence, is a term to describe the
00:04rules a database system uses to put things in order.
00:07SQLite supports three coalition orders:
00:10BINARY, which uses the numeric value of the Binary content;
00:14NOCASE, which treats the 26 uppercase ASCII characters as equivalent to their
00:20lowercase counterparts; and RTRIM which works exactly like BINARY but
00:25ignores trailing spaces.
00:27It's important to note in this context that NOCASE, which is the one that treats
00:32upper and lowercase characters as the same, only works with ASCII characters;
00:36it does not work with extended Unicode characters or other character sets.
00:41Collation may be specified in the create table statement in the column
00:45definition like this or collation may be specified using the COLLATE operator in your query.
00:51As we discussed sorting and indexing, it can be important to understand how SQLite
00:56uses collation sequences to order its records.
Collapse this transcript
Sorting results with ORDER BY
00:00When you need your results to be in a certain order, you'll use the ORDER BY
00:03clause to get sorted results.
00:05For example, from the world database, we have this simple query: SELECT * FROM Country;
00:15You'll see we have lot of results that are not in any particular order.
00:20Now if we wanted them ordered by say Region, so it would be this column here, you
00:25can say ORDER BY Region, and it's as simple as that.
00:31Now they're in order by region, so we have all the Antarcticas together, the
00:35Australias together, the Baltics together, etc.
00:38Now if we wanted them ordered by population within region, we could put in a
00:44comma here and say Population.
00:47That's this column here.
00:48Now of course, there is no population in any of the Antarctica ones, but if
00:53we look at Australia and New Zealand, we'll see that we have 600, 2000, 2500 and 3.8 million.
01:00So those are now in order.
01:02Now perhaps we want the population to be descending within region. We just put in
01:07the descending keyword, DESC, after Population and say Go, and now we have
01:15Australia at the top and then New Zealand, and so it goes larger, smaller,
01:19smaller, smaller, like that.
01:22So that's how ORDER BY works.
01:24ORDER BY is a simple and powerful way to get sorted output from your queries.
Collapse this transcript
Removing duplicate results with DISTINCT
00:00To ensure that you only get unique rows in your results, use the DISTINCT
00:04modifier with your SELECT statement.
00:07For example, using the world database, if I select Region from the Country
00:13table, see I get a long list of 239 rows with just one value of the region in them.
00:22And you'll see there is Central Africa,
00:23there is Central Africa again, Middle East several times, and so it's not very useful.
00:29If I say DISTINCT, SELECT DISTINCT Region FROM Country,
00:35now I get a list of 25 rows, and there're all the regions, and they also happen
00:40to be in alphabetical order, which most database engines will do that, and it's
00:44part of how they accomplish the DISTINCT.
00:46They first sort it into order, and then just eliminate the duplicates.
00:51Now as another example, if I wanted to see all the CountryCodes that are in
00:56the City table, do that, and I get 232 rows, which is all the CountryCodes in the City table.
01:06Now maybe I also want the DISTINCT, selecting distinct CountryCode, and DISTINCT
01:13from City. Now, I get 1,412 rows.
01:16Now, I know that there's 4,000 and something rows in the City table.
01:19So this is not everything, but what I have here is each row is a unique
01:24combination of CountryCode and District.
01:28So the DISTINCT operator operates on all of the expressions in the Select clause.
01:36So in other words, no matter what I have here, it's going to make sure that
01:39each row is distinct.
01:41It's not going to just make sure each CountryCode is distinct;
01:44it's going to make sure each row is distinct.
01:46If you need something different than that, you're probably looking for
01:50the GROUPBY clause.
01:52DISTINCT is very much like GROUPBY, but it doesn't do the aggregation.
01:56So DISTINCT is useful for ensuring that every row of output is unique, but if
02:01you need real aggregated results, you'll need to use GROUPBY instead.
Collapse this transcript
Understanding indexes
00:00Indexes can be a powerful tool for improving the performance of your queries.
00:04There are also costs involved in implementing indexes.
00:09A table consists of many rows and many columns, where an index consists of many
00:16rows and one column.
00:18A table is optimized for storage, and an index is optimized for searching.
00:24The purpose of an index is to be able to quickly find rows in the table, without
00:29having to search through the massive amount of data in the table.
00:32Indexes are optimized for searching, usually using binary trees or some
00:38derivative of binary trees,
00:40so that it's very quick to locate a particular record, and then that will point
00:44out the row in the table that you're looking for.
00:47The down side of indexes is they take up space, and they slow down inserts.
00:51So you'll need to do your own real-world tests to make sure you're getting the
00:55best performance possible for your situation.
00:58Let's take a look at an example.
01:00So I'm going to select the world database, and I'm just going to paste in a query here.
01:07So this is a joined query.
01:09We're looking for the average population of the cities in the City table,
01:13grouped by country, and we're displaying both the name of the country and the
01:18average population.
01:20And so this joined query is getting the country name from the Country table and
01:27doing the group by and the average on the City table.
01:30And so this is how it performs just as it is. That took about 300 milliseconds.
01:36And if I run the query a few times, we'll see that that's fairly consistent,
01:41right around 295, 296, 300 milliseconds.
01:46Now I'm going to create a couple of indexes on the Code column in the Country
01:50table and CountryCode column in the City table.
01:53Now watch how this impacts the performance.
01:56
02:15So I'll create these indexes, and you see it took 25 milliseconds, 24 1/2
02:20milliseconds to create those two indexes.
02:23Now, I'll just paste that query back in, and we will run the query, and we see
02:29that the query now happens in about 15 milliseconds.
02:33So that's 5% of the time that it took without the indexes.
02:37It's 200% faster. And even with the cost of creating the indexes, it's
02:43still significantly faster, an order of magnitude faster than it was
02:47without the indexes.
02:49But now that the indexes are made, every time I run this query, I don't have to
02:53index the entire table again.
02:55So in this case, that index is saving me a lot of time, and judging by the amount
03:00of time it took to create the indexes in the first place,
03:03those inserts aren't going to be impacted too badly.
03:06So, indexes are a powerful and useful technique for improving the performance
03:10of database queries.
03:12Be mindful of the cost and disk space and insert update times when
03:16designing your indexes.
Collapse this transcript
Working with primary key indexes
00:00A primary key index is used to create a unique and primary index for the table.
00:06Usually the primary key is the index that will be most commonly used for finding
00:11records in that table.
00:13In SQLite, a primary key index works exactly the same as a unique index, except
00:19that only one primary key index is allowed per table.
00:23Let's take a look at how this works and what it looks like in SQLite.
00:26We'll create a table, and we're using the in-memory database here, and it'll
00:31have a column called code, which is primary key.
00:35And first column in the table doesn't have to be the primary key, but it's
00:39traditional for the primary key column to come first.
00:42Then we'll call the next one value and make it a TEXT column and the third one
00:47perhaps ycode, and we'll call it UNIQUE.
00:51And ycode, that might be a column that's used to index into another table.
00:55Now if we take a look at the SQLITE_ MASTER pseudo-table, we'll see that we've
01:04created this table, and there is the definition, and we have two indexes.
01:09The way that they are represented here is pretty much the same.
01:12The only difference between these two is if we call this one PRIMARY KEY,
01:15we'll get a syntax error.
01:18It says General error: 1 table "t" has more than one primary key.
01:22So we call this one UNIQUE.
01:25Really, when you think syntactically about the words "PRIMARY KEY," we're talking
01:30about the primary way that other tables will find records in this table.
01:35So that's what it means.
01:36It has that meaning, even though operationally it's no different than a unique.
01:41So go ahead and insert some rows in here, and we don't need this anymore.
01:45So we will say INSERT INTO t VALUES, and we will
01:51say 'a' and thing one, and one, we will create a few of these, call this one
02:02'thing two' and 'two' and 'three', 'three' and 'four' and 'four'.
02:16Now we'll SELECT * FROM t;, and we'll get an error because we have
02:22inadvertently put the same value in all of these different rows for primary
02:26key, and those have to be unique.
02:28So we say Integrity constraint violation:
02:3019 column code is not unique, and
02:33it says query 3, 4 and 5, so we know which one's they are there, this one and,
02:36this one, and this one.
02:37So, we need to make these unique, and so I'll just put b and c and d. Now we have
02:45four rows in our table, thing one, thing two, a, b, c, and d.
02:48You'll notice, also, that if I put an X here, it's still allowed of course, but it
02:53doesn't change the order of things.
02:55Some databases will also order by that column by default; SQLite doesn't do that.
03:01You'll also notice that I am allowed to put a NULL in here.
03:06In fact, I can put two NULLs in here.
03:10So two things that we need to notice about this is one, NULLs are not disallowed
03:15in primary key columns in SQLite.
03:17They are in most databases. In fact, the SQL-92 standard requires that NULLs be
03:24disallowed, and there is a note on the SQLite web site about why this is.
03:28It was basically a mistake that's been perpetuated, and they don't want to break legacy code.
03:33They say they may it in the future, but they probably won't.
03:37So this is a behavior that is non-standard that you need to be aware of.
03:40You can, and actually most people do, put NOT NULL. Even though it's disallowed in most databases,
03:48you'll still see in most cases where primary key is used, you'll say NOT NULL PRIMARY KEY.
03:55In fact, now that is not allowed in either place.
04:02So that's how primary key works.
04:04Primary key is often used to define the primary index for a table, and it
04:08creates an index where each value is guaranteed to be unique.
Collapse this transcript
Understanding how to use the INTEGER PRIMARY KEY function
00:00SQLite has a very easy, fast, and powerful feature for implementing ID fields.
00:06Every row of every table has a sequential ID called rowid.
00:10You can access this by selecting for it, using rowid, or one of its aliases, oid
00:17or rowid with underscores on either side.
00:19Let's take a look at how this works.
00:22Using the in-memory database, I'm going to create a table, and it'll just have
00:28a few general columns, and we'll insert some data into it.
00:42And the data doesn't really matter here.
00:43We'll insert a few rows, and then we'll select those rows.
00:51So, there we have our table, and there we have the rows for our table.
00:55Now, if I want to, in addition to the rows, and I can just name them here,
00:59I can select rowid (SELECT ROWID), like that.
01:04So if I if I just do it in lower case (rowid), it's exactly the same.
01:07ROWID is guaranteed to be unique, and it is also sequential, although under
01:15some circumstances
01:16it's not really guaranteed to be sequential; most of the time it is.
01:21Now you can access that. You can use rowid.
01:23You can say oid like this.
01:25You get exactly the same result, or you can say _rowid_ like that and get
01:33exactly the same result.
01:35Now that's very convenient and very easy, but it's also not terribly compatible
01:39with the way that other database systems work.
01:41So instead, I can define a column in the table and call it whatever I like and
01:46say INTEGER PRIMARY KEY, exactly like that.
01:51If I spell it differently or if I just say INT instead of INTEGER, if I do
01:55anything differently at all, it won't be the same thing. But what this does is
02:00this creates a column in the table, that's not really in the table.
02:04It works just like it were in the table, and it does everything exactly like it
02:09were in the table, except that it's really the rowid column.
02:12It's an alias for the rowid column.
02:14Now because I now have four columns in the table, I can't insert my values like this anymore.
02:19I now have to say a, b, c, like this, and I don't need rowid here anymore;
02:28in fact, I can just do that.
02:33Now I have an ID column which is this ID column here.
02:38It doesn't actually take up any space in the table.
02:40It's really just an alias for the rowid column that's already there.
02:44It doesn't cost anything extra on inserts, so unlike primary key, it's not
02:49creating a separate index.
02:50In fact, if I do SELECT * FROM SQLITE_ MASTER, you'll see that we just have the
03:00table there, and it doesn't actually even have a separate index.
03:04So this is fast, it's easy, and it's virtually free, and I include it, just on
03:09general principle, in every single table that I create, and I suggest that you do too.
03:14Now, a couple of interesting properties about this.
03:17If I were to come along here and delete a row, and I'll delete one from the middle,
03:26I'll delete row with the ID 3,
03:29then I'll go ahead and insert another row,
03:35You'll see that there is a hole in the middle, and the next one that it
03:37inserted was number 6.
03:39But if the one that I delete is number 5 because at this point, that's the
03:43highest one, I have one, two, three, four, five,
03:47then I insert another row,
03:49instead of 6 there, I'll give 5 because what it does is it uses the value one
03:54higher than the highest value in the table.
03:57Now that behavior can be changed by using the keyword AUTOINCREMENT.
04:00Now, you might be used to another database system, like MySQL, where autoincrement
04:09actually creates that automatically incrementing ID field, like Integer Primary
04:14key does in SQLite. In the case of SQLite, what AUTOINCREMENT does is it changes
04:19the behavior of the Integer Primary key.
04:22So, now instead of using the value that's one higher than the highest value in
04:26the table, it'll use the value that's one higher than the highest value that has
04:30ever been in the table.
04:32So in this case, while I have deleted number 5, it's still going to use number
04:366 for the next one.
04:39It's created another table here in the database called sqlite_sequence, which is
04:44just one column, one row, to hold that last highest value.
04:55Scroll down here, and you will see that it's got a 6 in it.
04:59Now if I were to insert another row here, and I will insert it here, and I will
05:06actually also put an ID in it and give it a value of 25.
05:15Now it's going to use 26 for the next highest value, and down here, we have a 26
05:19in that because the highest value that has ever been in that column was 25, and
05:24then when we inserted another one and so it became 26.
05:28So the behavior with AUTOINCREMENT is, quite simply, that the next value used for
05:33the ID will be one more than the highest value that has ever been in that table,
05:38and it uses the sqlite_ sequence table to keep track of that.
05:42So there is a cost to this.
05:43It's not a great cost, but if you need that behavior, it's available
05:46with AUTOINCREMENT.
05:48Id fields are very useful, especially in applications with several tables that
05:52interact frequently.
05:53Using SQLite's Integer Primary key feature, they cost virtually nothing.
05:57I strongly recommend that you use ID fields in most of your tables.
Collapse this transcript
11. Transactions
Understanding transactions
00:00Transactions are used to enhance reliability and performance in your SQL databases.
00:05SQLite is ACID-compliant, and that means that each transaction is atomic.
00:11It's either completed and committed, or it's entirely discarded.
00:15The database is always in a consistent state.
00:18Transaction data is isolated.
00:21It's not available until a transaction is completed.
00:25Completed transactions are durable.
00:27They will survive a system failure.
00:29SQLite accomplishes this by using transactions.
00:33A transaction is one or more operations that may modify the database.
00:38A transaction is only written to the database when it is complete.
00:42A transaction may be rolled back if it cannot be completed.
00:46The database is locked while a transaction is in progress.
00:50Transactions may be used to improve performance because one write is faster
00:56than a hundred writes.
00:57Transactions are used to ensure data integrity and improve performance.
01:01SQLite supports transactions using the BEGIN and COMMIT statements, or the PHP
01:06functions beginTransaction and commit.
Collapse this transcript
Using transactions in SQLite
00:00Transactions are used to ensure data consistency and reliability, and to
00:04improve performance.
00:06Let's take a look at some examples of how this is done.
00:08First, we're going to look at a traditional use of transactions, and I'm going
00:12to paste into this window here some SQL.
00:15This defines three tables: an item table, an inventory table, and a sale table.
00:21The item table has name of the item, description of the item.
00:25The inventory table has a link to the item table through item ID, and
00:30a quantity for inventory.
00:32The sale table has an item ID, quantity, and price.
00:37Now obviously, in real life, these tables will be a lot more complicated, but for
00:40illustration purposes, this will do the trick.
00:43Then we insert a few rows into the item table, and a few rows into the inventory
00:48table, and then we'll take a look at what we've got.
00:51So, here we have a join of the inventory, and item tables, showing what we have in inventory.
00:57We have 127 monitors, 12 external storage, 768 printers, and 42 tower computers.
01:05So, now let's make a sale.
01:07So the way a sale works is we will insert a sale into the sale table, and
01:12we will update the inventory table to update the quantity on hand for the
01:17items that we just sold.
01:19Now, you want to do this atomically.
01:22That means you want to do both of these transactions as one unit, so that if for
01:28some reason, the inventory table cannot be updated, you don't get a sale without
01:34an update to the inventory.
01:36So, either both of these things happen or neither of these things happen, and
01:40the way that happens is with a transaction.
01:42Start the transaction by saying BEGIN.
01:45That's the statement that begins a transaction.
01:47You can also optionally say BEGIN TRANSACTION, and SQLite will accept that, and
01:53that's not entirely standard.
01:54So, we'll just use BEGIN because that's the standard way to start a
01:58transaction in SQL.
02:00I'll say INSERT INTO sale.
02:02We're going to insert the item_id, the quantity, and the price, and give these VALUES.
02:12So the item ID will be 4, and that'll be the printers, the quantity will be 12,
02:19and the price will be 19295.
02:25So that's a price in cents because our price field is an integer.
02:30Now we'll update the inventory table, and we'll set the quantity equals, and
02:38here we'll use a subselect to get the existing quantity so that we can
02:41subtract from that.
02:43So we'll select quantity from inventory where ID equals 4, and we'll subtract 12
02:52from that, and where id equals 4 for the update as well. And then commit.
03:00And then we'll go ahead, and we'll look at the inventory table again.
03:05So I'll copy and paste this.
03:07And we'll also look at the sale table, and I'll paste that in over here.
03:12So, Go, and here we have our inventory table before the sale, and there is our
03:18inventory table after the sale.
03:20You can see that printer quantity is reduced by 12, and there are 12 items
03:24in the sales table.
03:26So, that's the way it works normally, but let's say that the inventory database
03:31is on another machine in another state, and
03:35that update fails because the Internet connection gets broken.
03:40So I want to comment that out.
03:42Our program, it detects that, that wasn't able to happen,
03:47so, we roll it back.
03:50So, instead of committing, we execute a ROLLBACK.
03:53We've got this INSERT INTO sale.
03:55We've already done that, but the inventory couldn't happen, so we rolled it back.
04:00Now what happens is the inventory does not get updated, and there is nothing in the sale table,
04:06because the transaction, the BEGIN and either COMMIT or ROLLBACK makes that
04:11whole transaction one thing.
04:13It's either all or nothing; it's atomic.
04:16It's either completed and committed, or the entire transaction is discarded.
04:21So, this is the traditional use of transactions.
04:25When people think of transactions, they think of something like this, of being
04:28able to group together a number of database actions that would otherwise make
04:32changes to your database, so that if any one of them fails, the entire thing is discarded,
04:39so you don't get an update to one table without a corresponding update to another.
04:44So, this is common in inventory applications.
04:46It's common in general ledger applications, and in things like that, where a
04:50number of things are tied together.
04:52There is one other use of transactions, which is equally powerful and equally
04:58important, and we're going to talk about that right now.
05:02This is just a small insert of a thousand records into a table.
05:07So I'm going to go ahead and take this entire file and select it.
05:11I'm pressing Command+A on my Mac.
05:13You could press Ctrl+A if you're on a PC.
05:16I'm going to copy it and paste it in here, and we're going to select the test database.
05:21We're going to go ahead and create that table and insert a thousand values. Go.
05:27So that took a little over a second, and now I'm going to do exactly the same
05:32thing, but I'm going to put it in a transaction.
05:33I'm going to say BEGIN at the top of it,
05:35and then I'm going to scroll down to the end, and I'm going to put COMMIT at the end.
05:41So, it was 1,061 milliseconds without the transaction, and here we go.
05:47It's 41 milliseconds with the transaction.
05:51The reason for that is that one write to the hard disk is much, much faster than
05:56a thousand writes to the hard disk.
05:58One big write is faster than a thousand small writes, and maybe this is so much
06:02data that it takes a handful of big writes.
06:05That's still much, much faster than a thousand small writes.
06:09So, these are the two major uses for transactions:
06:13First is atomicity to have a number of database transactions as one atomic
06:19transaction, to keep the database consistent and durable,
06:23and another is simply speed - performance, because writing a few times is a whole
06:29lot faster than writing a lot of times.
06:32Transactions are used to ensure data integrity and to improve performance.
06:36SQLite supports transactions using BEGIN and COMMIT statements.
Collapse this transcript
12. Subselects and Views
Understanding subselects
00:00Sometimes, when you're planning a query, you find yourself wishing that your
00:03data were organized differently, or just wondering how to get a piece of data
00:06that seems buried or just unavailable in the way that you want it.
00:10Subselects are often the answer in these circumstances.
00:14Subselects, or subqueries, are SELECT statements used in place of other
00:19expressions and data sources.
00:21Subselects are effectively nested SELECT statements.
00:24For example, in this query, the highlighted SELECT statement is nested within
00:30the outer SELECT statement, and the inner SELECT statement is actually used as a
00:34data source for the outer SELECT statement.
00:37Subselects may be nested at many levels.
00:40In this case, there is a subselect nested way inside there, and that's a data
00:45source for this outer SELECT, and that in turn is a data source for this
00:51outer, outer SELECT.
00:53Subselects may be stored as views, so that they may be easily reused.
00:59In this example, this SELECT statement is being stored as a VIEW
01:02called JoinedAlbum.
01:05Views may be used wherever a subselect may be used.
01:09In this case, this JoinedAlbum view is being used as a data source for this query.
01:14To see what views have been stored, you may query the SQLITE_MASTER table, like this.
01:20Subselects are a very convenient way of making your data available in different
01:23forms, while keeping your database schema simple and well organized.
Collapse this transcript
Creating a simple subselect
00:00To create a subselect, you first create the inner SELECT statement that gets
00:04you the data the way that you want it; then plug it into your outer query as a subquery.
00:09For example, imagine that we're getting data from an outside data source, and
00:12that data is packed into a very compact format.
00:15This is a common thing.
00:17This will often happen when data has to be transmitted from one place to another.
00:21It's often in a very compact format.
00:23In this particular table, what we have over here under the B column is we have a
00:29country code packed in with some kind of a useful number.
00:33We'll just call that a country value.
00:36So, if we wanted to just take those two values out and use that as a data source
00:41in another query, we could do something like this.
00:44We'll select the substring of b, and starting at the beginning for two
00:50characters, and we'll call that Country, and the substring of b, and starting at
00:59column 3 and for the rest of the string as country value.
01:05So we'll go ahead and we'll run this query, and we've created the table there.
01:11And we've got this little subquery here that we're going to use as a data source
01:15in our larger query.
01:17So I'll get rid of this, and now we'll build a query around this, and
01:21we're going to join it with our Country table, so that we can display the
01:25country names over here.
01:26It's a very simple application, but you can see from this, the technique of
01:31building a query to get you the data the way that you want to get it, and then
01:34using that as a data source in your outer query.
01:37So I'm going to select, I'm going to call this subquery as tt, so I'll be
01:44selecting from that, and then I'm also going to join it.
01:48So I'm just going to format this the way that I tend to format subqueries like this,
01:53so that it's very obvious to me how I'm using it.
01:57Then I'm going to do a JOIN from the Country table as co, and so now I know my
02:04aliases, and that makes it easy, ON tt.Country = co.Code.
02:12And this is actually Code2 in my country table because these are
02:16two-letter abbreviations.
02:19What I'm selecting from here, now that becomes easy because I have all of my aliases.
02:23I can say co.Name, that's the name from the Country table, and tt.CoValue, so
02:29that'll be the value from the tt table, like this.
02:33That's from my subquery, which I have aliased tt.
02:38So now I actually get something just like this, except with the country name.
02:42There we have country name, United States, United Kingdom, and France, and CoValue.
02:48So, here I took a query, and I got it just the way that I want it, with exactly
02:53the data, in exactly the format that I needed it for my outer query, and then I
02:57use that as a data source, even to create a join.
03:00This is a very simple technique for getting your data available in the form that you need it.
03:05So, creating a subselect is simply a matter of breaking down a problem into two or more parts.
03:10First, create your inner SELECT statement as a separate problem, and then create
03:15the outer statement that uses it.
Collapse this transcript
Searching within a result set
00:00When you have data from multiple sources in multiple formats and you need
00:03to use one data set to search another, you can use a subselect to perform your search.
00:08For example, if I have this data, which is coming from an outside source, and
00:12oftentimes data from outside sources, if it needs to be transmitted in some way,
00:17is often packed like this,
00:19and in this B column here I have a country code followed by some sort of a
00:25number that's associated with that country and code.
00:27I'm calling that a country value in this case.
00:30So I may want to use that to find out, what are all the cities in all of the
00:36countries within that code.
00:38The first thing I'm going to do is I'm going to create to SELECT that simply
00:41gets that code out of that, and that's easy to do with SUBSTR.
00:45So it's B, and it's from the first position, and its two long, and so if I run
00:52this query here, we'll see I now have just those country codes.
00:56And so I'm going to get rid of this part, and I can use that as an index to search
01:01through my local tables.
01:04So I'm going to have a SELECT, and I will be selecting stuff, and I'm going to
01:09say, where co.Code2 is IN this data set.
01:18And I'll go ahead, and I will format this like I normally format my subselects,
01:24and so my outside query is going to look something like this, co.Name AS
01:28Country, and the city name FROM city.
01:35Search my primary query, AS ci, and joined with country table, AS co ON
01:43ci.CountryCode = co.Code, and where the co.Code2 is in this list.
01:55So I'm using this list as the data source, and I'm searching through my existing
02:00tables based on what's in this list.
02:03And so this will give me a list of all the cities in all the countries
02:07where those country codes are included in this list from an outside source,
02:13and there's my query.
02:14So I have three. You will remember
02:16there was United Kingdom, there was France, and there was the US, and so there is
02:19United Kingdom, and there is France, and there is the US.
02:24So subselects are a very convenient method for searching one data set from
02:28another, and this technique is especially helpful when your data is coming from
02:31multiple sources and may be in a packed format.
Collapse this transcript
Searching within a joined result
00:00A subselect may be just about any SELECT statement, including a joined query.
00:04For example, we have a query here which is joining the track table and the
00:10album table and coming up with this result, which shows us artist and album and track.
00:14And you'll notice that the duration here is a number in seconds, and that's not
00:19really very readable for your average person.
00:21They want to see it in minutes and seconds, all nicely formatted with a colon in between.
00:25And we're going to use a subselect in order to do that.
00:29And so from this query here, the first thing we are going to do is we are going
00:32to separate out the minutes and seconds from this duration.
00:35So I'm going to put it over on this line by itself here, and I am going to say
00:39duration divided by 60 AS m, for minutes.
00:43And so that's an integer division, and it will leave off the remainder rather
00:47than turning it into a fraction.
00:50And then we're going to take t.duration again, and we are going to use the
00:53modular operator, which gives us the remainder of that division, and we'll call that seconds.
00:59So now we have a same query, but over here where it says duration, instead of
01:03that, we are going to have minutes and seconds.
01:05And there we have minutes and seconds, so that's already getting to be more readable.
01:09Well we would like to format that just right, and in order to that, we're going
01:13to take this query, and we're going to turn it into a subselect.
01:18So I'm going to go ahead and format this differently, and I am going to say FROM
01:23because we are going to use this as a data source.
01:29And we'll go ahead and indent this, as I like to do to keep things nice and clear.
01:36And we'll have a select, and one of the things that is going to be in the select is
01:39we are going to format these minutes and seconds the way that we want them with
01:42a colon in between and a leading zero on the seconds and things like that.
01:46So I'm going to say m concatenated with a colon, and that's the SQL
01:53concatenation operator like that.
01:55And then for the seconds, if the seconds are less than 10, I want a leading zero,
01:59so I am going to need to use CASE statement and so CASE in SQL is like if, then,
02:05else in other languages, so I am going to say CASE when S is less than 10, then
02:11we are going to use a leading zero,
02:14I have to have then in here, and that will be concatenated with the value of the
02:18seconds, else the seconds by itself.
02:23And we'll call that whole thing duration. And I'll also want, in my select, I
02:28want artist and album and track and track number.
02:36And so this trackno is the alias from over here in our subselect, and artist
02:41is this one from our subselect, and album is this one here, and track is this one here.
02:47So we have all these nicely aliases as in our subselect, and we can use those
02:51in our outer query.
02:53Then finally, I'm going to do in ORDER BY, and we'll make it artist and album
03:00and track number.
03:05And then we'll go ahead and run the query, and it looks like I've got a syntax
03:09error here some place. Yup, I forgot a comma right there.
03:14We go ahead and run the query.
03:15And here we have exactly the same results.
03:18They're ordered by artist, album, and track number.
03:21And over here in the fourth column, we have our duration, formatted exactly how we
03:25want it, with the minutes, and the seconds you have there nicely leading zero on
03:29the seconds that are less than 10.
03:31We have the colon and everything. And this is all accomplished because we
03:35formatted our data in a way that it works for us, in the subselect.
03:40And so we have the minutes in the seconds broken out, and that makes it easy for
03:44us to use the concatenation operator to build that string exactly how we want it.
03:50So subselects may be, and often are the complex queries.
03:53Feel free to construct your subselects in any way that makes sense for
03:57your purpose.
Collapse this transcript
Creating a view
00:00Anytime you want to reuse a query, you can create a view.
00:03A view is simply a saved query that may be used in place of a data source.
00:07For example, and we'll be doing this in the world database,
00:12here we have a table that we have been using in some of these examples in this chapter.
00:16And this table has packed a data in it, and it may come from another data
00:19source, where it's common to pack data.
00:22And we have a SELECT statement that has a number of substrings for extracting
00:27the data points out of that data.
00:29So if I select GO here, we see that we get this State, State value, the Country,
00:34and the Country Value from that table.
00:37If I want to save this query as a view, I simply say CREATE VIEW and give the view a name,
00:45and the keyword AS, and then I like to indent here, but that's certainly not required.
00:55And that will create a view named unpackData that will perform this query.
01:00So I'll go ahead and execute that, and now I can simply say SELECT * FROM
01:08unpackData, and I get that query.
01:14Now this data source can be used anywhere that you can use a data source.
01:18In fact, I can do something like this, and use it as part of a join, if I want to.
01:24
01:40And so now I'll be looking at this data.
01:42I'll be looking at the Country name from the Country table, and the Country
01:47Value from the unpackData data source.
01:52So this will be joined with the Country table, and so this query works just as if
01:56this were any data source, a table, or a subselect, or anything, and it's just as
02:04named query, stored as a view.
02:07So views are easy to create and easy to use.
02:10You may use a view wherever you would like to use a query.
02:13Views are especially useful in place of subqueries.
Collapse this transcript
Searching within a joined view
00:00There are many uses for views.
00:01One common use is to hide the complexity of a complicated subselect.
00:06For example, here we have a select that breaks out the minutes and seconds from
00:13a duration field that was originally entered in seconds, and here we have the
00:18result of it, and you can see that it separated minutes and seconds, and they're
00:22separated by this division and a corresponding modulus.
00:26So if we'd like, we can create a view based on this.
00:30So we just CREATE VIEW, call it JoinedAlbum, and I'd like to indent this.
00:41It's not required. And now we're creating a view with this query in it, and now
00:46we can use that view instead of that complicated query.
00:50We can simply say SELECT artist, album, track, and trackno, and then this now
01:01familiar making the minutes and seconds out of these separated values.
01:06
01:21So that takes the minutes, and it concatenates a colon, and then it
01:26concatenates the seconds with or without a leading 0, depending on whether the
01:31seconds is less than 10.
01:33Then we simply say FROM JoinedAlbum, and all the complexity of that is hidden and
01:40can be looked up if you want to.
01:43Here's our query, with the duration all combined as the minutes and seconds, like
01:48that, with the colon.
01:50If you want to see, if you forget, or you're looking at this after the fact, and
01:54you see that JoinedAlbum, you can SELECT from SQLITE_MASTER.
02:03I need a FROM in here.
02:04So, you can SELECT from SQLITE_MASTER, and you can see to CREATE VIEW statements
02:12and so you can see what that JoinedAlbum represents.
02:15So views are a useful tool in designing and using relational databases.
02:19When you have a complex subselect that you'll be using regularly, a view is a
02:24good way to hide that complexity and make your code clear and understandable.
Collapse this transcript
13. Triggers
Understanding triggers in SQLite
00:00Triggers are operations that are automatically performed when a specific
00:03database event occurs.
00:06Common uses of triggers include: preventing changes to a record after it's been
00:10reconciled, logging and auditing changes to a table, enforcing date and time
00:16stamps, enforcing business rules, and enhancing performance by maintaining
00:22derived tables in a format used for common queries.
00:25SQLite supports row-level triggers.
00:27These are triggers that are performed for each affected row.
00:31SQLite does not support statement-level triggers.
00:35Triggers can be notoriously difficult to debug.
00:39Effects of triggers almost always look like side effects,
00:43so documentation, logging, and careful design will go a long way toward
00:47preventing problems when debugging a database with triggers.
Collapse this transcript
Automatically updating a table with a trigger
00:00A common use for triggers is to force a table to be updated whenever a row is
00:04inserted or updated in another table, and this is usually done to enforce
00:08some business rules.
00:09For example, in this example we'll be updating the customer table with a
00:14last_order_id whenever a sale is made, whenever a new record is inserted in the sale table.
00:21So we're going to start with creating the tables and creating some customers.
00:26I'm just going to copy and paste this into SID.
00:31We'll run in, and we can see that we have this customer table that has three
00:35customers in it, and the last_order _id field is NULL in all of those.
00:40We've also created a sale table. Before we insert some sales though, we want
00:44to create a trigger.
00:45So the way this works is we use the CREATE TRIGGER statement, and we name the
00:50trigger newsale, and the trigger will happen AFTER INSERT ON sale.
00:55So first, the record will be inserted into sale, and then we will perform the
01:00statements in the trigger.
01:01So those statements in the trigger are bracketed by BEGIN and END, and there is
01:05a semicolon at the end of END.
01:07There's also a semicolon at the end of each of the statements inside the trigger.
01:13So there can be more than one statement in here; for the purposes of this example,
01:16there's just the one.
01:18So this statement will update the customer table, and it will set the
01:22last_order_id column to NEW.id, and I will get back to that in a moment, where
01:29customer.id = NEW.customer_id.
01:32So NEW is a pseudo-record.
01:36It's a virtual table that contains the values from the event that triggered the trigger.
01:43In other words, we've inserted something on sale.
01:47So that inserted row in sale is available as NEW in this virtual table.
01:53So the id field from sale will be this INTEGER PRIMARY KEY.
01:58So the id field will be the id of the newsale, and so that will be assigned to
02:03last_order_id, which is exactly what we want to have happen.
02:07Customer_id from the newsale row will be the customer.id that we want to update.
02:12So, we're saying where customer.id = NEW.customer_id. And when we're all
02:17done, we're going to go ahead and take a look at this trigger in the
02:21sqlite_master table,
02:22so you can see what that looks like.
02:23So I'm going to go ahead and create the trigger here.
02:25I am just going to paste this in after all of this.
02:28So we have to run this every time, because we're using the in-memory database.
02:32So each time you run it, it all starts with a fresh database.
02:35So I am going to insert that trigger there.
02:37I'll just make a little space here,
02:38so that is easy to see, and so that'll create the table, and then we're going to
02:42look at sqlite_master.
02:44We still will be displaying this, because that was already there.
02:46We're just going to be accumulating results down here.
02:49So sqlite_master now has the two tables that we've created, and it also has this trigger.
02:54So triggers appear in sqlite_master, and this is very important to understand.
03:00Triggers are a source of a lot of debugging headaches, because they create side effects.
03:05The things that happened as a result of a trigger always look like a side effect.
03:08So you might be looking at your database and going, "Oh my God, how come that
03:12table is getting updated," when you don't remember, or you didn't create this
03:16problem in the first place, and you're not familiar with the database schema.
03:20So looking at sqlite_master is a great thing to do, because you can see what
03:24your triggers are, and there's the whole trigger definition right there.
03:27So you can find out exactly what it looks like, and exactly what's going on in your database.
03:33So after we've created this trigger, let's go ahead and insert some sales records.
03:38So we're going to insert three sales, and then we'll take a look at the sale
03:42table, and we'll look at the customer table again, and we'll see what has
03:46happened to the customer table.
03:47So I'm going to paste this in, and we'll click Go.
03:51Now we have the sale table, which has the item_id.
03:55We don't have an item table yet, but it's got a item_id, customer_id, quantity, price.
04:01Look at our customer table.
04:02Now it has these last_order_id.
04:05So the first order, which is id 1, was given to customer number 3, and customer
04:09number 3 is Fred, and you can see he's got order_id 1.
04:12The second one is 2, went to customer number 2, and so we have that there.
04:17And the third one was 3, and it went to customer number 1, and so we have that there.
04:22So you can see that our customer table is getting updated based on the trigger.
04:28So this is getting executed every time we insert something in the sale table.
04:32Finally, just so that you know, when you're done with the trigger, obviously in the
04:36in-memory database it's not going to matter much, but you can say DROP TRIGGER
04:43and name it, and then I'll just take a look again at the SQLITE_MASTER table, so
04:51that we can see that the trigger has been dropped. I forgot the FROM.
05:04There you can see that the trigger is gone, because we executed the DROP TRIGGER.
05:10A trigger can be an excellent way to enforce business rules that require a table
05:14to be updated whenever another table is updated.
Collapse this transcript
Logging transactions with triggers
00:00Using triggers to log transactions can be a very useful tool for auditing
00:04purposes, and for debugging.
00:06For this example, we're going to create three tables: a customer table, a sale
00:10table, and a triggerlog table.
00:12We'll start by populating the customer table and selecting from it to show that
00:18we're successfully creating these tables and populating the customer table.
00:22So we'll go ahead and paste that into SID.
00:24We're using the in-memory database,
00:26so each time we run this example we'll have to create all these tables and
00:31populate them anyway.
00:32So there we see that we're successfully populating the customer table,
00:36it has these three records in it, and last _order_id is NULL for all three of them.
00:41So now we'll create the trigger.
00:42We start with the CREATE TRIGGER statement, and we name the trigger newsale, and
00:47this trigger will happen after insert, on the sale table.
00:50The body of the trigger has an UPDATE statement that updates the customer table
00:55and sets the last_order_id to the NEW.id, and NEW, of course, is the virtual table
01:01created by the trigger that's in effect while the trigger is being run that has
01:06the data from the row that's being inserted into sale.
01:10Then we have the triggerlog.
01:12Here we insert a row into the triggerlog table, and we set the timestamp, the
01:18events name, which in this case is UPDATE last_order_id - that's why we're naming
01:22the event that's being triggered - and the trigger name itself, which is
01:26newsale, the name of the table that's being updated, which is customer, and the NEW.customer_id.
01:33So we'll take this trigger definition, and we'll also inserts some rows
01:38into sale, and we'll select from all three of these tables so we can see what happened.
01:43I'll copy that and paste it into SID here and run it, and now we see we
01:50inserted these rows into the sale table, and that automatically updated the
01:55customer table, and here's our log.
01:58So this can be very useful, not just for auditing purposes, but of course the
02:02suits will love it because they have an audit trail that says exactly what
02:06happened in the database records,
02:09but also for debugging purposes for later on after you've designed a million
02:14other database and don't remember what happened here, or for somebody else who
02:17comes along to maintain this, you have a log of your trigger events, which will
02:22help you know what was certainly not a side effect and to be able to follow
02:27the path of execution and find out what's going on when you need to debug all of this.
02:32So triggers are a great way to ensure that transactions are logged properly for
02:36auditing purposes, and these logs can also be a very useful tool for debugging.
Collapse this transcript
Improving performance with triggers
00:00For a busy database it can be useful to set aside some tables for
00:03reporting purposes only.
00:05These tables can have data that is accessed frequently, in a format useful
00:09for your users, and that will direct heavy traffic away from the more critical tables.
00:14Triggers are an excellent way to keep these reporting tables updated.
00:18For our example, I am going to create a number of tables here:
00:22a customer table, a sale table item, an item table, and a report table.
00:28We'll populate the customer table, we'll populate the item table, and then we'll
00:33take a look at those. So, we'll get started by doing that.
00:35There is our customer table, with last_order_id as NULL, and there is our item table.
00:45Now that we have that, we're going to create a trigger.
00:48Now, this trigger is a little bit more busy than some of the other triggers that
00:52we have gone before.
00:54We'll call it newsale, and it's AFTER INSERT ON sale.
00:57The first statement will update the customer table and set the last_order_id.
01:02The second statement will insert into a report using a joined query as a data source.
01:07Then we'll go ahead and insert some records into sale, and we'll select from
01:12report to see what we get there.
01:14So, we'll copy and paste.
01:18So there is our trigger, and there is our sales, and there we go.
01:24Now in our report table we have item, we have the customer, the quantity, and the price.
01:31So the people in the sales department, or in whatever department need these reports,
01:37they have their own table, and they can query that as often as they want.
01:40They can query it in different ways that they want,
01:42they can make it busy,
01:43they can make it slow, and it's not necessarily going to impact the performance
01:48of the tables that get updated frequently.
01:51So using triggers, you can keep a separate set of reporting tables updated to
01:54improve performance, both for reporting and performance-critical tables that
01:58otherwise would be slowed down by reporting traffic.
Collapse this transcript
Preventing unintended updates with triggers
00:00Triggers are often used to prevent changes to rows that have already been
00:03reconciled, or should not be changed for other reasons.
00:05For example, we have here our customer table and sale table, and you'll notice
00:10this time the sale table has this extra reconciled column, and that's an integer,
00:15as SQLite uses the integer type for Boolean values.
00:19So it'll be a 1 or 0, representing true or false.
00:22So we'll go ahead, and we'll define these tables, and we'll populate them, and
00:25we'll display the sale table, so we can follow it.
00:31So here's our sale table.
00:32You'll notice the quantities are set at 5, 3, and 1 for ids 1, 2, and 3.
00:37Now, we're going to go ahead and define a trigger, and you'll notice that the
00:42CREATE TRIGGER statement this time says BEFORE UPDATE. In some of our other
00:46examples that we've been doing after insert,
00:48but in this case we want to trigger the trigger before the update actually happens.
00:54So this'll happen when you try to do an update, like these updates down here, and
00:59before the update is actually executed, this trigger will be triggered. So in
01:04this trigger we're selecting the RAISE function, which raises an exception, and in
01:08this case we're using the ROLLBACK exception so that the change will actually
01:13not occur, and we're returning a message along with the exception.
01:18The message says, 'cannot update table "sale."'
01:21We're doing this select from the table sale so that we can test the reconciled flag.
01:26So our select is WHERE id = NEW.id,
01:30so we're looking for the ID of the row that's going to be updated, and we check
01:35for the reconciled flag to be true.
01:37So if both of those conditions are true, if that reconciled flag is set to true
01:43for the row that's being updated, then we'll raise the ROLLBACK exception.
01:47So let's go ahead and add some updates to our script.
01:50We're going to update all three of these rows, and we're going to set all of the
01:53quantities to 9, just so that we can see what's going on.
01:56So we'll go ahead and add these updates.
02:01Why don't we put the trigger in there also? That might help.
02:04Put that trigger in there.
02:06So we've got our trigger defined, and we're going to update our rows.
02:10Now, you'll notice that we get this error for query number 12, and that's the
02:15update for id 2, and it says cannot update table sale.
02:19That's our message.
02:20So our trigger got triggered and our exception got thrown just for that row.
02:25So there are the three updates, setting quantity to 9 for all three of them, and here we have,
02:30in our results, we have quantity as 9 for the first row and for the third row,
02:34but the second row is unchanged, and that's because our trigger prevented it.
02:40So triggers are often used to prevent changes to tables or rows that should not be changed.
02:46It's a simple usage, and it's a valuable business tool.
Collapse this transcript
Adding automatic time stamps
00:00Triggers may be used to create timestamps in the table you're triggering from, as
00:04well as in other tables.
00:06We'll start by creating some tables here, and inserting some customers in
00:10the customer table,
00:11so we have a customer table, a sale table, and a log table that we're creating.
00:16Then we'll select from the customer table to see that that worked.
00:19We're using the in-memory database here,
00:21so we need to run this CREATE TABLE statements each time we run the queries,
00:27because this database goes away every time we use it. And we'll go ahead and run
00:31that query, and we see that the customer table is populated with three records:
00:36Bob, Sally, and Fred.
00:37And so now we'll create our trigger.
00:39Now this trigger is going to insert time and date stamps.
00:44We'll call this trigger newsale, and it's AFTER INSERT ON: sale.
00:47The first statement updates the sale table itself and sets the timestamp.
00:53The second one updates the customer table, and in addition to setting the last
00:58order id, it also sets a timestamp.
01:02The third one logs the event, and you'll notice that as we insert into the sale
01:06table, we're using a transaction here.
01:08We have BEGIN and COMMIT, and the reason for this is we're actually updating the
01:13row in the sale table right after we insert it.
01:17This can create some disk churning.
01:19So by putting this inside of a transaction, we will minimize the number of writes to disk.
01:25So this could otherwise be a bit of an expensive way to do this, but by
01:28putting it inside of a transaction, the performance should be fine for most circumstances.
01:34Then we will go ahead and select from all these tables, so we can see what happened.
01:38So will create the trigger here and execute it.
01:44So we have our three sales, and you'll notice that they have the timestamps in
01:48them. And the customer table now has these timestamp saying that was updated at
01:53this time and date, with the last_order_id.
01:57You'll notice, also, we've our logs, and this has the same timestamps.
02:00So we've exactly the same timestamp for all of these events.
02:04So you can use triggers to create timestamps in the table you're inserting or
02:09updating, and in other tables too.
Collapse this transcript
14. PHP Interfaces
Choosing an interface
00:01There are two interfaces available for using SQLite with PHP 5.
00:06The PDO interface, PHP Data Objects is a normalized interface that works across
00:12platforms, with many different database engines.
00:16The SQLite 3 interface is a native interface that mirrors SQLite's C
00:21language interface.
00:23So let's talk first about PDO.
00:26PDO is a normalized interface that works mostly the same for a number of
00:30different database engines.
00:32PDO is constantly being improved, tested, and debugged.
00:36It has a rich set of methods for handling all functions of SQLite, as well as
00:41some that are not supported by the SQLite 3 driver-specific interface.
00:46PDO's performance is comparable, and even better in some areas, compared to the
00:50driver-specific SQLite 3 interface.
00:53The SQLite 3 driver-specific interface is designed to work much like the native
00:58C language interface for SQLite 3.
01:01It is not a complete implementation of the C language interface, and it does
01:06not fully support exceptions for error handling.
01:09The PDO interface is clearly the superior interface for new code.
01:13Use SQLite 3 interface where required, or where it would involve too much work
01:18to convert existing code.
Collapse this transcript
Using the SQLite3 interface
00:00The SQLite 3 interface is the native driver interface for the SQLite 3 database engine.
00:06This a small test script, using the SQLite 3 interface with PHP.
00:12This is using my little sandbox framework. Down here in the main function,
00:16we have everything inside of a try and catch block, so that we can catch any errors.
00:22It's just worth noting that some errors using this interface don't get caught.
00:28So we instantiate the database like this: new SQLite 3 database, and the
00:33database constant here is defined up here with the path to the database, and
00:39make sure you put in your own path here instead of using mine, because it will not work.
00:45Then we use the dollar db object that gets created by the new call.
00:50That $db object is now the SQLite 3 interface.
00:54The exact method is used for executing SQL that doesn't require a result like
01:00this DROP TABLE IF EXISTS, CREATE TABLE.
01:03Then we'll put out a little message that we successfully created the table, and
01:07then we insert some values.
01:09Now, we use the Prepare to prepare a statement, and we use these placeholders,
01:14and all the different kinds of placeholders are available, and then we use the
01:17bindValue function on this statement handle. The prepare returns a statement
01:21handle, and the statement handle is now an object.
01:25We use the bindValue method, and you have to bind each value individually with
01:29a separate call, and then execute, and then bindValue, and execute and
01:33bindValue, and execute.
01:34So we are using this one statement with three different sets of parameters.
01:38So we're executing it three times, and the first one we're putting in a, b, c
01:43characters, and then second time we're putting in 1, 2, and 3 numbers, and the
01:47third time we're putting in one, two, and three strings.
01:51We call execute for each of those.
01:53Then we prepare a SELECT statement to simply select all of the columns in the
01:59table, and then we call execute, which prepares a result object, and then we need
02:05a While loop because result is not an iterator, to go through and call
02:09fetchArray for each row.
02:12So we run this, and it looks like that:
02:16Table t successfully created, and there is our result rows in the message call
02:21here for each of the rows in the While loop.
02:24So in a nutshell, that's how the SQLite 3 interface works.
02:29The SQLite 3 interface is a rudimentary interface.
02:32It doesn't have the feature set of the PDO interface, but it does closely match
02:37the native C interface for the SQLite 3 library.
Collapse this transcript
Using the PDO interface
00:00The PDO interface is a modern cross- platform interface, designed to make your job
00:05easier as you write your database applications.
00:08This is a little test script written with the PDO interface, using my sandbox framework.
00:15Up here at the top, you'll see we defined the database constant, and you'll want
00:20to make sure that you put your path in here, because using my path on your
00:23computer will not work.
00:25Down here, we have the main function, and the first thing we do is instantiate the PDO
00:32class into an object called $db, and all of this is inside of a try block so that
00:37we can catch exceptions.
00:39You'll notice that the very first line after that
00:42setAttribute(PDO::ATTR_ ERRMODE, PDO::ERRMODE_EXCEPTION);
00:47and what this does is it turns on exception handling for the entire PDO
00:51library. And it's pretty thorough. It works about 99% of the time, and occasionally
00:56there are errors that do not get caught.
00:58Then we use the exec method to DROP TABLE IF EXISTS and create the table, and
01:04then we'll send out a message that the table is created successfully.
01:08Then here comes the fun part.
01:09The prepare statement will prepare a query that can be used over and over, and
01:15here we use the question mark placeholders.
01:17Other types of placeholders are supported as well.
01:20That returns a statement handle, which is an object that has this execute method.
01:25The execute method will take an array with all of these positional parameters.
01:30I tend to use the question mark parameters;
01:32you can use named parameters, and in some cases that's really handy where you are
01:36going to have one value that's used in more than one place.
01:39So we execute that three different times, with three different sets of
01:43parameters. We have the characters a, b, and c, we have the numbers 1, 2, and 3,
01:48and we have the strings 1, 2, and 3.
01:50Then we go ahead and select.
01:52So, here we prepare another statement, and this one doesn't have any parameters.
01:56It's just SELECT * FROM t. We set the fetch mode to be an associative array.
02:03Then we run execute.
02:05After we run execute, that statement handle becomes an iterator.
02:09So we can just save foreach statement handle as row and get all of the rows.
02:15So you can see there is a lot of thought put into this library to make your job
02:18easier, and many of these things you can even expose as you create your own
02:23library, and we'll talk about that in a movie on creating your own library.
02:27So there is our script, and let's go ahead and run it, and there it is.
02:33Table t successfully created, and there are the rows, and those rows, again,
02:37using this iterator,
02:39it's very easy to step through those without having to read an entire table into
02:43an array, which is not going to work a lot of the time.
02:46So the PDO interface is rich and well-implemented.
02:50It's cross-platform.
02:51It's mature, modern, object-oriented implementation with many useful features,
02:56including iterators for stepping through results.
Collapse this transcript
Creating a library
00:00I strongly recommend that you create your own normalized database interface to
00:04make your job easier as a programmer.
00:06The PDO interface is mature, and well-written, but it's still not tailored to the
00:10way you work, or to your applications.
00:13Having your own interface will increase your productivity, and it will also
00:16serve as a starting point for those times when you need a library for a
00:19specific application.
00:21What we are looking at here is the test program for my interface that I call
00:27bwSQLite 3, and here you can see where it's included in this file.
00:31I want to start with this test script. Because this is where I start when I
00:35write these database interfaces, because I've written them in several
00:38different languages and at several different parts of my career, and sometimes
00:42even in a language where I already have one, I'll just throw it away and write
00:45a new one, because the way that I work changes, or just because I've evolved as a programmer.
00:51So this is one that I actually wrote this year in PHP.
00:54I had done this before in PHP a number of years ago, and then when PHP went from
00:58number four to number five, I just translated it a little bit.
01:03But I decided to sit down and do it again and to make it work the way that I
01:06work these days, which is much more object-oriented, and in my mind much more
01:10evolved as a programmer.
01:12So I start with the test script, because this tells me how I wanted to work, and
01:17then I build the library to fit the test cases.
01:19For example, when I instantiate the object, all I want to give it is a file
01:23name in a table, and I want both of those parameters to be optional, so that I
01:27can use setter getters to set them later, and I will show you that in the
01:30library when we get to it.
01:31I gave it a file name obviously, because SQLite 3 uses a file name as its database.
01:37I gave it a table, because I do have some simple CRUD operations that are in
01:42the library, that allow me to use associative arrays as records and pass those
01:46back and forth to the CRUD, and have it insert records and delete records, and do things like that.
01:51So I need a table name to be able to do that.
01:53Here I have a simple version method for getting the version, and I also if, I
01:57pass it the sqlite3 string, it will give me the version of sqlite3.
02:02Then we get into the SQL stuff.
02:04You notice I have sql_do, which is sort of a workhorse function.
02:08It's for executing SQL statements that don't require a return value, and so in
02:14this case, I drop table if exists, I begin transaction, and then I use sql_do
02:20again to create the table and insert some records.
02:23You'll notice that I've combined the functions of the PDO prepare and execute.
02:29So I can just pass it the SQL with the placeholders, and then pass it the
02:33replacement parameters and do that all in one statement.
02:37So this makes my job easier as a programmer.
02:39This is the kind of thing that I am talking about.
02:41Then I commit and I close the exception.
02:44I have a timer_start function, because I knew I was going to use that a lot in
02:48benchmarking and in testing my code as I write it.
02:53Then I test the table_exists function.
02:56I know that a lot of times when I am writing code, I just want to check if a
02:59table exists without having to do a create table if not exists.
03:05Here I am starting the timer again, because I want to test the speed of this
03:09query. And it's just a little select from the table, but you'll notice that the
03:14entire query is the iterator. And this is easy to implement, because I am using
03:19PDO, and I am just returning the iterator from PDO.
03:22But again, this is something that I can do all in one statement, and I can even
03:26put parameters in this, if I want to.
03:28It just makes my job easier.
03:30Really, that's the entire point of all of the choices that I made is I just want
03:35it to work in a way that makes my job easier.
03:37I have sql_query_row, which returns a single row for queries where I know that I
03:42am just getting one row back.
03:44I have sql_query_value for queries where I know where I am just getting one
03:47value back, and that returns a scalar value that's not an array at all.
03:52Now we get into some of the CRUD methods.
03:55Get_rec and I give it a record number.
03:58All of my CRUD methods depend on by having that integer primary key as the first
04:03column in the table named id. And so I say get_rec, and I am getting one
04:09record, and I am using it as an associative array.
04:13Get_recs with an s returns an iterator so I can use it in a foreach.
04:19Insert, I just pass it an associative array, and here I am creating one
04:23on the fly with the array function.
04:26Here I am using get_recs in a different way. Instead of using get_recs as the
04:30iterator, I use it to set a global iterator. It's just not an option.
04:34This is one that I had used in some legacy code if I was writing this
04:38completely from scratch, and not requiring it to be backwards compatible, I
04:42might not have done it this way.
04:43But that sth is a statement handle object, and it's just a function that returns
04:49a statement handle rather than doing it directly from the get_recs function.
04:53Update will update an existing record, and I give it the record number here
04:57and the record as an array there, and then I use get_rec again, and delete
05:03works the same way.
05:04Again, this is a CRUD function, and it just takes a record ID.
05:08So that's the test case, and here is the library.
05:12Let's go ahead and take a look at the test case running.
05:16I will go ahead and run test.
05:18There we see, again, it's using my sandbox framework, and it just gives me all
05:23of these results, so I can look at them and make sure that they're all working.
05:27If I was running a unit test, I might have used some sort of assertions to just
05:31give me messages that they succeeded or failed.
05:34But in this case, I really want to do experiment with it while I was building it.
05:38So, that was the purpose of this test script.
05:41So, here is the library itself.
05:43It creates a class called bwSQLite 3, and everything else is just methods within
05:49that class, or they are all public function.
05:51The constructor is very simple. It defaults using an in-memory database, and
05:56defaults to a NULL table name, and it sets up the PDO database object.
06:03You'll notice that it uses an alternate method of setting this error mode, using
06:08it as a fourth parameter to the PDO constructor.
06:12Then if I pass it a table name, it sets the table name, which is this private variable.
06:16I use private variables for these things.
06:18It's just one of those object-oriented habits that I have gotten into.
06:22If I want to access any of those variables, I'll use a setter getter;
06:25I don't like to have public variables in an object.
06:29Again, it's my own style.
06:30Feel free to use this as a basis for your own libraries, and to do them in your own style.
06:36So I have setter getters for the table name and a setter getter for a
06:40PDO database handle,
06:41so I can actually change the PDO database handle if I want to.
06:46A setter getter for a statement handle.
06:48If I wanted to I could set something up in PDO, pass it the statement handle,
06:51and then use my own CRUD methods.
06:54Then we get into the meat of it.
06:55Sql_do, this is the one that I probably use the most.
06:59You'll notice that it doesn't have any parameters here;
07:01instead, it uses PHP's func_get_ args, which allows me to have a variable
07:06number of arguments.
07:08I have one set up for multiple queries, which just passes a query to exact.
07:13This does not do any parameter substitution whatsoever. And then there is SQL
07:17query, which returns to the statement handle from PDO and allows it to be used as an iterator.
07:24query_all is a very dangerous one that actually calls fetchALL, and will give
07:28you all of the rows of the database in a variable.
07:31This is a great way to crash your program. Only ever use this if you know exactly
07:35how many rows you're expecting, and it's not a large number.
07:39Sql_query_row returns one row. sql_query _value returns one value. Then we have
07:47begin_transaction and commit. These are just passed through,
07:50but it's handy to have them.
07:52Finally, we get into the CRUD methods.
07:55The CRUD methods actually create the queries on the fly.
08:00So get_rec is very simple. get_recs is also very simple.
08:04Insert a record actually has to create the insert SQL.
08:09So it does it based on the keys and the values of the associative array
08:12that's passed to it.
08:14Likewise with update;
08:15it has to create that SQL query on the fly.
08:19Delete is very simple.
08:20It just deletes from the table name, based on the id that's passed to it.
08:25Get_next is a pseudo- iterator that is useful in an array.
08:29Again, this is legacy, so that it would work with some of my older code.
08:33count_recs is self-explanatory;
08:35it will just count the number of recs.
08:36It does select count.
08:38Table_exists actually looks in the SQLite master table with type set to table to
08:43see if it gets back a result.
08:46I did some experimenting with different ways of checking to see if a table
08:49exists, and this actually turned out to be the fastest way.
08:52You'll notice over here that I was timing these table_exists as I tried the
08:56different ways of doing it.
08:57Version just returns the versions.
08:59timer_starts sets micro time, and timer ends the timer.
09:03So that is my library.
09:06Again, I strongly suggest that you create your own.
09:09Feel free to use my bwSQLite 3 interface as a starting point in writing your own interface.
09:15Having your own interface can be very helpful for your own productivity, and the
09:19process of writing it can be educational as well.
Collapse this transcript
15. A Simple CRUD Application
Defining CRUD
00:00The term CRUD stands for Create Read Update and Delete;
00:03these are the four basic functions of a database system.
00:06This CRUD application is a demonstration of how those functions work in
00:10a relational context.
00:12This application also demonstrates the use of two tables that are related, in
00:16this case the Album table and the Track table. An album contains tracks and so
00:22an album has a one-to-many relationship with tracks.
00:26So when you first bring up the CRUD application, you get this screen.
00:29It counts the number of albums in the database and tells you how many there are.
00:33It displays the albums in the database, along with some information about each
00:37album, and it allows you to add an album, to edit an album, or delete an album.
00:42So those are the four functions, Create:
00:45add an album, Read:
00:47display the list of albums, Update:
00:50edit an album, or Delete, to delete an album.
00:54So, let's go ahead and add a new album to the database.
00:57We'll call this My New Album, and give it an artist name, and a label, and we'll
01:08give it a release date, and we'll go ahead and press Add Album.
01:17So we get the message, Album "My New Album" has been added, and you may now add tracks below.
01:22The cursor is put in the first field of this Tracks row, and we'll just start
01:26adding tracks, track number 1, "This is track number one," and give it a duration
01:34of 29 minutes and 42 seconds.
01:37I'm just going to press the Return key here.
01:40I could click on the Add button, but I'm not using my mouse.
01:43I'm just going to click on the Return key, and you'll notice that the track
01:47gets added, we have the message at the top, and the cursor is put in position
01:51for the next track to be added.
01:53So 2, "Another track for this amazing album," and 14 minutes and 27 seconds.
02:05And track number 3, we'll just put in one more, and give it a duration.
02:16Now, I'll just press on Done up here, and we have added an album to database.
02:24Here it is, My New Album, and we can edit it if we want to, and we could add another track.
02:35We can delete a track.
02:36Let's delete this second track.
02:38We get a confirmation page, and we can press Done, and there it all is.
02:44Now all of these changes are happening in the Album database, which, of course,
02:47is also available in SID.
02:49So, if I switch over here to the album. db and I select * from album, you'll see
02:58that we have a list of all the albums.
03:00ID for my new album is ID number 19, so I can say select * from track where
03:07album_id = 19, and there are all those tracks.
03:12So all this data is available in SID, so you can use SID to look at what's
03:17happening while you're operating on the database in CRUD.
03:21Now, if I delete this album, and I just press the Delete over here, I can
03:25delete the entire album,
03:27this will actually delete the album from the Album table and all of its tracks
03:31from the Track table.
03:32So I press Delete, and there it is.
03:35If I go into SID, I'll see that all those tracks are gone.
03:40If I select * from album, you'll see that that album is gone, too.
03:47So a CRUD application is a great way to learn about database operations.
03:52This application can be repurposed for any number of relational uses, or it can
03:56be used as a basis for further experimenting and learning about SQL relational
04:01databases, or database applications in general.
Collapse this transcript
Using PHP to open and use an SQLite database
00:00This CRUD application uses the PDO interface so that the same code can be used
00:04with either SQLite or the MySQL database management system.
00:08So you'll see one of the first things we do here up at the top of the
00:10application is we define a constant, called DBENGINE, and that is set in
00:15this case to SQLite 3
00:17instead of MySQL, if you are using this using this application with a MySQL database.
00:22Then for SQLite, we define DBFILE, which is the location of the database file
00:28in the file system.
00:29Be sure to edit this line and use the path to the database on your system, as
00:35this path will not work on your system.
00:37This is set up for this set for this system here in the recording booth.
00:41You'll notice that this is commented out, the for MySQL stuff.
00:45We don't need any of this USER, PASS, DB for the SQLite database.
00:51Moving on to the init function,
00:53this where we open the database.
00:56You'll notice I'm using a switch construct here, because I plan to support more
01:00databases in the future.
01:02I'll probably be adding support for Postgres next.
01:06Right now, it's got support for SQLite3 and for MySQL.
01:10So, here in this PDO constructor, this uses what's called a DSN format for the database.
01:18It first has the name of the database engine in their tables, which in this case
01:23is sqlite without a 3.
01:24If it were sqlite version 2, it would have a 2 there, and a colon, and then
01:29that's concatenated with, using the PHP concatenation operator with DBFILE, which
01:34is the path to the file on the file system.
01:38Then once we have constructed the PDO object, which is kept here in this dbh
01:44variable, we then use that to run PDO's sqliteCreateFunction method to create
01:51two functions and bind those to SQLite.
01:53Those are user-defined functions that are used here in this application, and
01:58one aggregate, the SUM_SEC_TO_TIME.
02:00So SEC_TO_TIME, TIME_TO_SEC, and SUM_ SEC_TO_TIME are user-defined functions.
02:05SUM_SEC_TO_TIME is an aggregate function.
02:07These are used for the track length in the track table.
02:12The track length is stored in the duration column of the track table, and it's
02:17stored as a number of seconds, and these functions convert back and forth
02:21between a time format.
02:22When we look at the CRUD application you'll notice one of the things you see
02:26here is the duration of the entire album. That uses the SUM_SEC_TO_TIME function.
02:32And you'll notice, if you do run this with MySQL, that doesn't show up at all,
02:37because that's only used for the SQLite version of this application.
02:42You will notice if we go ahead and edit one of the albums, you'll notice that
02:45these durations are displayed in minutes and seconds, even though in the database
02:51they're stored as number of seconds, and that conversion is done by this
02:55SEC_TO_TIME function.
02:57So this sqliteCreateFunction method allows you to use a PHP function to create
03:04user-defined functions that are used from SQLite.
03:07It's a terrific capability.
03:09It's one of the things I love about SQLite.
03:12So that's how we connect to the database using SQLite and PDO in this CRUD application.
03:18We'll talk about the rest of the four functions of the database: Create, Read,
03:22Update, Delete in the other movies in this chapter.
03:24SQLite makes it easy to open and use a database.
03:28This example shows how you can easily support multiple database engines for
03:32flexibility and scalability in your applications.
Collapse this transcript
Using PHP to insert into an SQLite database
00:00In this movie, we will look at how to use SQL to insert into a table in
00:04an SQLite database.
00:06Here around line 590 in the CRUD application, you will see there is a comment
00:11that says database interface functions and SQL.
00:14So I tried to, in this application, because it's a lot of code -
00:19there is about 1000 lines of code here -
00:20I've tried to section it off, so that certain types of things happen in
00:25certain areas of the code.
00:27So in this section of the code, this is where all the database
00:30interface functions are.
00:31And so you see we have the get_albums_ sql, the get_tracks_sql, and down here, we
00:37have the insert_album_sql.
00:39This is very simple, and it's a great place to start because we can look at the
00:42structure of it, rather than the details of it.
00:45So we define the query, and this is the actual SQL part of it.
00:49It's a simple insert statement, and you will see that it uses these placeholders
00:53for the data that's actually being inserted.
00:56This is a very important technique.
00:58By using this technique, instead of using variable substitution, in other words
01:03instead of just putting the variables in there from PHP directly, by allowing the
01:08interface to do the interpolation and the quoting of the data, then we are
01:14allowing the database engine to do what it does best. And one of those things is
01:18to protect us from SQL injection attacks and things like that, by using the
01:22facilities that are provided to us for the variable substitution.
01:26So it's really valuable to do it this way.
01:29It's important that you do it this way as often as you can, instead of doing
01:33something like having a quoted string, and having your INSERT statement, and
01:48then actually putting the data in using quotes and using the variable
01:53substitution that comes from PHP, like doing something like that.
01:58So it's really important that you do not do something like this where you
02:01type the SQL into a double-quoted string, and actually put the variables in
02:07the string, like here.
02:13If you do it this way, then you are a lot more exposed to the security risks on
02:17the Internet, allowing people to do things like create SQL injection attacks and
02:22other nefarious activities to exploit your code.
02:27This is why we use variable substitution in the database engine.
02:31These question marks are replaced in the PDO driver and also with the facilities
02:37of the database code itself, which is designed to protect us from the threats and
02:43the exploits that are out there in the wild.
02:46So it's really important to use this technique and not the language
02:49variable substitution.
02:51The Prepare method in DBO, it takes the query and prepares it, and then the
02:56Execute method actually does the variable substitution.
03:00So here we pass in an array, and we pass in all of these variables from the
03:05screen, and let the database drivers do their job in replacing them into the
03:10places of these placeholders in the SQL.
03:13Finally here, we check for errors, we report the errors if we have to, and
03:17finally, the lastInsertId method which returns the ID of the row that was just
03:22inserted into the table, and allows us to operate with the relational
03:27properties of the database.
03:29This is the code for insert_track_ sql, which is pretty much the same.
03:33You see we use the same variable substitution.
03:36It's very, very similar code, and it also returns the lastInsertId.
03:39So you'll notice that this code is not different for SQLite and MySQL.
03:45SQLite uses standard SQL.
03:49There are some cases you will see, in the rest of this code, where we do have to
03:52use different SQL for SQLite.
03:55But in most cases, the same SQL is going to work across database engines.
04:00That makes it very easy for us to support scalability, because SQLite is an
04:05excellent choice for applications that will run on one server at a time.
04:09But with PHP and PDO, it makes it easy to support multiple database engines in your
04:14application for flexibility and scalability.
Collapse this transcript
Using PHP to read from an SQLite database
00:00Let's take look at how to use PHP and SQLite to read from tables in the database.
00:05In the get_albums_sql function,
00:08this is really very simple,
00:09it has a simple select and prepare and execute.
00:14There's really not much to this one.
00:16In the get_tracks_sql, this one is a little bit more complicated.
00:21You will notice that there are two different queries for MySQL and for SQLite.
00:27The MySQL version uses this proprietary MySQL function CONCAT_WS to build the time string.
00:34SQLite uses our user-definable function SEC_TO_TIME to build the time string.
00:41The problem is, if we look in the database, and here is a set of tracks from the
00:45database, and you will notice that the duration is stored in number of seconds,
00:50and we have displayed it in CRUD, here is the same data in crud.
00:54You will notice that it's displayed in minutes and seconds.
00:57So that needs to be converted.
00:59The data is stored in one format, and displayed in another.
01:01So MySQL uses this proprietary string function to do it, which is certainly one
01:06way, and you will notice how much cleaner the code is for SQLite.
01:09So we use our user-definable function SEC_TO_TIME.
01:13So if we take a look at that function, that's actually a PHP function, we go up
01:18to _init, we will see that it's registered with SQLite, using the sqliteCreateFunction.
01:25The first argument is the name of the function, for SQL purposes; the second
01:29argument is the name of the function in PHP; and the third argument here tells
01:35how many arguments there are.
01:38If we go down here to the actual function in PHP, we see that it's a very
01:43short piece of code.
01:45It simply takes the number of seconds, make sure it's an integer, does a little
01:49bit of math on it, separates out the minutes and seconds, and returns it using
01:54sprintf to return a formatted string.
01:58So if we look again at the get_tracks_sql, that function is called right here in
02:05the SQL, and it makes the entire query very simple to understand, very clean.
02:11So SQLite, with its flexible user-defined functions facility, makes it easy to get
02:16data from your table in a format that fits your application.
Collapse this transcript
Using PHP to update an SQLite database
00:00Now let's take a look at how to use PHP and SQLite to update tables in a database.
00:05This is really the simplest of all the database operations in this application.
00:10It's simply a matter of submitting the standard SQL, which is exactly the same
00:14for both the MySQL and SQLite engines.
00:18You will notice we are using the placeholders here again:
00:201, 2, 3, 4, 5, and so we have the 1, 2, 3, 4, and 5 arguments, and those are all
00:31passed as an array to the execute method.
00:34That's all there is to it for the album.
00:36The track SQL is virtually the same.
00:39We have four arguments here:
00:40the track number, title, duration, and the ID for updating the correct row in
00:47the table, and that's really all there is to it.
00:50The Update function is the simplest function in the application.
00:53It's really just a matter of submitting a simple update query to the database engine.
00:57SQLite and PHP make it easy to perform simple operations like this.
Collapse this transcript
Using PHP to delete from an SQLite database
00:00When deleting records from a set of related tables, you need to make sure that
00:04the subordinate rows are deleted, as well as the primary rows.
00:07First, we'll take a look at deleting tracks, because this is very simple.
00:11There aren't any subordinate rows involved.
00:13When you delete a track, you simply delete the track where the track ID is a particular row.
00:20Deleting albums is a little bit more complicated because you want to make sure
00:24that the tracks for the album are deleted as well.
00:28So we have two queries here.
00:29We have the delete tracks query, which deletes the tracks where the album_id is
00:34the ID that's been passed to function.
00:36Then delete the album after that.
00:38So first we delete the tracks, because these are the subordinate rows.
00:42Once the album has been deleted, those tracks will be orphaned, and it would be
00:46a challenge to clean them up afterwards.
00:48So first we delete the tracks.
00:50This way, even if the application is interrupted in the middle of deleting
00:54the tracks, they're not completely orphaned, because the album record is still there.
00:59After we've deleted the tracks, then we can delete the album.
01:03Deleting rows is a little bit more complicated when there are related tables involved.
01:07This CRUD application provides an example of a simple solution to this problem.
Collapse this transcript
16. An Application for Web Site Testimonials
An overview of the testimonials application
00:00The Testimonials application is an example of a real-world application that uses
00:04many of the techniques covered in this course to leverage the power of SQLite
00:08and PHP, to create significant functionality with a minimum of code.
00:13This is what the Testimonials application looks like on a web site.
00:16I actually use this on my web site, this exact same application, to show testimonials.
00:20Whenever somebody sends me an e-mail or something telling me that they like my
00:24work, I'll take an excerpt of it, and I'll drop it in the database.
00:28There would be testimonials that will show up on the page.
00:31So every time you hit Reload, you get a different set of quotes.
00:35For our purposes here, I've just put some witty sayings in there,
00:38but this is actually an application that I use on my web site.
00:42At the backend of the application - that's this db.php, this is where you can
00:47enter and edit the testimonials.
00:49It's got a little paging feature here, so you can show just so many of them on
00:53the screen at a time.
00:54I've got it set to five at a time, because we have limited screen real estate
00:58here for the training purposes. Bottom line, I've got it set to some higher
01:01number, and so you can go directly to the page, or you can page back and forth like this.
01:07You want to add a new testimonial,
01:09you just put in something witty here,
01:10you say "Something witty or inspirational," and you put in a Byline, so this was said by me, and you add it.
01:21Then you see it shows up down here.
01:23These are alphabetical by the author.
01:26If you want to edit it, you press Edit, and say "Something else," and say Update. It updates there.
01:34It's very simple. If you want to delete it, you press Delete.
01:37You get a little confirmation page here.
01:38You press Delete, and it's gone.
01:41So Create, Read, Update, Delete;
01:43it's the fundamentals of database programming.
01:46So, this is the backend.
01:48This is the way you manage the Testimonials database, and the testimonials
01:53themselves - I have to get at it this way,
01:57the testimonials themselves are displayed with this testimonials.php.
02:01You see that this just brings up raw HTML.
02:04If I do the View Source on the browser here, you see it's got these class
02:09attributes, so that it can be formatted with CSS.
02:12That's exactly what's showing up in the test page that has the Lorem ipsum.
02:17It's exactly the same HTML there, but it's just formatted with the CSS.
02:21If you look in the Testimonials folder, in the example files, you see the layout of it here.
02:27You see testpage.php.
02:29If I open that up in my editor, you'll see the testimonials gets included here
02:35in this one line of PHP.
02:36The rest of this page is just HTML.
02:38It just gets included with this include statement.
02:43The number of testimonials to display is set with this variable here.
02:47So it chose three testimonials at a time.
02:49Then the db.php, we'll look at that in another movie,
02:54that is the code for the backend, which is 300 and something lines of codes. It's very small.
03:00The testimonials.php, we'll look at it in another movie.
03:03It's also much, much smaller than db.php.
03:07That just displays the testimonials.
03:09In the lib folder is the bwSQLite 3.php library.
03:14We are using that for this application.
03:17That's part of why it can be as small as it is, because it uses the CRUD features.
03:21In the data directory is the testimonials database.
03:25If you are on a Mac or on a UNIX system, you want to make sure that this data
03:28directory has Read & Write permissions for everyone, like it does here, and also
03:35that the testimonials.db file has Read & Write permissions for everyone.
03:40That's really important so that the application can write to the database.
03:46Both of those, the directory and the database itself, have to have Read & Write
03:49permissions for everyone.
03:51Then the assets folder has little snippets of HTML, like for instance this one
03:56here, which are used to assemble the pages for the backend.
04:04The main page is right here.
04:05It is just very small, and it's got the space for the content on it.
04:10It's just a lot of variable replacement, placeholders.
04:15So, that's the structure of the application here.
04:18So you take all of this stuff,
04:20you drop it on your web site,
04:22you edit the paths in db.php and testimonials.php, and include it in your web
04:29site using this testpage.php as an example, and
04:33this thing will run on your web site.
04:35So the Testimonials application demonstrates how to use the techniques covered
04:39in this course to create a small and functional database application.
04:43You may use this as a starting point for your own small database applications.
Collapse this transcript
Managing the database in PHP
00:00The db.php application represents the database backend.
00:04This is where the database is maintained.
00:07So, here at the top of the file, we set some constants:
00:10the TITLE, VERSION, things like that.
00:13The PAGELIMIT is set here.
00:15This is something that you might want to change.
00:17This is the number of items that are displayed on the page in the
00:20Testimonials database here.
00:22The bwSQLite 3 library is included.
00:27This application does use that library to keep the database operations simple.
00:31You'll need to define the location of the database.
00:34If you use the same structure, you probably won't even have to change that name.
00:38This is the name of the table for the database itself.
00:42Down here in the init function is where the initialization happens.
00:48Variables are set up and initialized for the page display engine.
00:53The database itself is constructed using the bwSQLite 3 constructor.
00:58It just takes the database name, which is the file name and file location, and the TABLENAME.
01:03That's really all there is to that.
01:06The database routines themselves start here with add.
01:10You'll see they're all very simple, because we're using the CRUD interface in
01:14the bwSQLite 3 library.
01:18So we populate an associative array, and pass that rec on to the CRUD routines.
01:24This one inserts a record.
01:28This one here gets a record for confirming a delete.
01:34This one here performs the delete based on the ID.
01:37So these are all very simple because we're using the CRUD interface.
01:42Here is the update.
01:43It populates an associative array, and passes down along with the ID to the update method.
01:50And edit, just like delete, it uses the ID to get a record and displays that for editing.
01:57Then the real work of the page display is done here in listrecs.
02:02This is what creates this area of the page down here, with its paging.
02:07You can go forward a page, back a page.
02:09You can jump to a page.
02:12That's all done in this routine here.
02:15It first takes a count of the records.
02:20It does some calculations to decide how many records we have, how many we're
02:25going to display, and which page we're on, and so which records will be
02:29displaying on the page.
02:30It's a really very simple arithmetic to figure that out.
02:34Then it constructs a very simple SELECT statement using LIMIT and OFFSET.
02:39This LIMIT and OFFSET is nonstandard SQL, but it uses the same syntax as MySQL
02:45uses for this same function.
02:47LIMIT says how many records to return for a SELECT statement, and OFFSET says
02:52how many off from the beginning of the set to offset those number of rows that
02:58you're going to return.
02:59Then that's passed into sql_query using the calculations that we got earlier.
03:06sql_query returns an iterator.
03:09It returns a PDO statement object, which can then be used in this foreach loop,
03:16so the call is actually right there in the foreach loop.
03:19That makes the syntax very simple and compact.
03:23That's really all that's involved.
03:25I mean, there is some other support code here for displaying the pages
03:28and things like that,
03:29but that's the meat of how this is done. It's very simple.
03:33It's very straightforward.
03:34Having a library that does the basic functions that you need to do commonly in
03:40your style of coding is a real great asset.
03:43It helps to keep the line count down.
03:44It helps to keep the code simple and compact enough, so that the logic of what
03:49it is that you're doing is prominent.
03:51It just makes it easy to code and to maintain.
03:54So the db.php application is the heart of the Testimonials database.
03:59This is an example of how you can create functionality and usability with
04:03a minimum of coding.
Collapse this transcript
Displaying the testimonials using PHP
00:00This is the part of the application that displays the testimonials on the web page.
00:04Because the work has already been done in other parts of the application, this
00:07part is remarkably simple.
00:09So, all this does is it displays these quotes, or testimonials, on the screen and
00:13every time the page is reloaded you get a different set of them.
00:17Here's the code that does that.
00:18It's just very short and sweet.
00:20It's just 90 lines of code, including comments, and it starts with a set of
00:24constants, just like the backend does, and it calls init, which in this case is very short.
00:30It really just instantiates the database using bwSQLite 3, so that makes it
00:35really easy. And the bulk of the work is done here in the main function.
00:41So, it grabs RECCOUNT from the host PHP file, and if we look at the testpage.php -
00:47this is where that Lorem ipsum is,
00:49you'll see that the testimonials. php is included here. And before it's
00:53included RECCOUNT is defined, and that gives us the number of records to
00:59display on the page at a time.
01:02Then over here in testimonials.php, we define that as a global so that we can get
01:06that from the enclosing PHP namespace, and then we check to see if it's set. If it is, great;
01:13if not, we default to 3.
01:15We check to make sure that we've got the database.
01:20Then we query the database, getting all of the IDs, and we loop through this
01:24query, because sql_query returns an iterator.
01:27We can put it right there in the foreach.
01:30We loop through that and populate our idlist with all of the ids from the
01:34database. Very quick and very simple.
01:37So, we check out how many we have.
01:40We make sure that we have a number that we can use. Given the RECCOUNT, you want
01:44to make sure that there is enough records to give it decent randomness. And the
01:49meat of it is right here.
01:50We use the array_rand function from PHP.
01:53This is really handy little function.
01:55PHP has a rich set of functions for dealing with arrays, and this one will give
02:00us a set of random keys from an array.
02:03You tell it how many you want, and it will give you that many random keys from the array.
02:07So, you want three of them,
02:08we grab those, and then we loop through that resulting array of keys, and
02:14we call printrec for each of the IDs that we grab from our idlist using those keys.
02:19Printrec simply displays a record from the database.
02:23So that's all there is to it.
02:25It's really very simple and very short.
02:28The entire Testimonials application is an example of a simple and usable tool,
02:33written with a minimum of code, using SQLite 3 in PHP.
Collapse this transcript
Conclusion
Goodbye
00:00In this course, my goal was to give you a good understanding of SQLite so you
00:04can use it with PHP to build powerful and compelling applications for yourself
00:08and for your clients.
00:10I've covered the basic features of SQLite, its unique data model, its
00:14functions, its use of SQL, and
00:16I've walked you through some real PHP applications that use SQLite, so you can
00:20apply your knowledge to building your own projects with SQLite and PHP.
00:25I've really enjoyed creating this course for you, and I sincerely hope that it's
00:29as useful for you as it has been fun for me.
Collapse this transcript


Suggested courses to watch next:

SQL Essential Training (2h 22m)
Bill Weinman

MySQL Essential Training (2h 46m)
Bill Weinman


SQL Server 2008 Essential Training (6h 54m)
Simon Allardice


Are you sure you want to delete this bookmark?

cancel

Bookmark this Tutorial

Name

Description

{0} characters left

Tags

Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading
cancel

bookmark this course

{0} characters left Separate tags with a space. Use quotes around multi-word tags. Suggested Tags:
loading

Error:

go to playlists »

Create new playlist

name:
description:
save cancel

You must be a lynda.com member to watch this video.

Every course in the lynda.com library contains free videos that let you assess the quality of our tutorials before you subscribe—just click on the blue links to watch them. Become a member to access all 104,141 instructional videos.

get started learn more

If you are already an active lynda.com member, please log in to access the lynda.com library.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Get access to all lynda.com videos

You are currently signed into your admin account, which doesn't let you view lynda.com videos. For full access to the lynda.com library, log in through iplogin.lynda.com, or sign in through your organization's portal. You may also request a user account by calling 1 1 (888) 335-9632 or emailing us at cs@lynda.com.

Access to lynda.com videos

Your organization has a limited access membership to the lynda.com library that allows access to only a specific, limited selection of courses.

You don't have access to this video.

You're logged in as an account administrator, but your membership is not active.

Contact a Training Solutions Advisor at 1 (888) 335-9632.

How to access this video.

If this course is one of your five classes, then your class currently isn't in session.

If you want to watch this video and it is not part of your class, upgrade your membership for unlimited access to the full library of 2,025 courses anytime, anywhere.

learn more upgrade

You can always watch the free content included in every course.

Questions? Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com.

You don't have access to this video.

You're logged in as an account administrator, but your membership is no longer active. You can still access reports and account information.

To reactivate your account, contact a Training Solutions Advisor at 1 1 (888) 335-9632.

Need help accessing this video?

You can't access this video from your master administrator account.

Call Customer Service at 1 1 (888) 335-9632 or email cs@lynda.com for help accessing this video.

preview image of new course page

Try our new course pages

Explore our redesigned course pages, and tell us about your experience.

If you want to switch back to the old view, change your site preferences from the my account menu.

Try the new pages No, thanks

site feedback

Thanks for signing up.

We’ll send you a confirmation email shortly.


By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

Keep up with news, tips, and latest courses with emails from lynda.com.

By signing up, you’ll receive about four emails per month, including

We’ll only use your email address to send you these mailings.

Here’s our privacy policy with more details about how we handle your information.

   
submit Lightbox submit clicked