IntroductionWelcome| 00:04 | Hi! I'm Bill Weinman, and welcome to
SQLite 3 with PHP Essential Training.
| | 00:10 | In this course, we'll explore the world
of SQLite 3, a full-featured, relational
| | 00:14 | database engine, entirely
self-contained in a driver.
| | 00:17 | SQLite has really changed the way
people think of a database engine.
| | 00:22 | It's a small, fast, reliable, ACID-
compliant, fully transactional database
| | 00:27 | engine that can run a busy
web site, or fit in a phone.
| | 00:31 | I'll explain the major features of
SQLite, and its usage in a PHP environment,
| | 00:36 | focusing on real-world examples to aid
you in implementing this amazing little
| | 00:41 | database engine in your applications.
| | 00:43 | I'll show you the details of its SQL
usage, core and aggregate functions, its
| | 00:50 | unique manifest typing scheme, and even
how to create user-defined functions in PHP.
| | 00:56 | I'll demonstrate how to use
subselects, views, and triggers to improve
| | 01:01 | performance, compliance, and
security in your database applications.
| | 01:05 | This course is a great introduction
to SQLite 3, but some programming and
| | 01:10 | database experience will really help you
get the most out of these technologies.
| | 01:14 | I've been a programmer for more than
30 years, and I have rarely been this
| | 01:18 | excited about a database engine.
| | 01:20 | So, 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:00 | SQLite is a full-featured, relational
database engine, entirely self-contained in a driver.
| | 00:05 | SQLite is a complete
implementation of ANSI standard SQL.
| | 00:10 | It is a fast, reliable, ACID-
compliant transactional database engine.
| | 00:16 | SQLite is well suited for web,
desktop, or embedded applications.
| | 00:21 | SQLite is not a database server.
| | 00:24 | It is a fully self-contained
database management system in a driver.
| | 00:28 | That means you don't need login credentials.
| | 00:30 | You don't need to connect to anything.
SQLite opens a file and uses it as a database.
| | 00:37 | In order to get the most out of this
course, you will need some understanding of
| | 00:41 | programming in PHP or
another similar scripting language.
| | 00:45 | You'll need some understanding
of relational databases and SQL.
| | 00:49 | You'll need a web server with
PHP 5.3 or later and SQLite 3.6 or later.
| | 00:56 | Keep in mind that SQLite 3.6 comes
with most current installations of PHP.
| | 01:01 | You will need a text editor;
| | 01:04 | a word processor will not work for this purpose.
| | 01:06 | A word processor saves formatting
information along with the text in its files,
| | 01:11 | and that won't work for writing code.
| | 01:13 | The XAMPP package is used for
demonstrations in this course.
| | 01:17 | You do not need XAMPP in order to
do the exercises in this course;
| | 01:21 | you may use any server with PHP and
SQLite, but XAMPP is convenient for testing
| | 01:26 | code on your desktop.
| | 01:28 | XAMPP runs on most modern operating
systems, including current versions of
| | 01:32 | Windows and Mac OS X, and it includes
the latest versions of PHP, PDO, and
| | 01:38 | SQLite, all used in the examples in this course.
| | 01:42 | Installation instructions for XAMPP
on Windows and Mac OS X are included in
| | 01:46 | this course as well.
| | Collapse this transcript |
| Using the exercise files| 00:00 | If you're a premium member of the
lynda.com Online Training Library, or if you're
| | 00:04 | watching this tutorial on a DVD-ROM,
you have access to the exercise files used
| | 00:09 | throughout this title.
| | 00:10 | The ExerciseFiles folder for this
title is not going to be used from the
| | 00:14 | desktop. Please follow the
instructions in the installation movie for
| | 00:19 | your operating system.
| | 00:20 | The layout of the ExerciseFiles
folder is like this. The lib and assets
| | 00:26 | folders, those are used for the sandbox
environment that's used for some of the
| | 00:30 | examples in this course.
| | 00:33 | Some of the chapter files have PHP
scripts, and when they have PHP scripts they
| | 00:38 | will usually be a start.php or
something like this, create-start.php.
| | 00:44 | These are the starting
points for those exercises.
| | 00:47 | Make a copy of that starting
point script and work from the copy.
| | 00:51 | That way you always have a place you can
fall back to, if you need to get started again.
| | 00:55 | The end files are there for
comparison, to see if your results match mine.
| | 01:00 | Some of the chapters have
simply an examples.sql file.
| | 01:05 | These files have snippets of SQL that
are the same, or similar, to the examples
| | 01:11 | that I'm typing into the screen, as
we're doing the exercises in that chapter.
| | 01:15 | You can use these to copy and paste or
to see if your results don't match mine.
| | 01:21 | The folders CRUD, SID, and
Testimonials contain complete applications.
| | 01:27 | The Testimonials application is an application
for displaying testimonials on your web site.
| | 01:32 | The CRUD application is a demonstration
application of the four basic functions
| | 01:37 | of a database: create, read, update, and delete.
| | 01:41 | And the SID application is used to
demonstrate SQL statements throughout this course.
| | 01:46 | The SQL folder contains the SQL used
to create the databases that are used as
| | 01:51 | examples in this course.
| | 01:53 | Each of these files is an SQL script,
which is used to pipe into the SQLite 3
| | 02:00 | command line application
to create the databases.
| | 02:04 | If you prefer, the complete working
databases are available in these .db files,
| | 02:10 | and you do not need to create them from scratch.
| | 02:12 | One of the features of SQLite is that
its database files are cross-platform, so
| | 02:17 | these same files will
work on any operating system.
| | 02:21 | If you don't have access to the
exercise files, you can follow along from scratch
| | 02:24 | or with your own assets.
| | 02:26 | So let's get started.
| | Collapse this transcript |
|
|
1. Quick StartGetting the most out of the quick start| 00:00 | If you're a developer with experience in
PHP, or a similar scripting language, and
| | 00:05 | perhaps a different database, this Quick
Start is designed to help you get up and
| | 00:09 | running with SQLite quickly,
and with a minimum of detail.
| | 00:13 | The examples in this Quick Start use
the bwSQLite 3.php library to interface
| | 00:20 | with SQLite 3 from PHP.
| | 00:22 | This is a library that I wrote that
uses PDO, PHP's unified database interface.
| | 00:29 | I strongly recommend that you write
your own library with your own interface
| | 00:33 | that works well for you.
| | 00:35 | In the meantime, feel free to use this
one for the Quick Start and perhaps as a
| | 00:40 | basis for your own library.
| | 00:42 | The exercises in this Quick Start use
this sandbox framework that I created
| | 00:46 | for the purpose of demonstrating and
experimenting with SQL and SQLite in the PHP environment.
| | 00:53 | This is what it looks like in the editor.
| | 00:56 | We're going to start at the bottom here,
and we're going to look at these functions
| | 00:59 | here: message, error_message and error,
and what these do is these display those
| | 01:05 | messages that you see on the screen in
the sandbox environment in the browser.
| | 01:11 | Coming back up to the top, you'll notice
we have some constants to find.
| | 01:15 | We read in the bwSQLite 3 library with
the require_once directive, and we call
| | 01:21 | init, main, and page.
| | 01:23 | init is used for setting up the database
and setting up the variables and arrays
| | 01:28 | that are used by the sandbox environment,
| | 01:31 | main is where we will put our code that
we're going to be using for operating on
| | 01:35 | the database, and page is used for
displaying the page on the browser screen.
| | 01:41 | Here in init, we initialize our display
variables, we set up a few variables for
| | 01:46 | use on the screen, and we create the table.
| | 01:50 | The database is open with the
bwSQLite 3 object, and we set up the table
| | 01:56 | name, and then we have some SQL for
creating the table and inserting four
| | 02:01 | rows into the table.
| | 02:02 | This try and catch is PHP's exception
handling, and the bwSQLite 3 library uses
| | 02:11 | exceptions for all of it's error reporting.
| | 02:15 | Here in the main function, we display a
couple of messages and we start a timer,
| | 02:22 | and we do our database here. This is
the count_recs method from the bwSQLite 3
| | 02:29 | library. And then we mark the end
time and display the elapsed time.
| | 02:34 | And so that looks like this in the browser.
| | 02:37 | Reload, it says 16 milliseconds.
| | 02:40 | If I reload again, it says 4
milliseconds, because it gets cached, and that's how
| | 02:45 | the sandbox works for the
examples in this chapter.
| | 02:49 | bwSQLite 3 provides a
simple, object-oriented interface.
| | 02:56 | It's designed specifically to
make common CRUD operations easy.
| | 02:59 | CRUD is create, read, update, and
delete. These are the four most common
| | 03:04 | functions of a relational database.
| | 03:07 | The placeholder arguments for SQL are
passed as variable argument lists, much
| | 03:12 | like they would be in
printf, and any C like language.
| | 03:16 | The CRUD records are
passed as associative arrays.
| | 03:19 | These are PHP's equivalent of hash
arrays or dictionaries in other languages.
| | 03:23 | Rows are also returned as
associative arrays in the CRUD interface.
| | 03:28 | This provides a simple interface for
the most basic operations. Anything
| | 03:32 | more complex than that, there is a facility
for passing SQL directly into the database.
| | 03:37 | This interface does not
support prepared statements or other
| | 03:40 | optimization techniques.
| | 03:41 | If you need that, I strongly recommend
that you write a library specifically for
| | 03:45 | the application that you're looking at,
or write another general library that
| | 03:50 | works the way that you
would like for it to work.
| | 03:53 | So the examples in this chapter are
designed to give you a starting point for
| | 03:56 | working on your own applications.
| | 03:58 | They work well for that purpose, but
they're not designed to teach you the
| | 04:01 | specific techniques involved.
| | 04:03 | For more details on any topic, see
the corresponding lessons in the rest of
| | 04:06 | this course.
| | Collapse this transcript |
| Creating and using a database| 00:00 | In SQLite 3, creating a database
is as simple as opening a file.
| | 00:05 | Let's start by making a copy of create-
start.php, and I'm going to hold down the
| | 00:11 | Option key here on this Mac. And if
you're on a PC, you can drag it and hold
| | 00:15 | down the Control key to make a copy.
And then I'm going to take that copy, and
| | 00:19 | I'm going to rename it;
| | 00:20 | I'm just going to call it create.php,
and we'll open that in our text editor.
| | 00:27 | You'll notice that this version of the
sandbox is a little bit different, in
| | 00:31 | that there's nothing here in the
init, because we're going to do that
| | 00:34 | initialization in the main.
| | 00:36 | We're going to go ahead and
create the database right there.
| | 00:39 | I'm going to start by building the
try and catch block, which is used for
| | 00:44 | exception catching in PHP.
| | 00:47 | So that looks like this, and the catch,
| | 00:53 | we'll catch PDOException, and put it in a
$e variable. And then inside that catch,
| | 01:04 | we'll handle the error simply by calling
our error function, with $e->getMessage,
| | 01:13 | like that, and that's
our exception handling.
| | 01:16 | Now, the bwSQLite 3 Library uses
PDO, and so it goes ahead and uses the
| | 01:21 | PDOException class for its exceptions,
because that's really convenient, and works well.
| | 01:27 | We'll start with the message, and we'll
say, creating the db object, and we'll
| | 01:34 | go ahead and do that. db equals new
bwSQLite 3 and DB_FILENAME, TABLE_NAME,
| | 01:47 | and you'll notice that these
constants are defined up here at the top,
| | 01:52 | DB_FILENAME and TABLE_NAME.
| | 01:55 | We're using this special :memory:
| | 01:58 | file name for the database file name.
| | 02:01 | That's special in SQLite
for the in-memory database.
| | 02:05 | And so this will be using an in-memory database.
| | 02:08 | Once we've got it all up and running,
we'll create an actual database file, and
| | 02:12 | you'll see how that works and then how
that affects the performance at all, but
| | 02:16 | for now, there we have it.
| | 02:17 | We have created the database object, and
we'll go ahead and initialize a variable.
| | 02:23 | We're going to call it tn for the
table name, and that's because we can use
| | 02:27 | that inside of a string;
| | 02:30 | you cannot use a constant inside of a
string. And I'll show you an example of
| | 02:34 | what I mean there in a moment.
| | 02:36 | So, we'll send out another message, and
we'll say 'creating the table,' and we'll
| | 02:43 | go ahead and do that. And we're using
the sql_do method in the bwSQLite, and
| | 02:50 | BBEdit's completion doesn't know
about that, and that's okay. And we'll
| | 02:54 | just say 'drop table if exists $tn,'
and then we'll go ahead and create the
| | 03:03 | table, 'create table $tn.'
| | 03:14 | First column is called id., and this
is a special column in SQLite. If you say
| | 03:19 | integer primary key, like that, it's a
special type in SQLite that actually
| | 03:29 | doesn't take up any space in the table,
| | 03:30 | because SQLite is already doing this;
| | 03:33 | it already is assigning a row id to
every row in the table, and this just
| | 03:37 | makes it accessible.
| | 03:38 | And so it doesn't cost
anything. It's very useful.
| | 03:41 | We're not actually going to use it in
this table, but it's something that I typically
| | 03:44 | throw in almost by default, because it
just doesn't cost me thing to have it,
| | 03:48 | and it's often very useful.
| | 03:51 | Then we'll call our first column
'animal,' and it'll be a text column, and
| | 03:55 | then we'll have a sound, and it will
also be a text column, and that is our table definition.
| | 04:04 | Now, I'll go ahead and insert some
records, and these also use sql_do, and we
| | 04:18 | need to use the double quotes here
because we're doing variable expansion inside
| | 04:22 | of the string, 'insert into $tn,' 'animal,'
and 'sound values,' and we'll just put in a
| | 04:34 | couple of question marks for the values.
And we use SQLite's facility for value
| | 04:39 | substitution, and we'll put in the
strings over here, 'cat,' and the sound that a
| | 04:45 | cat makes is 'purr.'
| | 04:47 | Now, you'll notice that we're able to
use this value substitution with the
| | 04:51 | question marks for these values, but
we're not able to use it for the table
| | 04:56 | name, and this is true across the board
for every database that I've ever used.
| | 05:00 | A table name is not
substitutable, but the values are.
| | 05:05 | And so we can use the table name this way.
| | 05:07 | You just need to be careful that
this variable doesn't come from anywhere
| | 05:10 | outside, that it's defined right here locally.
| | 05:14 | In this case, we've used a constant, and
we've put that directly into a variable.
| | 05:18 | So, we know that this is not something
that's going to be subject to attack from
| | 05:22 | the outside for SQL injection
errors, and things like that.
| | 05:26 | In the case of these variables
here, we want to do it this way.
| | 05:31 | We don't want to just put them in with
a dollar variable like that, because
| | 05:37 | that's prone to a lot of error, even if
we have code that does escaping and goes
| | 05:42 | through a lot of trouble to
try and clean those variables up.
| | 05:46 | This method is much more reliable,
the code in SQLite, and the code in PDO
| | 05:52 | is already there for doing the
escaping and for making these into safe
| | 05:56 | variables inside the SQL.
| | 05:58 | So, it's much safer to do it this way
where we can, and we're just not allowed
| | 06:04 | to for the table name, and there's a
lot of reasons why that's always true.
| | 06:07 | I'm going to go ahead and copy
this and insert a couple more.
| | 06:12 | We'll do 'dog' and 'duck' and 'bear,' and
we'll put in their sounds. Dog goes 'woof.'
| | 06:25 | Let's go ahead and capitalize this as well,
and a duck says 'quack' and a bear says 'grrr.'
| | 06:38 | All right, and now we have created our
database, we've created our table, we've
| | 06:43 | inserted some records, and we'll go
ahead and send one more message. And we'll
| | 06:51 | say 'there are %d rows in
the table and $db->count_recs,'
| | 07:01 | The message function here - and we
can look at it down here at the end -
| | 07:04 | it uses this vsprintf so that you
can actually use it just like you would
| | 07:11 | with the printf. You can say %d here,
and you can put in a variable out here
| | 07:16 | that gives you a number.
| | 07:17 | If you say %s, you can use a string, and
that's just like printf in any language
| | 07:22 | that works like that.
| | 07:23 | Now, you'll notice that all we did
to actually create the database is to
| | 07:28 | instantiate the object. And so this
opens the database. And just like if you're
| | 07:33 | opening a file with fopen, if the file
does not exist, the file gets created,
| | 07:38 | and that's really all there
is to creating a database.
| | 07:41 | In SQLite a database is a file, and the
creators of SQLite have often said that
| | 07:48 | SQLite is really more of a
replacement for fopen than anything else.
| | 07:52 | Now, in my opinion, it's a whole lot
more powerful than that, and I would use it
| | 07:56 | in a lot of the places where people are
using MySQL for small to medium-size web
| | 08:00 | sites that don't really require
database replication in a separate server and
| | 08:06 | client for the database engine.
| | 08:07 | SQLite is a great substitute for that,
but it works very much like fopen.
| | 08:12 | You open a file, and it creates the
file if the file isn't already there.
| | 08:17 | So, let's go ahead and save this,
and we'll open up the browser and go to
| | 08:25 | exercise files and Chapter01, and
this is create.php, and there it is.
| | 08:31 | The file name is :memory:
| | 08:34 | and that creates an in-memory database,
and it creates the db object, it creates
| | 08:38 | the table and then counts the rows.
| | 08:39 | So, we can see there are four rows in
the table, and they are in our code;
| | 08:43 | we inserted these four rows:
| | 08:44 | cat, dog, duck and bear.
| | 08:46 | Now, if we wanted to create an actual
file and not just an in-memory database,
| | 08:51 | up here where it says memory, we
would put the actual file name.
| | 08:56 | In this case, on this computer, I've
created the SQLite 3 data directory in my
| | 09:02 | home directory, which on a Mac is in
the Users folder. It's called bweinman,
| | 09:07 | and it's sqlite3_data, and then I'll
give it a file name. And I'll call this
| | 09:14 | chapter01.sqlite, and I will save that.
And we'll go ahead and reload here, and
| | 09:25 | we can see now that that's the name of
the file, and it took 6 milliseconds this
| | 09:31 | time, instead of the fraction of a
millisecond that it took before with the
| | 09:34 | in-memory database.
| | 09:36 | So now, if we look in the SQLite 3_
data directory, we'll see that here's the
| | 09:41 | file that we just created. And in fact,
if I go ahead and delete this, pressing
| | 09:46 | Command+Delete on my keyboard,
and then I run this script again,
| | 09:50 | you see there is the file; it came back.
| | 09:53 | So, creating the database in
SQLite is as simple as opening a file.
| | 10:00 | Be sure your file and directory
permissions allow your application to create and
| | 10:05 | write to files in that directory.
| | 10:07 | That's really important.
| | 10:08 | You must, of course, have permissions
for the file itself, but you also need
| | 10:12 | permissions for the directory.
| | 10:13 | If you don't have permissions for the
directory, SQLite can't create and manage
| | 10:18 | its old temporary files that it
occasionally uses for the various things that it does.
| | 10:22 | So that's all there is to
creating a database in SQLite.
| | 10:25 | You just open the database and
be creative if it doesn't exist,
| | 10:29 | create your tables using SQL,
and you're off and running it.
| | Collapse this transcript |
| Inserting data into a table| 00:00 | Inserting data into a table in
SQLite is accomplished with SQL, using
| | 00:04 | the insert statement.
| | 00:05 | So we'll go ahead and look an
example of how this is done.
| | 00:08 | I'll start by making a
working copy of start.php.
| | 00:11 | I'm going to name this one
insert.php, and open that in my editor.
| | 00:16 | You'll notice that we've moved the code
from the create.php down into the init
| | 00:21 | function here, so that when we get
to the main function, the database is
| | 00:25 | already initialized, the table has been
created, and four rows have been inserted into it.
| | 00:30 | I'm going to start by putting our
cursor after the database operation 'go here,'
| | 00:35 | and we're going to create our exception
handling block with try and catch.
| | 00:51 | And that's because all errors are handled
with exceptions with the PDOException
| | 00:55 | class in bwSQLite 3.
| | 00:59 | And so, we'll handle that error with
the error function, like that, with the
| | 01:07 | getMessage method. I'll go ahead and
start inserting data into the table.
| | 01:12 | Use the db object, which you'll notice
here, comes from this global $G, and
| | 01:18 | it's copied into the global $G
down here, in the init function.
| | 01:23 | So that's already initialized for us,
and that is an object of the bwSQLite 3
| | 01:29 | class, and we'll use the sql_do
method, which we'll use quite a bit.
| | 01:36 | Go ahead and put some SQL in here, and
'INSERT INTO $tn' and tn is the TABLE_NAME;
| | 01:44 | you see that that's initialized up
here on line 26. And the SQL for this
| | 01:49 | goes like this, we say animal and sound,
specifying the columns to insert values into.
| | 01:58 | We can now say VALUES, and then
we're just going to use the place markers
| | 02:02 | question marks, and we'll
put in the values after here.
| | 02:06 | I want to do this on the next line
and say that this is horse, animal, and
| | 02:11 | the sound that a horse makes, of
course, is sings a song, 'a horse is a horse,
| | 02:18 | of course, of course.'
| | 02:22 | That's the sound that a horse makes.
| | 02:24 | Now, you notice that we have inserted
the row, and this table was created with
| | 02:29 | this integer primary key column.
| | 02:32 | This is a special column in SQLite.
| | 02:34 | It works like auto-increment does in
some other database engines, and it's very
| | 02:39 | convenient, and it's very easy to use,
and it doesn't actually even take up any
| | 02:43 | space in the table, because SQLite is
already internally keeping a row id for
| | 02:48 | every row in the table. And it just
assigns it when you use a special syntax
| | 02:52 | integer primary key.
| | 02:54 | So, I'd like to get a copy of
that value for the row that I've just
| | 02:57 | inserted, and so I'll use the last
insert row id function from SQLite, and
| | 03:04 | then that'll work like this.
| | 03:05 | So I'll say, horse_id equals sql_query_value.
| | 03:12 | This is a special method in the
bwSQLite 3 that returns a single value from a
| | 03:17 | query, so you want to give it a query
that will naturally return a single value.
| | 03:22 | And I want to give it this one here.
| | 03:25 | I'm going to say SELECT LAST_INSERT_
ROWID, like that, and we'll say 'message( 'Added
| | 03:36 | id number %d, and that'll be horse_id like that.
| | 03:44 | Now, we'll go ahead, and we'll save this,
and we'll run it in the browser, and
| | 03:54 | you see we added id number 5.
| | 03:56 | There were 4 rows in the table before,
and now we added id number 5. And if we
| | 04:01 | go ahead and say, message ( 'There are
now %d rows in the table, like that, and
| | 04:17 | switch back to the browser and reload,
see, there are now 5 rows in the table.
| | 04:24 | Now, that's one way to do it.
| | 04:26 | There is actually a simpler way to do
it, using the CRUD methods in the bwSQLite
| | 04:30 | 3 Library, and this is one
of the values of this Library.
| | 04:35 | So, I'm going to go ahead, before this
how many rows in the table, because we're
| | 04:38 | going to add another one, and
| | 04:40 | I'm going to say db->insert, and this
will return the new row id, so I'm going
| | 04:47 | to call this one bird_id.
| | 04:49 | What we give it is an associative array.
| | 04:54 | So, I'm going to create an array in
place here, and this will have animal, and
| | 05:01 | this is the syntax for creating an
associative array, and sound, like that.
| | 05:11 | That will create a new record, and
that's all that there is to it. And I can go
| | 05:16 | ahead, and I can just copy this line
down here a little bit, and change horse_id
| | 05:22 | to bird_id. And we'll see that we did
here in one line of code what it took two
| | 05:27 | lines of code to do there.
| | 05:28 | I had two SQL queries.
| | 05:30 | So go ahead and save that and reload
it in the browser, and we see Added id
| | 05:36 | number 5 and id number 6, and
there are now 6 rows in the table.
| | 05:41 | And so let's go ahead and print them out.
| | 05:44 | We can print this out with a
select, and we can say, row equals
| | 05:48 | db->sql_query_row, so that'll get us a
whole row. And I can say SELECT *
| | 05:57 | FROM $tn, which is the table name, WHERE
id = ?, and I can give
| | 06:05 | this one the horse_id, and then I can print it.
| | 06:15 | I can say id is %d, and The %s says %s,
and so we'll give it row sub id, row sub
| | 06:30 | animal and row sub sound.
| | 06:37 | I'll go ahead and save that and run it,
and I have a little syntax error here.
| | 06:43 | That's because I put in this
dollar sign before message;
| | 06:46 | that doesn't belong.
| | 06:47 | Go ahead and run it, and there we have id: 5:
| | 06:51 | The horse says, A horse is a
horse, of course, of course,...
| | 06:54 | There is of course another way to do
this using the CRUD methods, and that
| | 06:57 | would go like this:
| | 06:58 | row = db - it's very
simple - get_rec and bird_id.
| | 07:06 | And so that will allow the Library to
create the SQL, and we'll see that in
| | 07:11 | another lesson, how that works. And then
we can just print it out, save this, and
| | 07:18 | go to the browser and reload,
and The bird says Tweet.
| | 07:22 | So, we can see that inserts are
accomplished in SQLite using the insert
| | 07:28 | statement in SQL. And using a library
like bwSQLite 3 or library that you create
| | 07:34 | yourself, it's possible to do it
without writing the SQL every time for
| | 07:40 | operations that you're just going
to end up doing very, very often.
| | 07:44 | So, we can use something like insert
with the appropriate data structure,
| | 07:50 | instead of this SQL, and likewise we can use
the get_rec, instead of the SELECT statement.
| | 08:00 | So that's how data is inserted into a
table using SQLite 3, and that's also how
| | 08:06 | you can do it using a CRUD Library
like bwSQLit3, or use that as a starting
| | 08:12 | point in creating your own general
or application-specific libraries.
| | Collapse this transcript |
| Getting data from a table | 00:00 | Retrieving data from a table is
accomplished with the SQL select statement.
| | 00:04 | Using PHP's various interfaces, you
may retrieve rows into arrays, indexed by
| | 00:09 | numbers or associative arrays, indexed
by column names, or even single values.
| | 00:14 | We'll start by making a working copy of
start.php, and I'll name this 'retrieve.php.'
| | 00:23 | I will open that in the editor, and
then go down here into the main function,
| | 00:29 | where it says database operations go here,
and we'll start by making a try catch
| | 00:34 | block for handling exceptions.
| | 00:37 |
| | 00:54 | Now we'll put in our select statement
for retrieving a row, and we'll create a
| | 00:59 | row variable to handle the associative array.
| | 01:02 |
| | 01:20 | So there is my select statement, and
this is the placeholder for the variable,
| | 01:24 | and there is the value that will go
into the placeholder. And so now I
| | 01:29 | have the row, I'm going to
go ahead and print the row.
| | 01:32 |
| | 01:53 | So that's it. We have the select statement,
and we have this message that prints it out.
| | 01:59 | So we will go ahead and save that and
load it up in the browser. And here we have
| | 02:05 | the row, row number three the duck
says quack, and you will notice that we
| | 02:09 | selected where animal equals duck,
so we got that row, and we used this
| | 02:14 | associative array with the
column names as the keys.
| | 02:20 | So that's how you retrieve a row of data
from the table using SQL directly.
| | 02:25 | You can also get one particular
value from a select statement, and that
| | 02:30 | would look like this.
| | 02:32 |
| | 02:58 | So if I just want to see the sound
that a dog makes, I can say select sound
| | 03:03 | from the table where animal equals to
dog, and I can print it directly because
| | 03:08 | that will turn a string, so I can print
it directly with this %s in the message
| | 03:15 | functions. So I'm going to save that and run it.
| | 03:18 | It says the value result is woof,
| | 03:21 | so that's the sound that the dog makes.
| | 03:24 | Using the crud interface, it's very
simple to print out all the rows, and I can
| | 03:29 | use PHP's 'for each' to iterate over a
table and say db-get-recs with an s at the
| | 03:38 | end for plural and say as row and
message and just take all this here, copy it
| | 03:51 | over there, and save that, and reload in
the browser, and I get all four of those rows:
| | 03:59 | the cat says purr, the dog says woof, et cetera.
| | 04:02 | So the way that this works is this
get_recs actually returns an object that is
| | 04:08 | an iterator and allows you to use 'for
each' and so for each row this is really
| | 04:13 | just all from that one line, and you
can actually step through the entire
| | 04:17 | table and operate on it.
| | 04:19 | So retrieving data from the table
is accomplished using the SQL select
| | 04:22 | statement. Using interfaces provided
by PHP, you may retrieve rows in several
| | 04:27 | convenient formats, or feel free to
use the bwSQLite 3 libraries, as a
| | 04:32 | starting point in creating their own
general, or application-specific libraries.
| | Collapse this transcript |
| Updating data in a table| 00:00 | Updating a table in SQLite 3 is as
simple as using the SQL update statement.
| | 00:06 | We'll start by making a copy of the
start.php file, and we'll rename that to
| | 00:12 | update.php. We'll open that in the editor.
And scrolling down here to the main function,
| | 00:19 | where it says database operations go here,
| | 00:22 | we can start by creating our exception
handling with the try catch block, and
| | 00:38 | we'll handle that error with the
error function, and then we will
| | 00:46 | input our SQL in here.
| | 00:50 | We'll start by getting a row, and we'll
select a row using the SQL select statement.
| | 01:02 | We'll use SQL query row from the BWS SQLite library.
| | 01:06 | We'll select for particular
animal, and this will be the animal 'dog,' and
| | 01:19 | we will go ahead and print it
out using the message function.
| | 01:23 |
| | 01:41 | Now we have a row, and we
can get its ID like this.
| | 01:49 | We can use that ID for updating it.
| | 01:51 | So we'll message that we are going to update it, and
then we'll use some SQL for updating it.
| | 02:07 | Use the SQL update command.
| | 02:18 | So I've set update in this table and
set sound equals particular sound where ID
| | 02:24 | equals, and we'll use the ID
that we got in the lines before.
| | 02:28 | So the sound would be 'bow wow' for the
dog, so we can change it something else
| | 02:32 | that the dog may say, and we use ID
for the ID' and so that we'll replace this
| | 02:39 | first question mark with the bow wow,
and the second question mark with the ID.
| | 02:44 | Then we'll go ahead and will print the row again.
| | 02:49 | Now that we have the ID, we can use the
CRUD get-rec method, and we can go ahead,
| | 02:58 | and I'll just copy and paste
this message statement here.
| | 03:01 | I will save that, and we
will go ahead and run it.
| | 03:07 | So this is update.php, and here we
have these four rows in the table and
| | 03:12 | originally it says the dog says woof,
and we've updated it. Now it says the
| | 03:16 | dog says bow wow, and so that update was
accomplished with this SQL right here for the update.
| | 03:22 | Now, of course, we can also update
it with a CRUD method, and we can say
| | 03:27 | message updating id %d with CRUD, and
then we'll go ahead and use db update and we'll
| | 03:41 | give it the with ID, and we've given it
an associative array with just the part
| | 03:46 | that we want to update.
| | 03:47 | So sound 'Ruff,' like that, and then
we'll go ahead and get the record again and
| | 03:58 | print it again with those two lines
copied from up there, and so now we're
| | 04:02 | updating with a new sound that the dog
might say 'Ruff.' And we'll save that and reload
| | 04:07 | over here, and we see now updating
with CRUD, and we get Ruff from the table.
| | 04:13 | Updating data in a table use as
simple as using the SQL update statement.
| | 04:18 | You can make the process even easier by
creating corresponding CRUD methods in the library;
| | 04:23 | As usual, feel free to use bwSQLite
3 library as a starting point for your
| | 04:29 | own custom libraries.
| | Collapse this transcript |
| Deleting data from a table| 00:00 | Deleting rows in a table is
accomplished with the SQL delete statement.
| | 00:05 | So let's make a working copy of
start.php, and we'll rename this to delete.php.
| | 00:13 | Open that in the editor, and come down
here to the main function, where it says
| | 00:19 | database operations go here, and we'll
start with our exception handling block,
| | 00:25 | using try and catch, and error
function for the error message.
| | 00:45 | And let's start by printing out all
the records that we have, so we can see
| | 00:49 | where we're starting as
we go onto delete things.
| | 01:03 | We'll print them out with that message
function, and the row id, and animal and sound.
| | 01:30 | And now we'll go ahead and delete something.
| | 01:34 | So we're going to delete the record with id of 3,
and we'll say message, deleting, like that.
| | 01:46 | Now I'll do the delete.
| | 01:48 | We'll do this using sql_do.
| | 01:58 | I'm going to pass it the id, and then
after, we'll have so many records in the
| | 02:10 | database, and we'll go ahead and print
them all out again, so we can see that we
| | 02:23 | deleted the correct one.
| | 02:25 | Now, let's go ahead and save that and run it.
| | 02:32 | And here we have, there's four
rows in a table, and there they are.
| | 02:35 | And we're going to delete id 3, which
is this one here that is The duck, and
| | 02:40 | after delete, there are
three records in the table.
| | 02:42 | That's correct, and there is no duck.
| | 02:45 | Id number 3 is gone.
| | 02:47 | So that's how you delete a
record using SQL in SQLite.
| | 02:51 | You see here is our SQL,
DELETE FROM $tn, WHERE id = id.
| | 02:58 | Now, of course using CRUD,
this can be quite a bit simpler.
| | 03:03 | We'll set the id to 2, and
we'll give it another message.
| | 03:09 | Actually, I have this already up here;
| | 03:10 | I can cut and paste.
| | 03:11 | I'm a big fan of cut and
paste and db->delete($id).
| | 03:19 | It's just like that,
except I need to spell it right.
| | 03:24 | And then we can just copy and paste all
of this and say, after delete there is
| | 03:28 | that many records and print it out again.
| | 03:31 | So we'll save this, and we'll come
over here to the browser and reload.
| | 03:36 | Now, let's scroll down.
| | 03:39 | After we had these three
left, now we're deleting id 2.
| | 03:42 | After the delete, there is two records
left, and id 2 was 'The dog says Woof,' and
| | 03:47 | that's the one that's missing.
| | 03:49 | And so there we have it.
| | 03:50 | It's really very simple.
| | 03:52 | Deleting rows in a table is accomplished
with the SQL delete statement, and this
| | 03:58 | is a process that easily lends itself
to using CRUD, and you can see how simple
| | 04:03 | that is with the CRUD Library.
| | 04:05 | And again, I do recommend that you
create your own CRUD Library and feel free to
| | 04:09 | use mine as a starting point.
| | Collapse this transcript |
| Creating a database library| 00:00 | A custom CRUD library can
be a very powerful tool.
| | 00:03 | It can be helpful to have a generalized
library for creating small applications,
| | 00:08 | and that can be used as a starting point
for more specific libraries, targeted at
| | 00:13 | individual applications.
| | 00:15 | bwSQLite 3.php is included with
the exercise files for this course.
| | 00:22 | This is the library that I use
for SQLite work in my own PHP work.
| | 00:27 | test-bwSQLite 3 is a unit testing script
that I use for testing the library as I
| | 00:35 | work on the library itself.
| | 00:37 | So let's take a look at the library.
| | 00:39 | We're just going to take a tour
through it here, a very quick tour.
| | 00:42 | I'll give you an idea of what it is, how it
works, and I'd like you to be thinking
| | 00:47 | about how you would do it differently,
how you would create something like this
| | 00:51 | that works in your own way, and
in your own style of programming.
| | 00:55 | So this is object-oriented PHP, this is
a class definition, and the constructor
| | 01:01 | takes two arguments:
| | 01:02 | a file name and a table_name. And if
you don't pass it those, then it will
| | 01:07 | default to the in-memory database, and it
will default to having no table_name at all.
| | 01:12 | You can use the setter/getters for
table_name and a PDO handle, if you want to
| | 01:18 | initialize the database that way outside of
the constructor for this particular class.
| | 01:24 | sql_do is a very common function
that I use a lot for passing SQL that's
| | 01:30 | not select-oriented.
| | 01:32 | That doesn't return a result set.
| | 01:34 | sql_do_multiple simply passes to the
DBO exec method, and that's useful for SQL
| | 01:42 | scripts when there's more
than one statement of SQL.
| | 01:49 | sql_query returns a statement handle,
and a statement handle, in PDO, is actually
| | 01:55 | an iterable object, so it allows
you to iterate through the results.
| | 02:01 | It also sets up a query for the get_next method.
| | 02:05 | The get_next method uses a saved
statement handle to do pretty much the same
| | 02:09 | thing, and so you'll notice that it
initializes the statement handle that is
| | 02:15 | part of the object itself.
| | 02:16 | It's one of the private variables up here.
| | 02:23 | sql_query_all does a fetchAll,
which is a very dangerous thing.
| | 02:27 | You only want to do this if you
absolutely positively know that you have a small
| | 02:31 | set of results that will fit in
memory without causing you problems.
| | 02:35 | I strongly recommend against using
this, if you can instead use one of
| | 02:40 | the iterable methods.
| | 02:41 | sql_query_row returns a row, sql_query_
value returns a value, and all of these
| | 02:47 | SQL methods take an SQL
statement as their first argument.
| | 02:52 | These are the transaction functions, and
these just translate directly to PDO functions.
| | 02:59 | Now we get into the CRUD.
| | 03:01 | These all take either an id, as in
this case, which makes it very easy to
| | 03:05 | create the SQL, or some of them will
actually take an associative array, as in
| | 03:11 | this insert takes a rec, and then it
goes, and it creates the SQL, it analyzes
| | 03:18 | the associative array, finds out what
the column names are from the keys of the
| | 03:23 | associative array, and then it goes ahead,
and it builds the SQL and runs the query.
| | 03:29 | So insert and update both do that,
and you can see that they're a little
| | 03:34 | bit more complicated.
| | 03:35 | So feel free to crib from this code
because this code works, and it's pretty
| | 03:39 | clean, and it has been refined over a
period of time, and I know that it works well.
| | 03:45 | get_next is a pseudo-iterator.
| | 03:48 | If for some reason you prefer to not
use the for_each and you would rather use
| | 03:52 | a while, or if your code lends itself well
to that, get_next works for that purpose.
| | 03:59 | And then count_recs just does a Select Count.
| | 04:02 | Table_exists uses the SQLite special table
sqlite_master, to find out if a table exists.
| | 04:11 | And then we have version for getting
the version number of the SQLite and timer
| | 04:15 | functions for timing your
queries and refining them.
| | 04:19 | So it's a very small and simple library,
as a generalized library should be.
| | 04:25 | You know, a lot of times you'll see
libraries, like PDO, that's very huge, and
| | 04:30 | has a lot of functions and a lot of code
that may apply to somebody else's style
| | 04:35 | of coding, may apply to some other
application, may apply to something larger.
| | 04:39 | But for most purposes, where you're
simply writing a small application,
| | 04:44 | something like this is ideal. It's small.
| | 04:46 | If it's easily in memory, it's easy to
use, it's easy to remember what's in
| | 04:50 | there, and it's easy to look through it
to find the things that you might have
| | 04:53 | forgotten that you had.
| | 04:55 | I strongly suggest that you
create your own generalized library for
| | 04:58 | accessing your database.
| | 05:00 | Set up the interface so that it works
the way that you like it, the way that you
| | 05:03 | work. Refine it, polish it, and then
use it as a starting point for more
| | 05:07 | specific libraries for individual applications.
| | 05:10 | This strategy will serve you well
as you create applications using PHP
| | 05:15 | and SQLite 3.
| | Collapse this transcript |
|
|
2. Getting StartedInstalling XAMPP on a Mac| 00:00 | XAMPP, which is how I pronounce X-A-M-P-P,
is a cross-platform version of the LAMP
| | 00:05 | stack that includes the Apache web
server, PHP, SQLite, and a number of
| | 00:10 | other useful tools.
| | 00:11 | You do not need to install XAMPP
to do the exercises in this course.
| | 00:16 | You do need a web server with PHP 5
and SQLite 3, and XAMPP is a convenient
| | 00:21 | option to provide that on your desktop,
but any server with PHP 5.3 and SQLite
| | 00:25 | 3.6 or later will do.
| | 00:29 | This is the www.apachefriends.org web
site, where you can download XAMPP for Mac
| | 00:34 | OS X. And if we scroll down here to
the Download section of the page, you'll
| | 00:39 | notice that the current
version as of recording is 1.7.3.
| | 00:45 | The thing about this is that the good
folks at apachefriends.org update XAMPP
| | 00:51 | frequently, which is a wonderful thing.
| | 00:53 | They keep it up to date.
| | 00:54 | They're using the latest versions of stuff.
| | 00:56 | The down side of that is that if
you want to follow along with the
| | 00:59 | installation instructions in this movie,
that's likely to be different with a
| | 01:03 | later version of XAMPP.
| | 01:05 | So if you want to follow along, you
want to have XAMPP for Mac OS X Version
| | 01:10 | 1.7.3, and the way to get that is on my
web site. Because I've run into this before,
| | 01:16 | I'm providing a download for this
exact version so that you can follow along
| | 01:22 | with these installation instructions,
and it'll work exactly the same for you,
| | 01:26 | and this is the version that I'm
using throughout this course as well.
| | 01:30 | So you can find this download on my
web site at sqlite.bw.org. That's bw, as
| | 01:36 | in Bill Weinman, .org.
| | 01:38 | Now I've already downloaded and put on
my desktop this version of XAMPP, and
| | 01:43 | we're going to go ahead and install it.
| | 01:45 | But before we do, I'll come up here
to the Apple menu, and I'm going to
| | 01:49 | open System Preferences.
| | 01:50 | Now this is an important
step that you must not skip.
| | 01:53 | I'm going to open the Sharing, and I'm
going to check this check box right here,
| | 01:58 | web sharing, and you'll notice that it's not
checked, and that's the way that it should be.
| | 02:02 | What that does is it starts a version
of the Apache web server on your Mac.
| | 02:07 | It's the version that came with your Mac,
and there's nothing wrong with that version.
| | 02:12 | It's just that I don't know what version it is.
| | 02:14 | I don't know what version of PHP there is.
| | 02:16 | I don't know if that version of PHP
has the required libraries compiled in.
| | 02:21 | So if you want to use that,
there may be some work for you to do.
| | 02:25 | But you cannot use that and XAMPP at
the same time, because they fight for the
| | 02:29 | same resources and one or the other of
them it's not going to work, and it's
| | 02:32 | probably going to be the
one that you don't want.
| | 02:34 | So if you're going to install XAMPP,
you need to make sure the Web Sharing is
| | 02:37 | turned off, so that there's not
another web server running on your Mac.
| | 02:41 | So, I'm going to go ahead in quit
System Preferences, and I'm going to open up
| | 02:45 | this DMG file. And that mounts a
virtual drive on my desktop, and I simply
| | 02:51 | drag the XAMPP folder into the
Applications folder, and it copies it over.
| | 02:57 | And this can take a while.
| | 03:00 | When this is done, I'll hold down the
Ctrl key and click on an open spot in this
| | 03:05 | folder here and press Eject.
| | 03:07 | That will unmount that
virtual drive from my desktop.
| | 03:09 | Now, I can file this away some place, so
that it's safe, should I need it again.
| | 03:14 | Let me come down here and click on
Finder and go to Applications and scroll
| | 03:19 | all the way down to the end, which is where I'm
likely to find something that begins with an X.
| | 03:24 | I'm going to drag this XAMPP control app
to my dock, and I'm going to click on that.
| | 03:33 | What this does is this is the controls for
starting the servers that come with XAMPP.
| | 03:36 | I'm not going to start MySQL, and I'm
not going to start FTP, because I don't
| | 03:40 | need those for this course.
| | 03:42 | I'm just going to start Apache,
and I'll need to type in my password.
| | 03:46 | Now, your account must be an administrator
account in order to run a server on your Mac.
| | 03:51 | I am going to wait for
that to say that it started.
| | 03:56 | I've got a green light.
That means it started, and that it's running.
| | 03:59 | So I can completely quit this Control
app, and the server is now running, and
| | 04:05 | it will remain running.
| | 04:06 | When I reboot my Mac, it
will not start up again.
| | 04:09 | Anytime I want to start it or stop it, I just
bring this up, and I can start it and stop it.
| | 04:14 | But for now, let's close this, and I'm
going to open my web browser, and I'm
| | 04:20 | going to type in "localhost,"
and I should get this page here.
| | 04:24 | That means that the Apache
server is now running and working.
| | 04:28 | If it weren't, then I can just go ahead
and stop it here and see you'll see what
| | 04:32 | happens if it's not running,
| | 04:36 | reload, and I'll get this page.
| | 04:38 | If you get this page that the Apache
server is not running, and I'm just
| | 04:42 | going to start that up again and close
the XAMPP Control panel and click Try
| | 04:50 | Again, and there it is.
| | 04:51 | Now, I'm going to click on English.
| | 04:53 | You're welcome to click on whatever
language you like, but English works for me.
| | 04:57 | You'll notice that I get the version of XAMPP -
| | 04:59 | it's like this little XAMPP welcome
page and a lot of things that I can test.
| | 05:03 | Now some of these things won't work,
because we haven't started all the services
| | 05:06 | here, but phpinfo is the one that we care about.
| | 05:09 | phpinfo will tell us what version of
PHP is running, and that's compiled in
| | 05:14 | with the Apache server here, and it will
also allow us to search for the string 'sqlite.'
| | 05:19 | So I'm going to press Ctrl+F here, and
I'm going to type 'sqlite,' and you'll see
| | 05:26 | that I found it there, and I'm pressing
Command+G to search over and over again,
| | 05:30 | until I get down to the PDO section.
| | 05:33 | You'll notice that sqlite and
sqlite2 are in the PDO drivers.
| | 05:37 | The one that's just sqlite by itself,
without a 2 after it, that's actually
| | 05:40 | SQLite 3, and that's the one that we care about.
| | 05:43 | I am going to search again to find the
version of SQLite 3, and here we have the
| | 05:48 | version number, and it's 3.6.12.
And that's all we care about.
| | 05:52 | So these are acceptable versions,
and we have now successfully installed
| | 05:56 | XAMPP for Mac OS X.
| | Collapse this transcript |
| Installing XAMPP on a PC| 00:00 | In this movie, we're going to install the
XAMPP package on a Windows operating system.
| | 00:05 | Now it is not necessary to install XAMPP
in order to follow the examples in this
| | 00:10 | course, but the examples in this course
are using XAMPP, and it is convenient.
| | 00:15 | So if you'd like to install XAMPP on
your local machine, I'll be showing you
| | 00:19 | how to do that here.
| | 00:20 | So this is the XAMPP web site, where
you would download XAMPP, and down here is
| | 00:25 | the XAMPP downloader.
You'll see the current version is 1.7.3.
| | 00:29 | One of the nice things about XAMPP is
that it's well maintained, which means
| | 00:34 | that by the time you look at this page,
it may very well have a different version.,
| | 00:38 | and unfortunately the installation
procedure for that different version may be
| | 00:42 | different than what I'm going to show you here.
| | 00:44 | So if you really want to follow
along, you want to be installing 1.7.3.
| | 00:48 | If you want to install a later version,
you're welcome to do that, and I'm
| | 00:52 | sure it'll work, and the installation procedure
might be different than what I'm showing here.
| | 00:57 | So this movie might not be as helpful for you.
| | 01:00 | If the latest version is different than
1.7.3, and you'd like to follow along,
| | 01:04 | you can come to my web site,
right here at www.sqlite.bw.org.
| | 01:09 | Down around the middle of the page
here, you'll see you can download version
| | 01:13 | 1.7.3, so that you can follow along and
run exactly the same environment as I'm
| | 01:18 | running in the movies.
| | 01:20 | So here on the desktop, I have
XAMPP for Windows version 1.7.3.
| | 01:27 | So I'm going to double-click on
this installer, and it says Destination
| | 01:32 | folder, the root of drive C, and
that's exactly where I'm going to install it,
| | 01:36 | just because that's what they're
suggesting. And I'm assuming that their
| | 01:39 | package is set up for that.
| | 01:41 | While it's not the preferred place where
I would really like to install it, it's
| | 01:46 | fine, and it doesn't really bother me.
| | 01:47 | So I'm going to click Install,
and this process does take a while.
| | 01:51 | So go get a cup of coffee
or whatever is that you like.
| | 01:57 | So at this point, I tend to just
take the defaults as much possible,
| | 02:00 | so I'm going to say yes to the
shortcuts and yes to shall I proceed, because I
| | 02:07 | do actually want to do the installation.
| | 02:09 | I'm going to say no to this one,
because I actually know what it means, and
| | 02:12 | that default is correct, unless you're
doing something very different than what
| | 02:16 | our purposes are here.
| | 02:19 | Now it says XAMPP is ready to use.
| | 02:21 | I'll press Enter to continue, and it's
setting the time zone, and I'll press Return.
| | 02:26 | Again, if I want to change those things, I can.
| | 02:29 | At this point I'm going to press
the X for exit, to exit the installer.
| | 02:33 | Now with the XAMPP Control panel on
the desktop, and I'm just going to
| | 02:37 | double-click on it here, because this
is the control panel for starting XAMPP.
| | 02:41 | Before we actually start any of the
services, we're going to click on this button
| | 02:45 | here that says Port-Check.
| | 02:46 | And this is very important.
| | 02:48 | What this does is it checks to make sure
that the ports are open on our computer
| | 02:52 | for running the servers in the XAMPP package.
| | 02:55 | And in particular, this one here -
| | 02:58 | this is really the only one we're
going to be using for our purposes, Apache
| | 03:01 | port 80, and it says Free,
and that's what it should say.
| | 03:04 | If it says something else here, it
will tell you what program is using that
| | 03:08 | port, and you'll need to get that program
to stop using that port in order to use
| | 03:12 | the Apache web server.
| | 03:14 | The two most common things that you'll
see here are Skype and another web server,
| | 03:20 | like Microsoft IIS, or
another installation of Apache.
| | 03:24 | So that's what you want to look for here.
| | 03:25 | If it says Free, then you're fine.
| | 03:27 | I am gong to press Return to
continue, and it goes away.
| | 03:30 | Now you can start Apache.
| | 03:32 | It says Apache started, and we have
Running here, and we don't need any of these
| | 03:36 | other things for our purposes,
| | 03:38 | so I'll go ahead, and I'll close this,
and I'm going to start up my web browser
| | 03:43 | and type in "localhost."
| | 03:46 | There I have the XAMPP server running.
| | 03:48 | I'm going to click on English, and I'm
going to confirm that PHP is running, so
| | 03:52 | I'm going to click on
this thing that says phpinfo.
| | 03:54 | Again, none of these other things matter to me.
| | 03:56 | I'm just going to click on phpinfo.
| | 03:59 | Here we are, PHP Version
5.3.1, which is just fine.
| | 04:03 | I'm going to press Ctrl+F
for Find and type in "sqlite."
| | 04:09 | There, under PDO, I see that
we have sqlite, which is fine.
| | 04:14 | Sqlite2 is an older version.
| | 04:16 | This is actually sqlite3,
| | 04:17 | the one that says sqlite there.
| | 04:20 | Scrolling down a little bit, it says SQLite
Library version 3.6.20, and that is excellent.
| | 04:27 | Anything over 3.6 is just fine for our purposes.
| | 04:30 | So XAMPP is running, and it's
installed, and it's working.
| | 04:35 | The Apache server is running, and PHP is
running, and all our versions are correct.
| | 04:40 | So we have successfully
installed XAMPP on this Windows box.
| | Collapse this transcript |
| Setting up SID and exercise files on a Mac| 00:00 | In this movie, we're going to set
up the exercise files in the XAMPP
| | 00:03 | development environment.
| | 00:05 | Now, you don't need to be using
XAMPP in order to follow along with the
| | 00:08 | exercises in this course, but if you're
not using XAMPP, these instructions will
| | 00:13 | be different for you.
| | 00:14 | So follow along and use the same
principles, but you're going to need to
| | 00:18 | translate whatever needs to happen in
your environment from my instructions for
| | 00:22 | the XAMPP environment.
| | 00:23 | So first thing we need to do is we
need to make sure that XAMPP is running.
| | 00:27 | So I'm going to start the XAMPP
Control Panel, and if we don't have a green
| | 00:31 | light here already on Apache, I'm going to
press Start for Apache and type in my password.
| | 00:35 | Now, Apache needs to be running.
| | 00:38 | MySQL and FTP do not need to be running.
| | 00:41 | I'm just going to bring up my
browser and make sure that that is indeed
| | 00:46 | running, and there it is.
| | 00:49 | So I'll close my browser, and I'm going
to open a new Finder window and navigate
| | 00:56 | over to XAMPP down here, and
I'm going to click on htdocs.
| | 01:02 | This is a shortcut, pointing to the
htdocs folder, which is actually under the
| | 01:06 | xamppfiles folder, if you're looking
for it. And then I'm going to copy my
| | 01:10 | ExerciseFiles folder into the htdocs folder.
| | 01:14 | Now, I've got it on my desktop here;
| | 01:15 | you might have it in a
different place, and that's okay.
| | 01:18 | I'm going to hold down the Option key as
I drag it, and that gives me a little
| | 01:22 | Plus sign, and that means that I'm
copying it instead of moving it. There we go.
| | 01:28 | Now the ExerciseFiles
folder is here in the htdocs.
| | 01:32 | So I'm going to drag a shortcut to my
ExerciseFiles folder over here into my
| | 01:37 | Places on my sidebar.
| | 01:38 | That just makes it convenient for
me to be able to get back to it.
| | 01:42 | The next thing I'm going to do is I'm
going to click on my Home directory, and
| | 01:45 | notice here, this is the
short name for my Home directory.
| | 01:49 | You're going to need that
later on, so make a note of that.
| | 01:52 | And then I'm going to
create a new folder in here.
| | 01:55 | I'm going to Ctrl+click on an empty
space and select New Folder, and I'm going
| | 02:00 | to name it sqlite3_data. And this is
where I'm going to put the database files
| | 02:07 | for the SQLite 3 examples in this course.
| | 02:11 | Now, going back over to ExerciseFiles,
we're going to find the files that go in
| | 02:14 | there, and I'm going to click on the
SQL folder inside my ExerciseFiles, and
| | 02:19 | this is the ExerciseFiles,
not the one on my Desktop;
| | 02:22 | this is the one in the XAMPP folder.
| | 02:24 | So if I Ctrl+click up here at the top,
you'll see that it gives me this path,
| | 02:28 | and it's coming from XAMPP, xamppfiles, htdocs,
ExerciseFiles, SQL, and that's the one that I want.
| | 02:34 | If this is pointing to your desktop,
you've got the wrong shortcut in your Places.
| | 02:38 | Now I'm going to select these three
database files that say .db at the end:
| | 02:42 | album.db - and I'm going to hold down
the Command key while I do this so I can
| | 02:46 | select all three of them, test.db, and world.db.
| | 02:50 | And before I do anything else, I'm going to
Ctrl+click on one of those and press Get Info.
| | 02:57 | If you have the Ctrl key
clicked down, that changes.
| | 03:00 | You want to release the Ctrl key, so that you
get the little Get Info in the pop-up menu.
| | 03:04 | And you'll notice that all three of
these say Read only for everyone in the
| | 03:08 | Sharing & Permissions.
| | 03:09 | If you don't have Sharing & Permissions
open, you can just click on this little
| | 03:12 | triangle here, and you'll
get Sharing & Permissions open.
| | 03:15 | And you want to change the one for
everyone, the bottom one, to say Read &
| | 03:19 | Write, and you want to do that for
all three of these. And this will allow
| | 03:23 | SQLite to actually read and write these files.
| | 03:26 | That's really important, because SQLite
runs as a driver inside of PHP, inside
| | 03:32 | of your web server, and your web
server is running as a user, not you.
| | 03:36 | So it needs Read/Write Permissions for
these files, and this is the way that you
| | 03:41 | give it those permissions.
| | 03:42 | So I'm going to close these; done with those.
| | 03:45 | These three are still selected here.
| | 03:47 | If they're not, then you can Command+
click all three of them. And I'm going to
| | 03:51 | drag them, and I'm going to press down
my Option key so that I'm copying and not
| | 03:56 | moving over to my Home directory.
| | 03:58 | And I'm going to hold on that, so it
brings up the directory, and then I'm going
| | 04:01 | to drop them in this sqlite3_
data directory, and there they are.
| | 04:05 | Now I'm going to do the same thing here.
| | 04:07 | I'm going to Ctrl+click on sqlite3_data,
and I'm going to press Get Info, and
| | 04:12 | I'm going to do the same thing.
| | 04:13 | I'm going to give it Read & Write
Permission for the web server, which is running
| | 04:17 | in the everyone column, and that will
allow SQLite 3 to create files in here and
| | 04:24 | do whatever it needs
to do to operate in here.
| | 04:26 | And just again, I might put a shortcut
to this in the Places, because it might
| | 04:31 | come in handy later.
| | 04:33 | Now our databases are in the right place,
and they have the right permissions,
| | 04:37 | and you'll notice when I bring up the
Get Info on these, you can also get it by
| | 04:40 | pressing Command+I on your keyboard,
that it has got that Read/Write Permission,
| | 04:45 | and that is correct.
| | 04:47 | And you can actually confirm that for
all three of these, and there they are:
| | 04:51 | Read & Write, Read & Write,
Read & Write in everyone;
| | 04:54 | can't hurt to confirm that
everything is working as planned.
| | 04:57 | Now I'm going to go back to
ExerciseFiles, and I'm going to edit the path in
| | 05:02 | SID, and we're going to need to
do this in a couple of places.
| | 05:05 | So I'm going to Ctrl+click, and say Open With,
and select my text editor. I'm using BBEdit;
| | 05:12 | you can use whatever text editor works for you.
| | 05:14 | And I'm going to come down here
where it says path/to/sqlite3_data.
| | 05:18 | It will be around Line 21.
The token here is DBDIR.
| | 05:22 | That's being defined as a constant in PHP.
| | 05:24 | I'm going to select path/to, and I'm
going to put in the path to that directory,
| | 05:29 | which is under Users, and then my
short name, which in my case is bweinman;
| | 05:34 | in your case it will be something else.
| | 05:35 | So I'm going to save this file.
| | 05:37 | I'm pressing Command+S; do whatever
works in your editor, and close it.
| | 05:42 | And then I'm going to do the same in CRUD,
the CRUD file, open it in your editor.
| | 05:47 | I'm going to come down here to path/to
and put in Users/bweinman, and save that.
| | 05:55 | Now we're going to go and do something a
little bit different in the Testimonials.
| | 06:00 | The Testimonials apps use the data in
their own directories, so we don't need to
| | 06:03 | edit the app and put in the correct path.
| | 06:06 | What we do need to do is we need to set the
permissions on the data directory and
| | 06:09 | on the database file.
| | 06:10 | So I'm going to bring up the Get
Info on the data directory and set the
| | 06:16 | Privilege Permission for everyone to
Read & Write, and I'm going to close that,
| | 06:20 | and I'm going to do the same thing on
the testimonials.db file, and bring up the
| | 06:26 | Permissions and set it
correctly, and there, that's done.
| | 06:30 | Now, we've copied all the
database files we needed.
| | 06:32 | We've set up the PHP files to
point to the correct directories.
| | 06:36 | We've done everything we need to do to
get all this stuff working; now let's
| | 06:39 | just confirm that it's actually working.
| | 06:41 | I'm going to close that Finder
folder and open up my web browser.
| | 06:45 | I'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:56 | to our ExerciseFiles.
| | 06:58 | I'm just going to put that in my
bookmark bar, so I can get back to it easily.
| | 07:02 | And I'm going to come over here to SID
and bring up sid.php, and there it is.
| | 07:07 | And just to make sure that I'm
accessing the database files correctly, and that
| | 07:11 | I have Read & Write Permission, I'm
going to come over here to the test.db, and
| | 07:15 | I'm going to actually create a table,
put some data in it, and read it back.
| | 07:19 | So just type along with me.
| | 07:20 | If you don't understand
the SQL now, you will later.
| | 07:23 | I'm going to create table t, and in
parenthesis, put a, b, and that will create
| | 07:31 | a table with two columns in it.
| | 07:33 | And I'm going to insert into t
values, parenthesis 1, 2 (1, 2) close
| | 07:39 | parenthesis, and a semicolon at
the end of each of these statements;
| | 07:44 | all SQL statements must be
terminated with a semicolon.
| | 07:47 | And then I'm going to select star.
| | 07:51 | That's an asterisk from t semicolon.
| | 07:54 | We'll click on the Go button.
| | 07:56 | And if everything is just perfect,
you'll get a different elapsed time probably,
| | 08:02 | but you'll see down here, this
little table, a, b, 1, 2, and that is the
| | 08:06 | result that we want.
| | 08:08 | So that means that I'm able
to open that test.db file,
| | 08:11 | I'm able to write in that test.db file,
I'm able to read in that test.db file,
| | 08:17 | and that's exactly what
we need to be able to do.
| | 08:19 | So just to put the database back in its
original state, I'm going to type 'drop
| | 08:24 | table t;' and Go.
| | 08:29 | Now that database is as it was originally.
| | 08:32 | So I'm going to make a shortcut here
in my Bookmark bar, so I can get back to
| | 08:36 | SID very easily, because
we're going to use that a lot.
| | 08:38 | I'm going to go back to
ExerciseFiles and open up CRUD.
| | 08:42 | So we're going to bring up CRUD, and
we see that that's working, and it's
| | 08:46 | accessing its database okay.
| | 08:48 | I'm going to drag a shortcut up here to
the toolbar, and then we'll go back to
| | 08:51 | the ExerciseFiles, and we'll bring up
the Testimonials and the db.php, and
| | 08:56 | that's running, so we know
that that's working right.
| | 08:59 | So we've now set up the ExerciseFiles
and made sure all our permissions are
| | 09:03 | correct, and we've tested it all, and
we're ready to go and do the exercises in
| | 09:08 | the rest of this course.
| | Collapse this transcript |
| Setting up SID and exercise files on a PC| 00:00 | In this movie, we will set up the
exercise files on a PC running XAMPP.
| | 00:05 | Now, you don't need XAMPP in
order to run the exercise files;
| | 00:09 | you can run them on any server with
PHP and SQLite of the proper versions.
| | 00:14 | So, if you're running in a different
environment and you want to follow along,
| | 00:18 | just keep in mind the principles of
what I'm doing and not the exact file
| | 00:22 | locations, because those will be
different in a different environment.
| | 00:24 | So, I'm going to start out by making sure
that XAMPP is running, and it looks like it is.
| | 00:31 | Bring up the little Control panel
over here, and Apache is running, and so
| | 00:35 | that's all very good.
| | 00:36 | Now, I'm going to find the XAMPP directory here.
| | 00:41 | So, under Computer, on the C drive, I
installed XAMPP in its default location,
| | 00:46 | which is right in the root of the C
drive, and inside of there, I'm going to go
| | 00:49 | to htdocs directory, and I've got
my exercise files on my desktop here.
| | 00:54 | If you have them at someplace else,
you want to get them from there, and I'm
| | 00:57 | going to drag it and hold down the
Control key on this PC keyboard and copy it
| | 01:03 | right into the htdocs directory.
| | 01:05 | Now, you want to make a copy of it;
| | 01:07 | you don't want to move it, because that
way you've got a working copy and should
| | 01:12 | you want to start over again,
you've got the original still there.
| | 01:15 | Now, before we go any further, there is a
setting on the PC that I want to change.
| | 01:20 | I'm going to the Control panel.
| | 01:22 | I'm going to explain to you what I'm doing.
| | 01:24 | XAMPP runs very slowly in the default
configuration here on Windows 7, and this
| | 01:30 | may be true on other versions of
Windows as well. And the reason is a cache
| | 01:34 | setting in how the hard disks are set up.
| | 01:36 | So, I'm going to go to Control panel,
Hardware and Sound and right here under
| | 01:41 | Devices and Printers, there is a Device
Manager, and under Disk drives - now I've
| | 01:46 | got two disk drives here,
| | 01:47 | I'm going to set them both, but I'll
go in the first one and Policies, and
| | 01:51 | you'll see this right Write-caching policy.
| | 01:54 | Now, Enable write caching is turned on
by default, but this Turn Off write-cache
| | 02:00 | buffer flushing on the device is
actually unchecked by default, and you want to
| | 02:04 | make sure that it's checked.
| | 02:06 | In a nutshell, the explanation for this
is that write-caching can be dangerous.
| | 02:11 | Should you have a power loss on your
hard drive while write-caching is in
| | 02:16 | effect, you could lose data.
| | 02:19 | Now, in order to mitigate that, they
came up with this write-cache buffering, and
| | 02:24 | the write-cache buffering actually
pretty much defeats the purpose of the
| | 02:28 | write-caching in the first place. And so
it slows down database writes very much,
| | 02:34 | at a difference of orders of magnitude.
| | 02:36 | Things that take a few milliseconds
end up taking hundreds or thousands of
| | 02:40 | milliseconds with this write-cache
buffering on. And just to further confuse
| | 02:45 | things, the check box is checked when
the write-cache buffer is turned off, and
| | 02:51 | it's unchecked when the write-
cache buffering is turned on.
| | 02:54 | So if all of that sounds
confusing to you, here's what you do.
| | 02:57 | You get yourself an inexpensive
battery backup device for your computer, plug
| | 03:01 | your computer into that, so you
know that you're not going to lose power
| | 03:04 | unexpectedly and then you check this
box. And that'll make you perform it
| | 03:08 | sufficient for you to be
able to use SQLite on your PC.
| | 03:12 | So, I've got that checked here.
| | 03:14 | I'm going to say OK.
| | 03:15 | I'm going to go this other one and go
under Policies, and I've got that checked
| | 03:19 | there, and I'm going to
say OK, and now I'm okay.
| | 03:23 | So I'll close the Control panel,
and now we will install the database.
| | 03:27 | So before we go any further, I'm
going to take this folder here, because I'm
| | 03:31 | going to want to get back to it, and
I'm going to put it into my Favorites.
| | 03:33 | I'm going to go to my Home directory,
and I'm going to create a folder inside my
| | 03:39 | Home directory, say New > Folder.
| | 03:41 | I'm going to call this sqlite3_data.
| | 03:46 | Then I'm going to drag that into my
Favorites, so that I can get back to it
| | 03:51 | easily, and I'm going to go to my
ExerciseFiles, and down here under SQL,
| | 03:56 | double-click on that, and you'll see in
here there is three files that say .db.
| | 04:01 | These are the database files
that we'd be using in our examples.
| | 04:04 | So, I'm going to hold down the Control
key on my PC keyboard and click on all
| | 04:08 | three of those and scroll up here and
drag these three files. And I'm going to
| | 04:13 | hold down my Control key, so I'm making
a copy, instead of moving them, so that I
| | 04:17 | have a working copy and drag them into
the sqlite3_data folder, and I'm going to
| | 04:21 | click on that and make sure they are in there.
| | 04:23 | Okay, now we've got our databases
installed exactly where they're going to go,
| | 04:27 | and we want to remember this path.
| | 04:28 | So, if you click your mouse up
here, it's just a little trick,
| | 04:31 | you'll get the path, and you can press
Ctrl+C on your PC keyboard and copy that
| | 04:35 | into your copy buffer, and
you're going to need that in a moment.
| | 04:38 | So, we're going to now click on
ExerciseFiles and scroll down here to the
| | 04:42 | bottom and click on the SID folder.
Double-click on that, and I'm going to open
| | 04:47 | sid.php in my text editor and make sure
you have a good text editor and not the
| | 04:52 | default Notepad on a PC.
| | 04:55 | The one text editor in the world that
doesn't work with most program files is
| | 04:59 | Notepad that comes with a PC.
| | 05:01 | So Notepad++ here is free, and it's an
excellent editor, and I recommend that
| | 05:07 | one, but use whatever editor works for
you - not a word processor and not Notepad
| | 05:11 | that comes with the PC.
| | 05:12 | So, I'm going to click on that, and here
is my SID. And see down here where it
| | 05:16 | says path/to/sqlite3_data? I'm just
going to take all of that, press Ctrl+V, and
| | 05:22 | there we have the path to the sqlite3_data.
| | 05:24 | I'm going to save that and close
it, and then we will go back up to
| | 05:31 | ExerciseFiles, and in the CRUD
directory, do the same thing with CRUD.
| | 05:39 | And right here, path/to/sqlite3_data,
Ctrl+V, and I want another backslash at
| | 05:45 | the end of it, because it's got the path
actually to a file there, and I'm going
| | 05:50 | to save that and close that, and we're ready.
| | 05:53 | Now, I'm going to open up the browser,
and we'll test it real quick. Type in
| | 05:59 | localhost/ExerciseFiles, like that, and
there is my exercise files. And I'm just
| | 06:08 | a big fan of bookmark, so I'm going to
drag this into my Bookmark bar, so that I
| | 06:12 | can get back to that easily.
| | 06:13 | I'm going to come down here to SID and
open up sid.php, and there is our SID.
| | 06:20 | I'm going to put that in my Bookmark bar.
Now I'm going to test it real quick.
| | 06:24 | I'm going to bring up the test.db
database, and I'm going to enter a few SQL
| | 06:28 | commands here. And if you don't
understand SQL, that's all right;
| | 06:32 | you will by the time
you're done with this course.
| | 06:34 | I'm going to create a table, create
table t, and I'm going to give it a couple
| | 06:39 | of columns, a, b, close parentheses
semicolon - the semicolon is required, and
| | 06:47 | insert into t values 1 and 2, like that,
with parentheses and a semicolon and then
| | 06:55 | select star from t semicolon.
| | 06:59 | So create table t (a, b);
| | 07:04 | insert into t values (1, 2); select * from t;
| | 07:13 | and press Go, and this is the result
you're looking for, right down here.
| | 07:17 | You have two columns a and b,
with two values 1 and 2 in them.
| | 07:21 | That means that we've successfully
been able to open this database, create a
| | 07:25 | table, insert into the table and read
from the database, so that's all working.
| | 07:30 | So, I'm going to take all this, and
I'm going to delete that table, so that
| | 07:33 | it's back in its original state, drop table
t;, and that will drop the table.
| | 07:42 | So, now we want to make sure CRUD works.
| | 07:45 | Click on CRUD, click on crud.php.
| | 07:49 | This is just a little CRUD demonstration.
| | 07:51 | CRUD stands for Create Read Update Delete;
| | 07:53 | those are the four basic functions of
a database. And we'll want to put that
| | 07:57 | down here as well in the
Bookmark bar, and there we have it.
| | 08:02 | SID is the one we'll be using the most
throughout the course, and now you have
| | 08:06 | successfully set up your
exercise files to work with XAMPP on a PC.
| | Collapse this transcript |
| Using the command-line tools| 00:00 | In this movie, I am going to show you
how to use the SQLite 3 Command Line tool
| | 00:03 | on both the Mac and the PC.
| | 00:05 | First, we'll start with the Mac.
| | 00:07 | So we are going to talk a little bit
about using the SQLite 3 Command Line
| | 00:10 | application on a Mac.
| | 00:12 | We are going to open the Terminal
application, because that's how we get to the
| | 00:15 | command line on a Mac, and this will
put us with a command line in my Home
| | 00:20 | directory here, and so it will be
your Home directory on your Mac.
| | 00:23 | So I am going to try not to assume
that you know very much about the command
| | 00:27 | line. In UNIX, this is the Bash shell,
which is a default on a Mac, and the
| | 00:32 | commands here are very similar to
what you might find in most UNIXes.
| | 00:35 | In particular, this is based on BSD UNIX,
and if you're familiar with UNIX at all, it
| | 00:40 | should all be very, very familiar.
| | 00:42 | So the first thing we are going to do
is we are going to make a symbolic link,
| | 00:45 | because getting to the exercise files
in the XAMPP directory is a little bit
| | 00:49 | circuitous on a Mac.
| | 00:51 | So I'm going to type 'ln,' which is the
command that will make a symbolic link, and
| | 00:56 | that's a lowercase l and a lowercase n;
UNIX command line is case-sensitive.
| | 01:01 | I am going to say -s, which will
make it a symbolic link, and then we are
| | 01:05 | going to navigate to the applications
folder, so I am going to type a slash and
| | 01:10 | a capital A and maybe a couple of ps and hit
the Tab key, and it will complete that for me,
| | 01:15 | so I don't have to type it all out and
perchance misspell it, and then a capital
| | 01:20 | X and a capital A, and
that's probably enough for XAMPP.
| | 01:22 | I will hit the Tab again,
and it will complete that.
| | 01:25 | And then HT, and that's
probably enough, hit Tab.
| | 01:29 | You'll notice it doesn't put the slash in for me there
because that itself is a symbolic link.
| | 01:34 | So I am going to put in the slash for
it, and then a capital E, lowercase x, and
| | 01:39 | that should be enough for our exercise files.
| | 01:41 | Now I am going to backspace over that
last slash because I don't really want
| | 01:46 | that in my symbolic link, and press the
Spacebar and the period. And so you should
| | 01:51 | have a command that looks exactly like
this. And then press Enter, and that has
| | 01:55 | now created a symbolic link.
| | 01:57 | So if I bring up a listing of my
directory, which is with ls - a, like that,
| | 02:04 | you'll see that I have ExerciseFiles
right here, and that is a symbolic link.
| | 02:09 | So I am going to cd into that, and again,
I can just type a couple of letters, and
| | 02:14 | the Tab, and now ls-al, and I can see my
entire ExerciseFiles folder right here.
| | 02:23 | That makes it easy for me to
get into my ExerciseFiles folder.
| | 02:27 | So now I am going to navigate from here
into the SQL folder, and we will look in
| | 02:32 | there, and we see that we have
these SQL files and these db files.
| | 02:37 | The db files are already made database
files, and the SQL files are actually the
| | 02:42 | source SQL for creating those files.
| | 02:45 | So one of the things we are going
to do here is we are going to use the
| | 02:48 | command line application to create one of
these db files, just so we can see how that's done.
| | 02:52 | So I am going to type 'sqlite
3,' like that, all lowercase.
| | 02:57 | So this is the command line
application that comes with SQLite 3 and is
| | 03:01 | designed for managing these databases.
| | 03:04 | So if I give it the name of
a database, say like test.db,
| | 03:08 | now I have that database open. And if
I want to see what's in it, I can type
| | 03:13 | select * from sqlite_master;
| | 03:19 | all SQL statements are terminated with a
semicolon, and there is the schema for this database.
| | 03:26 | It's got a table named customer, and here
is the create statement that made that
| | 03:31 | table, and here is the one that
created the item table, and here's one that
| | 03:34 | created the sale table.
| | 03:36 | So we have all that in there, and if
I want to say select * from customer;,
| | 03:42 | then I get the contents of that.
| | 03:44 | At anytime, you can type help
with a dot in front of it, .help.
| | 03:48 | You have to scroll
back to see the whole thing.
| | 03:50 | We get all the dot commands that are
available, plus you can just type any SQL
| | 03:55 | directly into the command line application.
| | 03:57 | So I am going to Exit and type .quit,
to get out of this and show you one
| | 04:02 | other way to use this.
| | 04:04 | If I want to type sqlite3, like that,
and give it the name of a database, I can
| | 04:09 | then just put a command in quotes.
| | 04:12 | I can say, select * from customer,
and I get the listing right there,
| | 04:19 | without having to go into the
application and type it on the command line
| | 04:22 | inside the application.
| | 04:23 | You can also redirect commands from a
file. So if I want to create a new file
| | 04:28 | that's just like test.db, I can say
sqlite3 newtest.db and use the redirect
| | 04:35 | operator from the command line,
which is the left angle bracket.
| | 04:40 | If you press Shift on the Comma key,
you will get that left angle bracket, at
| | 04:44 | least on an American keyboard, and then
type test-sqlite3.sql, and this is the
| | 04:50 | source SQL for the test.db file.
| | 04:54 | So if I do that, it has now
created a new database file.
| | 04:57 | If I look at the listing of the
directory here, we have this new test.db.
| | 05:04 | That's a new file created
from the test-sqlite3 sql.
| | 05:08 | So we can look inside that test-sqlite3.
sql and use the less command, and we can
| | 05:16 | see what's in there.
| | 05:17 | I am scrolling with j. You can scroll
with j and k in here and just press q to
| | 05:24 | get out of it, and so you can see all the SQL
in there that's used to create the test.db.
| | 05:30 | So that's how you use the SQLite 3
command line application on a Mac.
| | 05:34 | Now I am going to show you how to use the
SQLite 3 command line utility on the PC.
| | 05:41 | First, we are going to locate the
utility, and so I am going to open my Computer
| | 05:46 | and go in the C drive, and I have
installed XAMPP in the default location, and
| | 05:52 | so I am going to go in the apache directory.
| | 05:55 | This is not intuitive where it is.
And it's in the bin directory under
| | 05:58 | the apache directory.
| | 06:00 | Scroll down here, and there it is.
| | 06:04 | So what I want is this path up here.
| | 06:07 | So I am going to do my little
trick here that works on Windows 7 -
| | 06:10 | I believe it also works on Windows Vista,
where I put my mouse in this little box
| | 06:15 | up there, and it will give me that path,
and I can just press Ctrl+C and copy
| | 06:20 | it. And this is going to come
in really handy here in a minute.
| | 06:24 | Now 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:34 | When I press Enter, it will ask me for
a name, and I am just going to say, SQL,
| | 06:39 | because what this is going to be is
it's going to be a command prompt in the
| | 06:43 | folder that has the default SQL stuff in it.
| | 06:47 | Press Enter for finish, and now we have that.
| | 06:49 | But before you run it, I'm going to right-click on
that shortcut and bring up Properties.
| | 06:53 | I am going to do a couple of things first here.
| | 06:55 | I am going to just move this out of
the way, and I am going to get another
| | 07:00 | Explorer window, that's computer
over here, and I am going to go to my
| | 07:04 | ExerciseFiles and click on SQL, and
bring my cursor up there and copy that path.
| | 07:11 | And that's the path that I
am going to paste in here.
| | 07:14 | So the Start in directory is going to
be there, because that's where we have our
| | 07:18 | SQL stuff, and that's where we're
going to want the command line utility.
| | 07:21 | So I will say OK, and now we have this
shortcut that brings us right there, and
| | 07:27 | there is that directory.
| | 07:28 | So I will quit that, and then I am going
to put this back in my Copy buffer.
| | 07:34 | And now I am going to add this to the
command execution path, so I can just type
| | 07:38 | sqlite3, and it will bring that up.
| | 07:41 | So I am going to go to my Control panel,
and System and Security, and down here
| | 07:48 | to System. Then I am going to
click Advanced system settings.
| | 07:53 | For some reason, that's where they stuck this.
| | 07:55 | There is a button down here that says
Environment Variables, and I am going to
| | 07:58 | scroll down here to the one that
says Path, and I am just going to
| | 08:02 | double-click on that.
| | 08:03 | And then all the way out at
the end, I am pressing my End key,
| | 08:06 | I press a semicolon and paste my copy
buffer there. Press OK and OK and OK and
| | 08:15 | red X. And now when I bring this up, I
am there in my SQL directory, and I can
| | 08:20 | type 'sqlite3' and press Enter, and I
have got the SQLite command line utility.
| | 08:28 | So all that setup stuff, you don't ever
need to do that again; all you need to
| | 08:31 | do is double-click on this shortcut icon,
and type in sqlite3 up here, and there it is.
| | 08:37 | Now in this utility, you can type .help,
and you get a full bunch of help here.
| | 08:43 | I can scroll up and see all of that,
and that's all the dot commands that work
| | 08:49 | inside the SQLite 3 command line utility.
| | 08:52 | I am going to type '.quit' and get out of
it, and dir, and we see our databases.
| | 08:58 | So the way this works is you type
sqlite3 and the name of a database.
| | 09:02 | So in this case I am going to bring up
the test.db database and press Enter, and
| | 09:07 | that's been open now and just select
* from sqlite master, sqlite_master;
| | 09:16 | and so there is the sqlite master
pseudo table for the test.db database.
| | 09:21 | So if I want to, I can
say select * from customer;,
| | 09:27 | and I get the customer table, and .quit.
| | 09:31 | I can also do this from the command
line without actually going into the
| | 09:35 | utility interactive mode.
| | 09:36 | I can say sqlite3 test.db and then
put in quotes "select * from customer;"
| | 09:47 | and I can get it right there as well.
| | 09:49 | One more thing that I can do that's
useful with this, if I want to create a
| | 09:53 | new database, what I have here is I have these
little SQL scripts, if I bring one of these up
| | 10:01 | in my editor, you can see here's test sqlite3.
| | 10:06 | You see this is the actual script that
created the database. And if I want to
| | 10:10 | recreate the database, I can say
sqlite3, and give it a new name, so
| | 10:16 | testworking.db and redirect from,
which is the left angle bracket and then
| | 10:23 | the name of the file,
| | 10:24 | so test-sqlite3.sql, and now I have
created a new database, test-working.db, and
| | 10:34 | that one will work just like the other one.
| | 10:36 | Sqlite3test-working.db and "select * from
customer;" and there we have the same data.
| | 10:48 | So I am just going to delete that file,
test-working.db, and that's gone now.
| | 10:57 | In this movie, I have shown you how to
use the SQLite 3 command line utility
| | 11:01 | on both the PC and the Mac, so that
you're able to work with your databases
| | 11:06 | from the command line, and even
recreate the databases from the raw SQL,
| | 11:10 | should you need to do that.
| | Collapse this transcript |
|
|
3. Creating a DatabaseUnderstanding databases and tables in SQLite| 00:00 | SQLite is different than other database
management systems in two significant ways.
| | 00:04 | First, SQLite is a database
management system in a driver, and two, SQLite
| | 00:10 | stores an entire database in one file.
| | 00:13 | So SQLite itself, the code base that
comprises the database management system,
| | 00:18 | lives inside your application as a driver.
| | 00:21 | So when it needs to access its
database, which is contained in a file,
| | 00:25 | SQLite must first go through the application,
and then the application accesses the file.
| | 00:31 | What this means is the directory where
you're creating your files, and the files
| | 00:36 | themselves, must be
accessible by the application.
| | 00:40 | So the application, whatever user it's
running under, has to have access to that
| | 00:44 | file, read and write access to the
file, and read and write access to the
| | 00:49 | directory the file is contained in,
because SQLite creates and destroys a few
| | 00:54 | temporary files along way, as
it's managing your database.
| | 00:59 | This becomes a little bit more complicated
when your application is a web application.
| | 01:05 | What this means is that the
application itself lives inside the web server.
| | 01:09 | So when SQLite needs to access the
file, it must first go through the
| | 01:13 | application, and then the application
must go through the web server, and then
| | 01:18 | the web server accesses the file.
| | 01:21 | So this means that the database file
itself, and the directory of the database
| | 01:25 | file it's living in must have permissions that
will allow read and write access to the web server.
| | 01:31 | So, especially if your web server
is a shared UNIX web server, or it's
| | 01:36 | running on a Mac, the web server
itself is probably running as a user
| | 01:42 | different than your login user.
| | 01:45 | This means that your directory and your
files must have read/write access to everyone.
| | 01:50 | Keep in mind also that SQLite stores all of
the data for a given database in one file.
| | 01:57 | This means that all of your
tables are stored in this one file.
| | 02:01 | This can be very convenient for small
and medium-sized applications, but it also
| | 02:06 | is significant when you're planning
the permissions for your files, and we'll
| | 02:11 | talk more about this as we go on.
| | 02:13 | So, as you create your databases and
tables, be aware of how SQLite creates and
| | 02:18 | accesses database files.
| | 02:20 | Maintain permissions so that the
SQLite driver, and its host application, and
| | 02:25 | even the web server has access to the
directories and files that it needs.
| | 02:29 | This will keep your development and
maintenance cycles smooth and stress free.
| | Collapse this transcript |
| Creating a database| 00:00 | Creating a database in SQLite is very simple.
| | 00:03 | A database is created automatically when
you open it, if it doesn't already exist.
| | 00:07 | Let's take a look at how this is done in PHP.
| | 00:10 | First, we'll make a working copy of
start.php, in the Chap03 of the exercise
| | 00:15 | files, and we'll name it createdatabase.php.
| | 00:20 | Open that up in our text editor,
and we'll go up here to the top, and
| | 00:24 | we'll define a file name.
| | 00:26 | Define ('DATABASE' , '), and we'll put
in the path, which in the case of this
| | 00:35 | machine, is in my user
directory, under sqlite3_data.
| | 00:43 | We'll call it chap03.sqlite.
| | 00:47 | So this is the folder location and the
file name for the file that will contain
| | 00:52 | the SQLite database.
| | 00:53 | You'll notice here, in my file system,
that this is the folder, sqlite3_data, and
| | 00:59 | that's in my home directory, and
that's where the database will end up.
| | 01:03 | Now, we'll come down here, and we'll
check to see if the file exists. And we'll
| | 01:08 | use the file_exists built-in
function in PHP, and the file name is this
| | 01:14 | constant DATABASE, and there will be
an else here, and we'll just put the
| | 01:20 | structure in place.
| | 01:23 | So, if the file exists, we'll just put
on a message that says Database and give
| | 01:29 | it the file name 'already exists,'
and give it the file name here.
| | 01:38 | If the file doesn't
already exist, we'll create it.
| | 01:40 | So we'll use a try block and catch(Exception).
| | 01:46 | I am going to be using the SQLite 3
interface, so we use regular exceptions,
| | 01:51 | instead of PDO exceptions.
| | 01:56 | If we get an error, we'll just print that error
out, and the error message is in $e getMessage.
| | 02:14 | Now here's where we actually create
the database. $db, that will our variable
| | 02:20 | for holding the object.
| | 02:22 | New SQLite 3, that's the class for the
database management interface and the file name.
| | 02:31 | After we've created it successfully,
we'll go ahead and put on a message that
| | 02:35 | say that we've done that.
| | 02:47 | That's all there is to it.
| | 02:49 | So, really, the database is created
in this one line of code right here.
| | 02:53 | The rest of this is telling us what
we've done, deciding if we are going to do
| | 02:56 | it, and handling errors.
| | 02:59 | We'll go ahead and save this, and we'll
load it up in the browser, and see what
| | 03:04 | happens. So this is in Chap03 and this
createdatabase.php, and there it says that the
| | 03:10 | database was successfully created.
| | 03:13 | So that's this message here, and that means
that we've successfully created the database.
| | 03:18 | Let's go ahead and look in the
file system, and there it is.
| | 03:21 | Now, there are, of course, a couple
of other interfaces, and let's just a
| | 03:25 | quick look at those.
| | 03:26 | We can do this with the PDO interface,
which is the way that I normally like to do it.
| | 03:30 | So, we'd used PDO here for the class,
and PDO requires what's called a DSN, which
| | 03:37 | is basically a way of defining which
database system it's going to be using as
| | 03:42 | well as the file name,
and now it looks likes this.
| | 03:49 | Now, we want to report that it's PDO
that we are working with here, and we want
| | 03:53 | to use PDOExceptions.
| | 03:55 | So I'll go ahead and save that and reload.
| | 04:00 | Now, we know that the file already exists,
so it should tell us that, rather than
| | 04:04 | creating a database, and there
it's says the file already exists.
| | 04:07 | So we need to go over here into our file
system and delete the file. There we go.
| | 04:13 | Now when we reload, it's says
Database is successfully created.
| | 04:17 | So, now PDO has successfully created a
database, and there's the database there.
| | 04:22 | Finally, using the CRUD interface, the
bwSQLite 3 interface, that look like this.
| | 04:29 | Again, this is all just very
simple, and we don't need all of that,
| | 04:33 | just the file name there.
| | 04:35 | We are still using PDO exceptions.
| | 04:40 | In this case, we're going to
need to bring in the library.
| | 04:44 | So that's done with require_once and
the path name for the library, which is in
| | 04:52 | the lib folder, like that.
| | 04:57 | So we'll save that, and we'll go ahead
and delete the database from the folder
| | 05:03 | there and reload, and there it
is. It's successfully created.
| | 05:07 | Now it already exists,
because we just created it again.
| | 05:10 | So if we delete this, and
there we go, successfully created.
| | 05:17 | So, those are the three different
interfaces that we have here on this system,
| | 05:21 | and you can see that creating
a database is a very simple.
| | 05:24 | The database is created by SQLite, by default,
if it doesn't already exist when it's opened.
| | 05:29 | Keep in mind that your host
environment, that is the web server in this case,
| | 05:33 | must have read, write, and create permissions
in the folder when you create the database file.
| | 05:38 | So, for more information about
permissions, take a look at the database
| | 05:42 | permissions lesson in this chapter.
| | Collapse this transcript |
| Setting permissions for a database| 00:00 | One of the defining characteristics of
the SQLite database management system is
| | 00:04 | that it keeps each database in a single file.
| | 00:07 | If your database is going to run in a
UNIX-based environment, which includes many,
| | 00:10 | if not most, web servers, then
you'll need to understand how to set file
| | 00:15 | permissions so that your
application can access the database.
| | 00:18 | Here we are working on a Mac, running
Mac OS X, which happens to be UNIX-based,
| | 00:23 | and so we have this issue of file permissions.
| | 00:26 | This is the directory where the data is
being kept, and if I right-click on this
| | 00:31 | and bring up Get Info - if you also on
a Mac, follow along if not, just watch -
| | 00:36 | you'll notice down here at the bottom
we have the permissions for this folder.
| | 00:39 | It says Read & Write permissions
for Me, and it says Read & Write
| | 00:43 | permissions for everyone.
| | 00:45 | That's what allows the web server, which
runs as a different user, to be able to
| | 00:49 | create files in that directory.
| | 00:51 | If I change this to Read
only, which is the default,
| | 00:54 | when I created that directory this
was the way that it came up, and then I come
| | 00:58 | in here and tried to run my createdatabase
database script, you'll notice that it fails.
| | 01:02 | It says "bwSQLite could not create
database," "unable to open database file."
| | 01:08 | That means that the permissions on
the directory were not allowing the web
| | 01:11 | server to create a file in that directory.
| | 01:14 | So, if I come back over here, and I
change this to Read & Write for everyone and
| | 01:21 | then I run this program again,
| | 01:23 | you'll see that it successfully created
the database, and there it is in the folder.
| | 01:28 | You'll also notice that when I Get Info
on that files that it's owned by nobody.
| | 01:34 | The web server is running as a user called
nobody, and this is typical of shared web hosts.
| | 01:40 | So, in order for that nobody user to be
able to create files in this directory,
| | 01:45 | the permissions on the directory itself
which is owned by Me, must allow Read &
| | 01:51 | Write permissions for others.
| | 01:53 | On the Mac, that's called everyone; on some
operating systems it's called others, or world.
| | 01:58 | Now, if you're not running on a Mac, or
even if you are running on a Mac and
| | 02:03 | your script is going to be running on
a server some place that happens to be a
| | 02:06 | UNIX-based server, like many of them
are, then you'll need to know how to set
| | 02:10 | these permissions on the remote server.
| | 02:12 | So let's take a look at how we do that.
| | 02:15 | Here I have a program called Transmit
which is the file transfer program, the
| | 02:18 | FTP program that I'm using.
| | 02:21 | It doesn't matter what FTP program
you're using or if you are using something
| | 02:24 | else on a different operating system;
| | 02:26 | the techniques are going to be the
same. The actual keystrokes and button
| | 02:30 | presses might be a little bit different,
but here is what you are going to look for.
| | 02:33 | This is the folder where I am
keeping the data on that server.
| | 02:37 | When I right-click on it, and usually if
I am in a program that I'm not familiar
| | 02:40 | with, and I am looking for how to do this,
| | 02:41 | that's going to be the first thing
I'm going to try, so I am going to right-click on
| | 02:44 | it, and I am going to select Get Info here.
| | 02:47 | You'll look for something in this pop-up
menu, or in one of the menus at the top of
| | 02:51 | the program that says something about
setting permissions or getting info or
| | 02:56 | something like that on the file, where
you might expect to find this dialog box.
| | 02:59 | Here is the dialog box that we are looking for.
| | 03:02 | This is the information on that file.
| | 03:03 | It's got its file Size.
| | 03:05 | It's got the Modified date.
| | 03:06 | It says who the owner of the file is.
| | 03:09 | That's the User account that
created it, and then it's got the
| | 03:12 | permissions themselves.
| | 03:13 | The permissions are often
displayed in several different forms.
| | 03:17 | Typically, if you're a UNIX person like I am,
| | 03:20 | you'll recognize this;
| | 03:21 | this is the octal bits of the file.
| | 03:24 | Anybody who has worked with UNIX
for a long time knows with that means.
| | 03:27 | If you don't know what that
means, don't worry about it.
| | 03:29 | It's automatically updated.
| | 03:31 | You are going to use these check boxes
here, and you'll notice that there are
| | 03:34 | permissions for User.
| | 03:36 | In this case, that's the
same as Owner, billw - that's me.
| | 03:39 | There are permissions for Group, and you
are not going to worry about that and all.
| | 03:43 | In this case, that's also me because
this is owned by me without being set to a
| | 03:47 | group. And then there are permissions
for World, which is the same as everyone on
| | 03:51 | the Mac or other on some other
operating systems, and here it says world.
| | 03:55 | So, that's the one that you are
concerned with, because the web server is running
| | 03:59 | as a different user than the owner of the file.
| | 04:02 | So, here it's checked Read, Write and Execute.
| | 04:04 | In the case of the directory, or a
folder, the Execute bit serves a different
| | 04:09 | purpose than it does with regular files.
| | 04:11 | The Execute bit is the bit that says
that it's a directory, so you need to
| | 04:14 | leave that checked.
| | 04:16 | But if I were to uncheck this one here,
which is Write permissions for World -
| | 04:21 | you'll notice that these
other displays get updated;
| | 04:23 | you need to worry about that -
| | 04:25 | and I press Apply, and now I go in, and I
run the createdatabase script on the
| | 04:30 | server which I have loaded up over here,
| | 04:33 | you see, I get that same error;
| | 04:35 | "could not create the database,
unable to open database file."
| | 04:39 | Now, if I come back here and bring this
up again, that's still up there actually,
| | 04:43 | and I checked that right and I press Apply,
| | 04:47 | now I go back over to the web
browser and run the script again,
| | 04:50 | you see, it successfully created the file.
| | 04:53 | So, we'll close this, and we need to
reload the directory because it's on a
| | 04:57 | remote server so it doesn't do that
automatically, and there is the file that we
| | 05:01 | created from that script.
| | 05:03 | So, understanding file permissions is
going to save you a lot of trouble and
| | 05:07 | headaches if your database application
is going to run on a UNIX-based system.
| | 05:11 | Keep in mind that the host
application, that's the web server in this case,
| | 05:16 | contains the SQLite driver, and it must
have Read, Write and Create permissions
| | 05:20 | in the folder where you
create the database file.
| | Collapse this transcript |
| Defining a table in SQL| 00:00 | To create a table in SQLite, you simply open
a database and use SQL to create the table.
| | 00:06 | Here on the screen, I have SID, which is
the SQL Interactive Demonstrator that I
| | 00:09 | created for demonstrating and
teaching SQL in this, and my other SQL-related
| | 00:14 | courses on lynda.com.
| | 00:16 | You don't have to use SID;
| | 00:18 | you have it in your exercise
files if you wish to use it.
| | 00:22 | You can really use any environment
that'll allow you to type in SQL and see
| | 00:26 | the results using an SQLite 3
database, including, if you like, the SQLite 3
| | 00:32 | Command Line interface that comes with SQLite 3.
| | 00:36 | So let's select the test.db, and we're
going to start by looking at the tables
| | 00:41 | that are already in the database, and
you do that with a special table in SQLite
| | 00:45 | called SQLite Master.
| | 00:48 | If we SELECT * FROM SQLITE_MASTER and
then press Go, you'll notice that there's
| | 00:56 | no table called SQLite_Master.
| | 00:58 | It's a virtual table for the purpose
of seeing what's in the database, and so
| | 01:02 | here we have a column for
type, and these are all tables.
| | 01:04 | There are other things that may be
listed in here, like indexes and such, and the
| | 01:09 | name of the table, and the SQL
that was used to create the table.
| | 01:13 | So let's go ahead and create a new table.
| | 01:15 | I am going to use the SQL:
| | 01:18 | CREATE TABLE, and we'll just
call it t. We'll have three columns.
| | 01:24 | The first one will be an INT, and the second one
will be a REAL, and the third one will be TEXT.
| | 01:32 | Now these type definitions are a
little bit different in SQLite than they
| | 01:36 | might've been in other
databases that you are used to.
| | 01:39 | For now, we'll use these, and later in
the course, we'll talk in a lot of detail
| | 01:42 | about SQLite's unique type system.
| | 01:45 | So we'll go ahead and execute that, and then
we'll take a look again at the SQLite Master,
| | 01:52 | SELECT * FROM SQLITE_MASTER, and we
will see that our table is now there,
| | 02:00 | table t, and there is the SQL
that we used to create the table.
| | 02:04 | So that's really all there is
to creating a table in SQLite.
| | 02:08 | You just open the database and use SQL
to create the table, and see the chapter
| | 02:13 | on SQL datatypes for more information
about type definitions, and the unique way
| | 02:18 | that they're used in SQLite 3.
| | Collapse this transcript |
| Creating a table in PHP| 00:00 | When you run your database application
in PHP, it can define its own tables to
| | 00:04 | make installation easier.
| | 00:05 | Let's take a look at how that's done.
| | 00:07 | So we'll start by making a working copy
of start.php, in our Chap03 folder, in the
| | 00:12 | exercise files, and we'll name that
createtable.php, open that in the editor.
| | 00:20 | The first thing we want to do is
define the name of the table.
| | 00:26 | That's in my Home folder, in
sqlite3_data on this Mac here.
| | 00:31 | Of course, you'll use whatever location
is going to work on your computer where
| | 00:38 | you've got the data files for your exercises.
| | 00:41 | I am going to use the test.db database,
and that way, we can look at it, and see
| | 00:50 | it, and see the results of what we're doing.
Now, we'll go ahead and open the database.
| | 00:56 | We start with the try block
for catching our exceptions.
| | 01:01 | We're going to be using
the PDO interface for this.
| | 01:05 | So we'll catch PDO exception, and
we'll put in an error message for that.
| | 01:13 | Now, we'll open the database, and
that's how we open a database in PDO.
| | 01:34 | Now, we create the table, and we'll use
SQL for this, and this is not a select
| | 01:41 | statement, so we can go ahead and use
the exact function from PDO, and that'll
| | 01:45 | allow us to prepare and execute all in one step.
| | 01:49 | CREATE TABLE IF NOT EXISTS t a b
and c, and that's our column names.
| | 01:59 | So we're using the if not
exists clause to create a table.
| | 02:02 | That way if the table already exists
and already has data in it, we're not
| | 02:06 | erasing the table and erasing all of
our data, and that's a good thing.
| | 02:11 | I'll just put up a message that we
have successfully created the table;
| | 02:15 | Table t successfully created.
| | 02:21 | So that should create a
table in our test database.
| | 02:25 | So let's start by looking at the
test database, and we'll select the test
| | 02:30 | database from our Database dropdown and
look at the SQLite_Master table,
| | 02:35 | and that shows us that we
just have the three tables
| | 02:44 | originally defined in the database.
| | 02:46 | So now we go over here, and
run our createtable.php script.
| | 02:53 | We see Table t successfully created,
and when we look in SID, we can just press
| | 02:57 | Go again, and we now have that table created.
| | 03:00 | So with this technique, when you write
your script and put this in your script,
| | 03:04 | in your init function or some place like
that, every time the script starts up,
| | 03:09 | it make sure that it has
the tables that it needs.
| | 03:12 | So if the script has never been run
before, it will create an empty database and
| | 03:16 | an empty table, and allow you to get
going without you having to distribute a
| | 03:19 | separate file with the
database in it along with the script,
| | 03:22 | so that's a nice and handy convenience.
| | 03:25 | So when you write a database
application in PHP, you can use this technique to
| | 03:30 | create your own tables and
make installation easier.
| | Collapse this transcript |
| Creating indexes| 00:00 | Indexes are useful for enhancing performance
for searches, joins, and ordered lookups.
| | 00:05 | The cost is slower inserts and updates
and greater storage space requirements.
| | 00:11 | SQLite uses standard SQL for creating indexes.
| | 00:14 | So let's take a look at
how this works in SQLite.
| | 00:16 | We will use the world database here, and
first let us take a look at the database
| | 00:22 | itself, using the SQLite MASTER TABLE.
| | 00:30 | SELECT * FROM SQLite_MASTER.
| | 00:33 | This is a special table in SQLite.
| | 00:34 | It is a virtual table.
| | 00:36 | It is not actually stored in the
database, and it gives us information about
| | 00:41 | the database itself. And so here we see,
we have three tables City, Country, and
| | 00:46 | Country Language, and this is their
definitions, and it has an index already in
| | 00:51 | the country and language table. And
that is because we had over here PRIMARY
| | 00:55 | KEY in the definition.
| | 00:58 | So let us enter a little joined query.
| | 01:00 | We are going to look at the average
population of the cities in the City table,
| | 01:03 | organized by country.
| | 01:05 | So we will say SELECT co.Name and then
use the average function, and we will
| | 01:13 | aggregate on ci.population, and we
will give it a name, average population,
| | 01:20 | because we are going to use it
elsewhere in this query, FROM city AS ci.
| | 01:27 | We are going to JOIN country AS
co, and we will do our join ON
| | 01:35 | co.Code=ci.CountryCode, so that's how those two
tables are related, and we'll group by
| | 01:46 | ci.CountryCode, and ORDER BY our average
population descending. Now if some of this
| | 01:57 | doesn't make sense to you, that's okay.
This is all going to be explained later on.
| | 02:00 | But for now the point is that we are
doing a joined query, and this is going to
| | 02:06 | be a little bit slow, because we are
looking up several things in a couple of
| | 02:10 | different tables, and going back
and forth between the two tables.
| | 02:13 | It is one of the least efficient
things you can do on an un-indexed table.
| | 02:17 | So I am going to press Go here,
and we see that this took 300 milliseconds, roughly.
| | 02:22 | Now, I am going to create some
indexes and make this query go a lot faster.
| | 02:32 | CREATE INDEX IF NOT EXISTS, and that
will allow me to run this create index. Even
| | 02:38 | if the index does exist, it just
won't do the creating of the index.
| | 02:42 | And I am going to call this
one co code ON Country Code.
| | 02:49 | So that is the code column in the country table.
| | 02:52 | I'm going to create another index,
| | 02:58 | call it ci Code, and this will be on
the city table, and it is the Country code
| | 03:05 | column in that table.
| | 03:07 | So this is where the join is.
| | 03:09 | The join is on these two
columns, in these two tables.
| | 03:14 | It is on the code column in the country
table, so this is the country table, and
| | 03:18 | the code column, and the country
code column in the city table.
| | 03:23 | So I am creating indexes for both of
those columns, and that should make this
| | 03:28 | query go a lot faster.
| | 03:29 | So here we've got 232 rows in
295, almost 300 milliseconds.
| | 03:34 | So when I run this query again,
and this is with the time it takes to
| | 03:38 | even create the indexes,
| | 03:40 | see, it just took 32 milliseconds.
| | 03:41 | It takes a 10th as much time.
| | 03:44 | And now that the indexes are created, I
am just going to comment these out with
| | 03:48 | SQL comments here, which are two dashes,
and now those two lines won't run.
| | 03:52 | When I run this again, you see
now it just takes 15 milliseconds.
| | 03:56 | So that's 5% of the time that it
took to run it without the indexes.
| | 04:01 | So indexes are very powerful.
| | 04:03 | They can slow things down
a little bit on inserts,
| | 04:06 | so you want to take that into
consideration as you are designing your indexes,
| | 04:09 | that it increases the amount
of time it takes to do inserts.
| | 04:12 | So if you have got a table where you're
more inserts than reads, the indexes aren't
| | 04:17 | going to buy you as much, and they may
actually cost you more than you buy.
| | 04:20 | So you want to take these things into
consideration, as you are creating your indexes.
| | 04:25 | Indexes are created in
SQLite using standard SQL.
| | 04:28 | Once created, the database will use
them automatically, so be sure to think
| | 04:33 | about disk space and insert update
times when you are designing a database with indexes.
| | Collapse this transcript |
| Indexing ID fields| 00:00 | SQLite has a very easy, fast, and
powerful feature for implementing ID fields.
| | 00:05 | Every row of every table has a
sequential id, by default, called row ID, and you
| | 00:10 | can access this by selecting for it,
using row id or one of its aliases.
| | 00:15 | Let us take a look at how this works.
| | 00:17 | If I look at the country table, for
example, in the world database, I will just
| | 00:22 | say SELECT Code FROM Country,
| | 00:26 | you see, we get 239 rows, and
we have got all of these codes.
| | 00:31 | Let us say that I wanted an id number for
each of these, a unique number for each row.
| | 00:36 | Well this table doesn't
actually have one defined.
| | 00:38 | If I look at the SQLite MASTER for this
database, we see that the country table
| | 00:48 | does not have an id field.
| | 00:50 | It has the code field, and that is
really its master index, and name and
| | 00:55 | continent like that, but there is no id field.
| | 00:58 | Now, if I wanted a unique id for each of
these rows, I can look at the row id like this.
| | 01:08 | Instead of *, I am going to say ROWID and code.
| | 01:15 | Now we can see there is a unique
number for each row in the table that goes
| | 01:20 | all the way to the end.
| | 01:23 | And so this is a feature built-in that
exists in every table, whether or not you
| | 01:28 | have defined it, and by default, it is
called ROWID or OID, like that, or _ROWID_
| | 01:38 | with underscores, and these are
simply there for compatibility with other
| | 01:43 | database functions.
| | 01:44 | SQLite allows you to give an alias to
this field, and you will notice when we
| | 01:50 | looked at the SQLite MASTER for this
database, that the city table has ID
| | 02:02 | INTEGER PRIMARY KEY, like that, and this
is how you create an alias for that row id.
| | 02:08 | So let us take a look that how this is done.
| | 02:09 | We are going to use our in memory database now.
| | 02:12 | Its SQLite MASTER is empty
because it is an in memory database.
| | 02:16 | Every single time we press this Go
button, we get a brand-new empty database,
| | 02:19 | and it is in-memory.
| | 02:21 | So I am going to create a table here.
| | 02:22 | I am going to CREATE TABLE, call it t. I
am going to give it a field called id,
| | 02:28 | and I am going to say
INTEGER PRIMARY KEY, like that.
| | 02:33 | Now, it needs to be all
spelled out, just like that.
| | 02:36 | If you say int primary key, it will not work.
| | 02:41 | And now, I am going to
insert some values into it.
| | 02:43 | Remember, every time I press the Go button,
I will get a brand-new blank database
| | 02:48 | because this is an in-memory database.
| | 02:49 | So I am going to INSERT INTO t, I am
just going to insert into the a, b and c
| | 02:56 | columns, and I am going to give it some values.
| | 03:01 | I am just going to say a, b, and c, like that.
| | 03:04 | Now we will go ahead, and
we will do a few of these,
| | 03:06 | so I am going to just copy and paste.
| | 03:08 | I will give it five rows, and then we
will SELECT * FROM t, and we will take a
| | 03:14 | look at the SQLite MASTER as well.
| | 03:16 | We'll say Go, and you will notice that
SID has some special things that it does
| | 03:26 | when there is multiple statements.
It tells us how many queries total, how many
| | 03:29 | non select queries affected, how many rows.
| | 03:32 | This is all happened in less than a
millisecond because this is an in-memory database.
| | 03:36 | Now you will notice that I inserted into
these, but I only inserted the a, b and
| | 03:40 | c. I did not insert anything in the id
field, and yet the ID field got populated
| | 03:46 | with sequential numbers.
| | 03:48 | So the way this works is that the
database engine will assign the next highest
| | 03:53 | integer to the id field.
| | 03:55 | So if I were to come in here and delete
one and then insert another row after it,
| | 04:07 | so we will delete number 3, and we will
insert one more, the next one should still be 6.
| | 04:13 | It does not reuse that number. If it runs
out of numbers, and this is a 64 bit integer,
| | 04:19 | so it would take a while
for it to run out of numbers,
| | 04:22 | it will try to find some spaces where it
can still insert a unique number. And if
| | 04:28 | that fails, and it just
fails to insert anymore rows.
| | 04:31 | Now if I were to have deleted number
five, let us see what happens then.
| | 04:36 | You see, we still get a 5.
| | 04:40 | There is another behavior available.
| | 04:42 | If I use the keyword AUTOINCREMENT
when I am defining this table, now the next
| | 04:49 | one will be 6 instead of 5.
| | 04:51 | The reason for that, and you will
see that we got another table in here
| | 04:55 | called SQLite Sequence,
| | 04:57 | this changes the behavior
of how it assigns the ids.
| | 05:01 | This will make it one more than the
largest number that was ever in that column,
| | 05:06 | and it keeps track of what the
largest number was in this special SQLite
| | 05:11 | sequence 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:20 | Let us go ahead and copy both of these rows.
| | 05:24 | If I delete this 6 and insert yet one
more row, then it will have a 7 there,
| | 05:31 | because it is remembering that six
was now the largest number that had ever
| | 05:35 | been in that column.
| | 05:36 | So that behavior is available
with the autoincrement.
| | 05:39 | Most of the time, you are just going to
use it without the autoincrement,
| | 05:42 | and it will just use one larger than
the large number that is already in that
| | 05:46 | column, and it won't create that
extra special SQLite sequence table.
| | 05:51 | Now ID fields can be extremely useful,
especially in applications with several
| | 05:55 | tables that interact frequently.
| | 05:57 | We use SQLite's integer primary key
feature, and it costs virtually nothing, because
| | 06:02 | that row id is already part of
the structure of the database.
| | 06:06 | So SQLite does not actually have
to create space for another column;
| | 06:10 | it simply aliases the row id to
act like a column in the table.
| | 06:15 | So it costs virtually nothing because of that.
| | 06:18 | I tend to just include an id INTEGER
PRIMARY KEY column on every table that I
| | 06:23 | create, and I strongly
recommend that you do the same thing.
| | Collapse this transcript |
|
|
4. SQLite Data TypesTaking care with data types in SQLite| 00:00 | Data types in SQLite 3 are
significantly different than they are in the other
| | 00:04 | database management systems
that you may be familiar with.
| | 00:07 | Most database management
systems use static typing.
| | 00:11 | That means that the type of a value is
determined by the type of the column that contains it.
| | 00:16 | So if you put a number in a TEXT
column, that's still going to be TEXT.
| | 00:23 | On the other hand, if you try to put
text in a numeric column, it's probably not
| | 00:27 | going to work, and you are
going to get an error of some sort.
| | 00:29 | SQLite is different.
| | 00:31 | SQLite uses something called manifest typing;
| | 00:35 | this means that the type of the
value is determined by the value itself.
| | 00:39 | Now, SQLite is syntactically
compatible with statically typed SQL.
| | 00:46 | It just manages the data types differently.
| | 00:49 | SQLite manages data types with the
concepts of storage class and Type Affinity.
| | 00:55 | Every value stored in a
table has a storage class.
| | 00:59 | Now, a storage class is
more general than a data type.
| | 01:03 | SQLite has five storage classes:
| | 01:05 | NULL, INTEGER, REAL, TEXT, and BLOB.
| | 01:10 | BLOB stands for Binary Large Object.
| | 01:13 | Each storage class may
support more than one storage type.
| | 01:18 | The NULL storage class is
only used for the NULL value.
| | 01:22 | The INTEGER storage class
is assigned an integer value;
| | 01:26 | it may be stored as 1, 2, 3, 4, 6, or 8 bytes.
| | 01:32 | The REAL storage class is for a
floating point value, and it's always stored as
| | 01:36 | an 8-byte IEEE floating point number.
| | 01:39 | The TEXT storage class is a text string,
and it's stored using the database encoding:
| | 01:44 | either UTF-8 or UTF-15.
| | 01:47 | The BLOB storage class is for a blob of
data, and it's stored exactly as provided.
| | 01:52 | SQLite has no Boolean class.
| | 01:55 | Booleans are stored as
integer values, either 0 or 1.
| | 01:59 | Likewise, SQLite has no date or time class.
| | 02:02 | Dates and times are stored as TEXT,
REAL, or INTEGER, depending upon
| | 02:07 | the representation.
| | 02:08 | SQLite also has this concept of Type Affinity.
| | 02:12 | Type Affinity is a type
recommendation for a column in a table.
| | 02:16 | So Type Affinity creates a preference.
| | 02:18 | It does not create a requirement.
| | 02:21 | Any type may still be stored in any column.
| | 02:24 | When you define a column with a
Type Affinity, you're simply creating a
| | 02:28 | preference for the way that SQLite
looks at the data when it's deciding how to
| | 02:33 | store it in the table.
| | 02:35 | If there may be some room for
ambiguity, it will favor a certain type over
| | 02:39 | another type, based on the Type Affinity.
| | 02:42 | Type Affinity may be NONE,
TEXT, NUMERIC, INTEGER, or REAL.
| | 02:47 | And Type Affinity is based
upon the column definition.
| | 02:50 | When you define a type in a column,
you are defining a Type Affinity.
| | 02:55 | The TEXT Affinity stores all
values as NULL, TEXT, or BLOB.
| | 03:02 | If you try to store a number in a
column defined with a TEXT Affinity, it
| | 03:07 | will store it as TEXT.
| | 03:09 | You define a column with TEXT
Affinity by defining your column type with
| | 03:14 | anything that has the characters
C-H-A-R, C-L-O-B, or T-E-X-T in the type.
| | 03:20 | For example, if you say VARCHAR, that
contains the letter C-H-A-R, and so that
| | 03:27 | column will have the TEXT Affinity.
| | 03:30 | With NUMERIC Affinity, any text that's
stored is converted to INTEGER or REAL,
| | 03:36 | if the conversion may be done
in a lossless and reversible way.
| | 03:39 | In other words, the text would have to
have digits and nothing but digits in
| | 03:44 | it in order for it to be converted to INTEGER or
REAL, and a decimal point for the case of REAL.
| | 03:50 | In order for this conversion to
happen, 15 significant digits must be
| | 03:53 | preserved and reversible.
| | 03:56 | If lossless conversion is not possible,
then the value is stored as text, even
| | 04:00 | though the column is defined as NUMERIC.
| | 04:02 | It's a NUMERIC Affinity;
| | 04:03 | It is not a NUMERIC type, and so it
will prefer NUMERIC, but remember, you can
| | 04:07 | store any type in any column.
| | 04:10 | If no other Affinity rules are satisfied,
then this is the default Affinity: NUMERIC.
| | 04:17 | The INTEGER Affinity works just like
the NUMERIC Affinity, but it prefers
| | 04:21 | INTEGERS over REALS.
| | 04:24 | Any column type that contains the string
INT will be given the INTEGER Affinity.
| | 04:30 | The REAL Affinity works like the
NUMERIC Affinity, but it prefers REAL storage.
| | 04:36 | Any column type that contains R-E-A-L,
F-L-O-A, as in Float, or D-O-U-B, as in
| | 04:42 | Double, will have a REAL Affinity.
| | 04:46 | And finally, the NONE Affinity, if the
column type contains the letters B-L-O-B
| | 04:51 | or no type is specified at all, then it will
have NONE Affinity, and there is no preference.
| | 04:57 | It will just do its best to
guess at how to store the data.
| | 05:01 | So let's look at some examples, so we
can get an idea of how this actually works.
| | 05:05 | And we are using the in-memory database
here, so we'll go ahead and create a table.
| | 05:12 | CREATE TABLE t, and we'll give it some columns;
| | 05:16 | we'll say a is INT, and
b is REAL, and c is TEXT.
| | 05:26 | And now we'll insert some values.
| | 05:31 | We'll try some numbers, and we'll try
some letters, and we'll try some words,
| | 05:51 | and finally, we'll take some numbers,
and we'll just put them in quotes.
| | 06:00 | Now let's see what we get out of this table.
| | 06:01 | We'll start with SELECT * FROM t,
and then we're going to use the TYPEOF
| | 06:06 | function to actually get a
representation of what type these results are.
| | 06:11 | So we're going to select TYPEOF (a),
and I'll just copy and paste to make this
| | 06:19 | easier to type, and that's a c, and
that's a b, FROM t. Now, this is an
| | 06:27 | in-memory database, so it will
just run all of this. There we are.
| | 06:32 | So we have created these three columns
in a table, and the first one has a Type
| | 06:36 | Affinity of INTEGER, and the second
one has a Type Affinity of REAL, and the
| | 06:41 | third one has a Type Affinity of TEXT.
| | 06:43 | But of course you can store anything
you want in any column, because this is
| | 06:46 | SQLite, and it's got manifest typing.
| | 06:49 | So first we store these three numbers:
| | 06:51 | 1, 2 and 3, and so this first row
has the 1 as an INTEGER, and 2 is REAL;
| | 06:59 | you see that its got the 2.0, and 3;
| | 07:02 | well, it looks like a 3, but if we
look down here, we have the TYPEOF.
| | 07:05 | The first one is INTEGER, the second
one is REAL, and the third one is TEXT.
| | 07:09 | It has actually stored that 3 as text,
and when we display it on the screen, of
| | 07:13 | course, it looks exactly the
same as the integer representation.
| | 07:17 | So the TYPEOF function is actually
pretty useful for finding out what you are
| | 07:20 | getting out of a table.
| | 07:22 | The next one, we stored three characters:
| | 07:25 | a, b, and c, and we see that they got
stored and retrieved from the table just
| | 07:29 | fine, and that they all came out text.
| | 07:32 | Even the column with the INTEGER Affinity
and the REAL Affinity, they came back as text.
| | 07:37 | And that's because there was no way to
convert these and to be able to convert then back.
| | 07:42 | In other words, there's no way to convert an
a into an integer and still convert it back.
| | 07:48 | So it stored it as text, and it
stored it as text, and stored it as text.
| | 07:53 | This next one, we have three words:
| | 07:54 | 1, 2, and 3, and those
work exactly the same way:
| | 07:57 | text, text, and text, and
there they are, the results.
| | 08:00 | And so you can see that we're
successfully storing text in columns that are
| | 08:05 | defined with an INTEGER and REAL Affinity.
| | 08:07 | This is an important point.
| | 08:09 | This is manifest typing.
| | 08:11 | And finally, we have numbers, and we
passed them as strings in quotes, and
| | 08:18 | watch what happens here.
| | 08:19 | We get back the number 1 as an INTEGER, the
number 2.0 as REAL, and the number 3 as TEXT.
| | 08:26 | So that worked exactly the same as this way.
| | 08:30 | So SQLite 3's manifest typing is very different.
| | 08:34 | If you are familiar with other
databases, it's very different than how other
| | 08:37 | databases work, and yet,
it is syntax-compatible.
| | 08:42 | You can define these columns with INTEGER,
REAL, TEXT, and other kinds of affinity.
| | 08:47 | You can store the same kinds of values
that you would expect to, using other
| | 08:51 | database engines, and it will
work in exactly the same way.
| | 08:56 | The advantage here is flexibility.
| | 08:58 | For a lot of small to medium-size
business applications, it's very easy to just
| | 09:04 | define a table and say, a, b, and c,
like that, and it will still run, and it
| | 09:11 | will still work exactly like you expect it.
| | 09:14 | If you really want those numbers to be
stored as Floats, then you can come in
| | 09:17 | here, and you can say REAL or Float,
or whatever, and you are getting the
| | 09:22 | results that you expect.
| | 09:23 | So you have this added flexibility,
and that flexibility gained is well worth
| | 09:29 | the effort in learning
how this new paradigm works.
| | Collapse this transcript |
| Storing numbers with INTEGER| 00:00 | Columns defined with INTEGER
affinity tend to store data using integers.
| | 00:04 | Like every column in an SQLite database,
they can store any data, but they will
| | 00:08 | prefer to store integers.
| | 00:10 | Let's create a table with a couple of INTEGERs.
| | 00:20 | Now we will go ahead and
insert some data into it.
| | 00:23 | We will make that a few rows, and now
we will take a look at what we have got.
| | 00:43 | So we are using the in-memory
database here, so all of this will happen
| | 00:49 | on the fly and each time we run it, it
will create an entirely fresh database.
| | 00:55 | So there we have all these numbers.
| | 00:57 | Now, we'd like to see their types, so
instead of selecting star, we can select
| | 01:02 | individually - we can say a and
TYPE OF (a) and b and TYPE OF (b).
| | 01:11 | It looks like I have a
syntax error here some place.
| | 01:14 | I am missing a comma right there,
and we will go ahead and run that.
| | 01:20 | So we can see here that all
of these values are integers,
| | 01:24 | so that's pretty useful.
| | 01:25 | Now we know, for example, if we were
to put quotes around this 4 that in that
| | 01:31 | third row the 4 will still be an
integer, because that can be converted to an
| | 01:36 | integer and converted back without any loss.
| | 01:39 | We also know that if we were to spell out
the word four, that because this is an
| | 01:44 | affinity, and it's not
actually a hard fast type,
| | 01:48 | that it will store that four as text.
| | 01:52 | But let's presume for a moment that our
intention here is just to put integers
| | 01:57 | in this table. Now, there are a lot of
good uses for integers in a database.
| | 02:02 | It's a small, efficient, and fast way to
store data and so there are many, many
| | 02:06 | things you are going to store this way.
| | 02:08 | One of the things you will notice,
however, is that there is no Numeric types
| | 02:13 | specifically designed for storing monetary data.
| | 02:16 | A lot of databases have that.
| | 02:18 | you don't want to store monetary data as
a REAL number, but if you use a decimal
| | 02:25 | point in any of these values, like for
instance if I say here 4.25, that will be
| | 02:31 | stored as a REAL number, and
a REAL number has loss to it.
| | 02:36 | It's a IEEE floating-point number, and
if you add them together and subtract
| | 02:41 | them back apart you don't always get exactly
the same value, so you cannot use that for money.
| | 02:48 | What you can do, however, is
you can do something like this.
| | 02:52 | I can say, I am going to call this
representation of, say this second column.
| | 02:57 | I am going to call this dollars and
cents, and I am going to say that this is
| | 03:00 | the number of cents.
| | 03:01 | So if I wanted o store $4 and
25 cents I would put in 425.
| | 03:06 | Then when I retrieve the data from the
database, I am going to want to divide
| | 03:11 | that by a hundred which is the number
of cents in a dollar, and I am going to
| | 03:15 | want to display it with a decimal point.
| | 03:17 | In other words, I am going
to display it as a Float,
| | 03:20 | but I am not going to store it as
a Float, and I am not going to do
| | 03:22 | arithmetic as a float;
| | 03:23 | I am going to do all of that as an
INTEGER, and just whenever I look at it, I am
| | 03:27 | going to look at it as if this is the
number of cents, and this works fine.
| | 03:30 | This is an excellent way to do this.
| | 03:33 | So the way that I would do that, and
I am just going to add a column to my
| | 03:35 | results here is I would say CAST (b AS
REAL) / 100, and I will call it Dollars.
| | 03:47 | So I will go ahead, and I will run that,
and you will see that here we have two
| | 03:51 | cents, three cents, four dollars and
25 cents, five cents and six cents, and
| | 03:56 | that works just fine.
| | 03:58 | As long as all of the arithmetic that
we do on this column is done with the
| | 04:02 | integers, that will work fine.
| | 04:04 | Now you will notice that I had to
cast it as a REAL before I could do the
| | 04:10 | division, and the reason for
that is, if I just did it like this,
| | 04:14 | b / 100, then what I get is an integer
result, and I get integer division, and I
| | 04:20 | don't get those cents.
| | 04:21 | This is sometimes called floor division.
| | 04:23 | It actually just ignores the remainder entirely.
| | 04:26 | So you need to cast it first before
you do the division, and that's okay.
| | 04:31 | You are just dividing it by a hundred
once, and you are just doing it for the
| | 04:34 | purpose of display, and so it's
always going to come out right.
| | 04:39 | So the syntax is CAST and then your
column name, AS, and then the type, you can
| | 04:46 | FLOAT, or you can say REAL, and then you
do the division, and then you give it a
| | 04:50 | name, and it will give it
that name and that column.
| | 04:53 | This is the kind of a thing that you
would put in a view, and we will talk
| | 04:56 | about views in another movie in this
course, and it's an excellent way to
| | 05:00 | handle money in a database.
| | 05:02 | So SQLite uses affinity for its
manifest typing scheme and INTEGER columns
| | 05:08 | prefer integers but may store any type
of data, and they are an excellent way to
| | 05:13 | handle money in a SQLite database.
| | Collapse this transcript |
| Storing numbers with REAL| 00:00 | Columns defined with the REAL
affinity tend to store numbers using
| | 00:03 | floating point representation.
| | 00:05 | If a number is inserted in a
REAL column, it will store it using
| | 00:09 | floating point representation.
| | 00:10 | If something other than a number is
inserted, it will store it as TEXT or BLOB.
| | 00:15 | So let's go ahead and define a table
and take a look how this works. Call it
| | 00:18 | t and put it a couple of columns and
define it with REAL affinity.
| | 00:25 | And now, we will insert some data.
| | 00:34 | If we insert numbers, they will be stored
as REAL numbers, even though the numbers
| | 00:39 | being inserted are integers.
Of course, if we insert floating point numbers,
| | 00:46 | those will also be and if we insert some
text, those will get stored as text.
| | 00:54 | So we will go ahead and take a look at
what we have here, and these first two rows
| | 01:02 | have these integers, and those got
stored as REAL, and then we have some REAL
| | 01:07 | numbers that stored as REAL, and then we
have some text that got stored as TEXT.
| | 01:11 | Let's look at the types, using TYPEOF.
| | 01:23 | And we will go ahead and run
that, and we can see these types.
| | 01:27 | Everything here up, until this
last row, got stored as REAL.
| | 01:30 | We're not able to put an
integer into one of these columns.
| | 01:35 | In fact, even if we cast this number
here as an integer, we'll use the cast operator,
| | 01:40 | it will still store it as a REAL,
and that's because this cast to is INT.
| | 01:49 | That whole expression is really, exactly
the same is just the two like that by itself.
| | 01:55 | So, because SQLite uses type affinity,
a column defined as REAL has a type
| | 02:01 | affinity of REAL. So it will tend to
store things as REAL if it can make numbers
| | 02:06 | out of them, and if those things are not
numbers, if they're text as in the case of
| | 02:10 | this fourth row, then it will
go ahead and store them as TEXT.
| | Collapse this transcript |
| Storing text| 00:00 | Columns defined with a TEXT affinity
will store all data as either TEXT, BLOB, or
| | 00:05 | NULL, but mostly TEXT.
| | 00:08 | In fact, it's actually a little bit
difficult to get something in there as a
| | 00:11 | BLOB, and a NULL, of course you can store it
all directly, and it'll be stored as a NULL.
| | 00:16 | So most of the data that goes into a
column with a TEXT affinity will be stored
| | 00:20 | as TEXT, even numbers will be
stored as TEXT. Let's take a look.
| | 00:24 | We'll create a table and give it a
couple of columns and give them text
| | 00:30 | affinity, and we'll insert some data.
| | 00:46 | We'll try inserting numbers, with and
without quotes, and maybe we'll just
| | 00:52 | spell the numbers out.
| | 00:57 | We'll store some NULLs, and
I'll like to write in caps(NULL).
| | 01:00 | You don't have to; they work just as
well either way. And maybe we'll cast some
| | 01:05 | BLOBs and maybe try casting an
INTEGER, see what that does.
| | 01:20 | And we'll look at the column and the
TYPEOF, and the other column and the TYPEOF
| | 01:25 | that, and we'll run that.
| | 01:35 | So, we see that the first two rows came
out as text, so the numbers were stored
| | 01:40 | as text, the quoted numbers were stored
as text, and we have the text here, and
| | 01:45 | the text there, and the spelled
numbers, of course, got stored as TEXT.
| | 01:49 | When we store NULL directly, either a
capital or a not capital, it comes out as
| | 01:54 | NULL, and SID here displays NULL
values in red so that we that they're NULL
| | 01:59 | values are not the letter are not letters N-U-
L-L, and we can see the TYPEOF is NULL there.
| | 02:05 | We did get a BLOB by casting a BLOB
directly so it is possible to store a BLOB,
| | 02:10 | but it's not possible to store an Int,
even if we cast that number as an Int,
| | 02:14 | because we know that 3 cast as Int is no
different than a literal 3, or a literal
| | 02:21 | number, so that got stored as TEXT as well.
| | 02:24 | You'll notice that I tend to type my SQL
in capital letters for the keywords and
| | 02:30 | use lowercase and mixed case for the
identifiers, and this is tradition.
| | 02:35 | It's often done that way.
| | 02:36 | It doesn't have to be.
| | 02:38 | SQL is case insensitive.
| | 02:40 | If I were to type this select here in
lowercase, it'll still work just fine.
| | 02:45 | But I tend to do that because it's traditional.
| | 02:48 | It tends to set off the keywords and
make them very obvious, especially in an
| | 02:53 | environment here without syntax
highlighting, as is this text input box and
| | 02:57 | HTML in this browser.
| | 02:59 | So Text columns may hold TEXT, BLOB or
a NULL data, and that's a type affinity,
| | 03:04 | not a type, so they will tend to store
data as text, even if the data is numeric
| | 03:10 | and numeric data, of course, will
always be stored as TEXT in a column with
| | 03:13 | Text affinity.
| | Collapse this transcript |
| Storing large data with BLOB| 00:00 | BLOB columns in SQLite do not have an affinity.
| | 00:03 | They are treated as if they
had no type definition at all.
| | 00:06 | BLOB stands for Binary Large Object.
| | 00:08 | In most databases, a BLOB column will
always store data precisely as it's given,
| | 00:13 | without any interpretation,
or otherwise manipulating it.
| | 00:16 | SQLite does that for everything,
and so the BLOB definition is
| | 00:21 | almost superfluous.
| | 00:23 | For most purposes, a BLOB column will
store data as TEXT unless otherwise told
| | 00:28 | to, and for most purposes that's okay.
| | 00:30 | We are going to demonstrate this in a
script, because binary data is a little
| | 00:35 | bit difficult to represent in a text box.
| | 00:38 | So we are going to start with this
start.php in the Chapter 04 folder, inside
| | 00:42 | your exercise files, and I'll make a
working copy of start.php, and I will name
| | 00:48 | it blob.php, and we'll
open it in the text editor.
| | 00:53 | And for this script, we are going to
use the bwSQLite 3 library, so we'll
| | 00:57 | import that using require_once, and
we'll come down here in main(), and we
| | 01:12 | don't need that, actually.
| | 01:13 | We'll go ahead and define some
constants, our database, and the path here is my
| | 01:23 | user folder, sqlite3_data, and we
are going to use the test.db database.
| | 01:31 | What we are going to do is we are
going to read in an image file. And you'll
| | 01:35 | notice in this folder here, there is
an image file called olives.jpg, and it
| | 01:40 | looks like that. And we are going to
read that in, we are going to store in the
| | 01:45 | database, and then we are going to
read it from the database, and display it.
| | 01:48 | And that's the kind of an application
that you'll normally have for BLOB data.
| | 01:52 | So we'll define another file name, and
that will be the olives.jpg. Go ahead and
| | 02:03 | open that file, and we'll
use 'rb' for the open in PHP.
| | 02:15 | That will make sure that we read in binary mode.
| | 02:17 | If you're using a computer that
distinguishes between text and binary files, like
| | 02:24 | a Windows-based computer, then you
require that b. It won't work without it.
| | 02:28 | Now we'll go ahead and read that file
into a variable called BLOB, usually fread.
| | 02:40 | And fread takes this as a second
argument, the number of bytes to read, and so we
| | 02:44 | just get the file size of the file.
| | 02:46 | Now we'll put it in our try block for
the database operations, and here goes
| | 03:09 | our database stuff.
| | 03:11 | So we'll initialize the
database, and we'll create a table.
| | 03:20 | Let me go ahead and drop the table if
it exist, so that we can create a fresh
| | 03:31 | table each time, just for testing
purposes; you are not going to want to do
| | 03:35 | something like this probably in production.
| | 03:42 | And we are going to use a couple more
of these sql_do, so I will just copy and
| | 03:46 | paste, couple of stray key-presses
there, so go ahead and create the table.
| | 04:01 | I am going to have an ID field in here
anyway, even though I probably won't be using it.
| | 04:06 | It doesn't cost me anything, and
it's just a good habit to get into.
| | 04:10 | So that it is the create table.
| | 04:13 | Now we'll insert the data into the table.
| | 04:25 | So we've created our table with one BLOB column.
| | 04:29 | A BLOB column in SQLite has a BLOB
affinity, which is really no affinity at all.
| | 04:34 | And so we're going to insert into the
table, into that one column, this BLOB
| | 04:40 | value, which was read up here on line 26
from the file, and so it has that image,
| | 04:46 | that picture of the olives in it.
| | 04:48 | Now we'll go ahead and read
that right back out of the table.
| | 04:52 | Now we are just reading one
value so we can use sql_query_value.
| | 05:02 | Now even though I am doing an
unqualified select here, I know that there is only
| | 05:11 | one row in the table, because I dropped
any old table if it existed, created a
| | 05:17 | new table, and inserted one row right
above this. And so I know that I will be
| | 05:21 | getting just one value, and that
allows me to use sql_query_value, like that.
| | 05:27 | Now that we've read the image back out of
the database, let's go ahead and display it.
| | 05:30 | Now PHP, by default, displays HTML, so
if we want it to display something other
| | 05:35 | than HTML, we need to tell it that we
are doing that. And we do that with the
| | 05:38 | header function, and we give it a MIME
header for the type that we are going to
| | 05:44 | be displaying, which in this case is
an image JPEG. So we say Content-type:
| | 05:47 | image/jpeg, and we go
ahead and display it with echo.
| | 05:57 | And finally, because we are going to
be displaying an image here, we are not
| | 06:01 | going to be using our framework.
| | 06:03 | We want to get rid of this page line up here.
| | 06:05 | All right, now we'll save the
file and go over here to the browser.
| | 06:12 | And the first thing I want to do is I
want to bring up SID, and come over to the
| | 06:15 | test database and take a look and make
sure that we don't have that BLOB test
| | 06:20 | table already in here.
| | 06:22 | So we'll SELECT * FROM SQLITE_MASTER and
press Go, and we see we just have these
| | 06:29 | three tables: customer, item and sale.
| | 06:31 | We do not have the BLOB test table in
here, so I am going to press on this
| | 06:36 | other tab and go to ExerciseFiles in
Chapter 04, and click on blob.php, and
| | 06:42 | there we have our image.
| | 06:44 | So we've read the image from the file,
| | 06:45 | we have stored it in the database,
| | 06:47 | we've retrieved it from the
database, and displayed it on the screen.
| | 06:50 | So this is the image that was
stored in the database and retrieved.
| | 06:54 | Now if we go over here to SID and run
SELECT * FROM SQLITE_MASTER again, we'll
| | 06:58 | see we now have this blobtest table.
| | 07:00 | The table got created.
| | 07:02 | So let's take a look at the type
that got stored. Remember, it had a BLOB
| | 07:06 | affinity, and we'll remember that a BLOB
affinity is just like no affinity at all.
| | 07:16 | So we would expect that data to
be stored as TEXT, not as a BLOB.
| | 07:21 | So if I say Go here, we'll see the
type of b is TEXT; it is not BLOB, and
| | 07:28 | that works just fine.
| | 07:29 | It displays the image just fine.
| | 07:32 | The reason for that is that a BLOB
type means Binary Large Object, and the
| | 07:38 | meaning of that is that it's an object
that will be stored and retrieved exactly
| | 07:43 | byte for byte, as it was stored.
| | 07:46 | Well, SQLite does its best to do that
anyway, and even in a TEXT column, it's
| | 07:51 | going to do its best to do that anyway.
| | 07:53 | Under some very rare circumstances, it
may do some interpolation of Unicode text.
| | 07:59 | It is not guaranteed that it
will never do that as a TEXT type.
| | 08:03 | So in a case like this, with actual
binary data, we want to ensure that it's
| | 08:07 | actually getting stored as a BLOB,
and the way we do that is with a CAST.
| | 08:12 | So here we have this question mark.
| | 08:14 | This is the placeholder for that
BLOB data in our INSERT statement.
| | 08:19 | We are going to use a CAST.
| | 08:21 | We are going to say CAST that
value AS BLOB, just like that.
| | 08:28 | Now when we save this and go over here
and run it, I am going to reload, and
| | 08:34 | that drops the table, creates a new
table and inserts that value into the table,
| | 08:40 | this time cast as a BLOB, and then
retrieves it from the table and displays it,
| | 08:45 | and so all of that happened there.
| | 08:47 | Now when I come over here to SID and I
run this query again, selecting TYPEOF
| | 08:52 | FROM blobtest, you will
see that it's now a BLOB.
| | 08:56 | So it worked fine as TEXT, and it would
probably always work fine as TEXT, but
| | 09:01 | if you want to actually ensure that
that data is stored byte-for-byte and
| | 09:05 | retrieved byte-for-byte exactly as you put it
in there, then you want to cast it to a BLOB.
| | 09:11 | So BLOB columns do not have an affinity.
| | 09:13 | Using a BLOB type affinity is just
like using no type affinity at all.
| | 09:18 | They tend to prefer TEXT storage, and
this actually works fine most of the
| | 09:23 | time, but if you really need BLOB
storage, use the CAST operator to ensure that
| | 09:27 | you get the storage type that you actually need.
| | Collapse this transcript |
| Storing booleans| 00:00 | SQLite has no Boolean type. Booleans in
SQLite are simply integers with the value 1 or 0.
| | 00:07 | So let's create a table and
take a look at how this works.
| | 00:17 | So we'll create a table with two integer
columns, a and b, and we'll insert into them 1
| | 00:29 | and 0, and so that is
true and false, respectively.
| | 00:34 | So we select from booltest.
| | 00:38 | We will see these values, 1 and 0.
| | 00:42 | So how do we use them as Booleans?
| | 00:45 | Well, in SQL, there is no If-Then, but
there is CASE, WHEN, which is used like
| | 00:50 | If-Then, so we can say select, and I'll
just put this on the next line, since it's
| | 00:56 | going to get a little bit long, CASE WHEN a THEN.
| | 01:01 | This is a logical Boolean expression.
| | 01:04 | It's either going to be true or
false, and so WHEN a, THEN we'll return a
| | 01:09 | true, else false END as boolA, and
we'll do the same thing for b: WHEN b
| | 01:24 | true else false END and call it b.
And we don't need a comma here, and we
| | 01:30 | just say from booltest.
| | 01:34 | So what this will do is it will say true
if there is a 1 and false if there is a 0.
| | 01:41 | So here for a, we have
True, and for b we have False.
| | 01:44 | So if we were to put a 0 in a, then we
would have False for both of them, and if
| | 01:52 | we put a 1 in b, then we have True over there.
| | 01:54 | You can also experiment, and you can
say well, does it think a 3 is True?
| | 01:59 | And Go and yes it does.
| | 02:01 | It thinks a 3 is True, but
does it think a string is True?
| | 02:08 | And no it doesn't. It really only thinks
that numbers that are not 0 are True and
| | 02:13 | numbers that are 0 are False.
| | 02:15 | SQLite uses integers to represent
Boolean values: 1 represents True and
| | 02:21 | 0 represents False.
| | Collapse this transcript |
| Storing dates and times| 00:00 | SQLite does not have a type for storing
dates and times; instead, it stores dates
| | 00:04 | and times usually as TEXT or
sometimes as REAL, or INTEGER, depending on the
| | 00:08 | representation of the time.
| | 00:11 | Let's take a look at how this happens.
| | 00:12 | We'll create a table here, and we'll
start with the TEXT representation.
| | 00:18 | So we'll say d1 TEXT and d2 TEXT.
| | 00:22 | Then we'll insert some values in there,
and we will use the DATETIME functions.
| | 00:31 | We'll get into more detail about the
Date and Time functions in the chapter on
| | 00:36 | Date and Time functions.
| | 00:44 | So now it defaults to UTC, or Greenwich
Mean Time, but if we say localtime, then it
| | 00:50 | tries to find our local time.
| | 00:51 | Where I am right now is on the west
coast of the United States. Then we'll select
| | 00:56 | d1 and the TYPEOF d1 and d2
and the TYPEOF d2 FROM t.
| | 01:08 | We'll go ahead and look at that.
And so what we get here is we've got the
| | 01:12 | Greenwich Mean Time, which is actually
tomorrow the 14th and the local time here
| | 01:18 | and these are both TEXT strings.
| | 01:20 | So that's the most common way for
dates and times to be represented is using
| | 01:25 | this format: year-month-day hour-minute-
second, and so this DATETIME function,
| | 01:30 | it's a little shorthand that's built into
SQLite that represents the time, like that.
| | 01:36 | It's also capable of storing times as a
REAL, using a special Julian date format,
| | 01:45 | or as an INTEGER, using a Unix Epoch.
And Unix Epoch, for those of you that aren't
| | 01:51 | familiar with it, is the number of
seconds since the beginning of the year 1970.
| | 01:56 | So if we insert these values, we
would say JULIANDAY, and that's a special
| | 02:03 | function that gives us that.
And there isn't a special function for the Unix
| | 02:07 | Epoch, so it's done with STRFTIME and
all of these other special functions for
| | 02:13 | representations are actually aliases of
STRFTIME with specific formats. And the
| | 02:18 | format for the Unix Epoch
is '%s', and let's say 'now'.
| | 02:24 | So if I run this, we'll see that we
have a REAL, and we have an INTEGER. And the
| | 02:29 | REAL is this really long number which
represents the Julian day, which is the
| | 02:33 | number of days since noon in Greenwich
on November 24 4714 B.C., according to the
| | 02:41 | proleptic Gregorian calendar, and
that's the definition of Julian day.
| | 02:46 | The other one, the Unix Epoch,
is a lot easier to remember.
| | 02:49 | Now these actually do represent dates
and times, and they can be converted very
| | 02:54 | easily into a readable format.
And so we can say SELECT DATETIME(d1) and
| | 03:03 | DATETIME(d2) FROM t. The DATETIME(d2)
actually has to be formatted with the
| | 03:13 | special modifier unixepoch,
because that one's not otherwise handled
| | 03:18 | automatically. So if you are storing
your dates as an INTEGER using the Unix
| | 03:22 | Epoch, which is a convenient number
that's easy to get from a lot of programming
| | 03:26 | languages - including PHP and Perl,
then you want to remember to use this Unix
| | 03:31 | Epoch. So we'll go ahead and look at
that, and we actually get that nicely
| | 03:35 | formatted date and time using the Date
and Time function from these values in
| | 03:40 | REAL and INTEGER format.
| | 03:41 | Now most of the time you're going
to just use the TEXT format because
| | 03:45 | that's very convenient.
| | 03:46 | It's very common in SQL
database engines. But for those cases where
| | 03:50 | you want a higher resolution, and you
want to use the Julian day, or those cases
| | 03:54 | where it's convenient to be using
the Unix Epoch, these other formats are
| | 03:58 | available, and they can be stored as REAL,
or INT, and SQLite just uses them like
| | 04:03 | it does any other date and time.
| | 04:05 | SQLite stores dates and times as TEXT,
REAL, or INTEGER. For most purposes, you'll
| | 04:10 | use the TEXT format, and for more detail
on the Date and Time functions in SQLite
| | 04:16 | see the chapter on Date and
Time functions in this course.
| | Collapse this transcript |
|
|
5. Storing and Reading DataStoring data with INSERT| 00:00 | The insert statement is how you add
rows to a table. So, we have SID open
| | 00:05 | here and using the in-memory database,
I'll create a couple of tables. We can
| | 00:10 | see how insert works.
| | 00:15 | So I'll create a table called a, and it
has three columns: a, b, and c, and I'll
| | 00:20 | create a table called b, and give it
three columns, and call them d, e, and f.
| | 00:25 | Now, I'm going to INSERT INTO a, and
this is how the insert statement works.
| | 00:33 | You say insert into and the table name.
| | 00:36 | You can give it the names of the
columns: a, b, and c, then the values with the
| | 00:43 | keyword VALUES and then give it data, and
I'll just say 'a', 'b', 'c' like that.
| | 00:51 | And then we can SELECT FROM the table,
and we should get one row with three
| | 00:58 | columns in it: a, b, and c, and the
values are a, b, and c because we inserted
| | 01:02 | those strings a, b, and c.
| | 01:03 | Now, because we're putting values in
all the columns, and they are in the
| | 01:07 | same order that they were defined, we
can omit this column definition here,
| | 01:13 | and just say INSERT INTO a, VALUES a,
b, and c, and it will put them in the
| | 01:18 | columns as they were defined in the table
definition. And so we'll get exactly the same result.
| | 01:24 | On the other hand, if I wanted to
just insert data into a couple of the
| | 01:28 | columns, I could say b and c here and
just give it two values over here and
| | 01:36 | then a will be NULL.
| | 01:37 | But for our purposes right now, we're
just going to go ahead and put some values
| | 01:43 | in here, and we'll insert a few rows.
Maybe we'll give it 1, 2, and 3
| | 01:53 | and 'this', 'that' and
other' and maybe 'x', 'y', 'z'.
| | 02:06 | Now, we select, and we have these four rows.
| | 02:11 | And we did that because with table b,
we're going to do something different.
| | 02:14 | I'm going to show you another
way that you can insert data.
| | 02:17 | You can insert data into a table
and get that data from another table.
| | 02:22 | So I can say INSERT INTO b SELECT *
FROM a and then if we SELECT FROM a, and
| | 02:34 | we'll also SELECT FROM b, you'll see
that the two tables are exactly the same.
| | 02:41 | This is a, and this is b. If I'd like, I
can specify that the data is going to go
| | 02:48 | in a particular order into table b. I
can say, insert into b and then give a
| | 02:55 | column names, and I can say f, e, d
reversing the order. And so column a from a will
| | 03:03 | go into column f from b, column b
from a will go into column b from b, and
| | 03:09 | column c from a will go into column d
from b and so these will be reversed in
| | 03:15 | the second select: c, b, a, 3, 2, 1,
other, that, this, z, y, x. Or of course I
| | 03:23 | could do this the other way and
instead of specifying them that way, I can say
| | 03:28 | SELECT c, b, a FROM a, and they will go
in the defined order in b, and we'll get
| | 03:34 | exactly the same result.
| | 03:36 | So this is how insert works.
| | 03:38 | You insert into a table.
| | 03:40 | You can either use the values keyword,
or you can use a select from another
| | 03:43 | table, or you can really use any
expression for where the data comes from.
| | 03:48 | So that's how insert works.
| | 03:50 | The insert statement is
simple to use; it's flexible.
| | 03:53 | You may insert values directly or
from another table using select.
| | 03:56 | The syntax is simple, and
it can be extremely powerful.
| | Collapse this transcript |
| Updating data with UPDATE| 00:00 | When you want to change the values in a table,
| | 00:01 | you use the update statement.
| | 00:04 | To demonstrate this, we're going to use
the test database. And I'm just going to
| | 00:09 | copy and paste some data in here, and
we will create a table, and insert some
| | 00:16 | data into it, and then select from it.
| | 00:19 | And now this data is in the test
database, and so we don't need this part
| | 00:25 | anymore, and you can see
that the data is still there.
| | 00:29 | So let's say that I wanted
to change one of these quotes.
| | 00:32 | Let's say number 4 here, The
Terminator, and he says, "I'll be back" and maybe
| | 00:38 | instead, we want him to say, "Hasta la vista, baby!"
| | 00:41 | So I say UPDATE t, which is the name of
the table, so I'm updating the table SET
| | 00:48 | quote =, and I'll put my new data
in there and I say WHERE id = 4.
| | 00:55 | So here is the id column, and you see its
number 4 is the one with The Terminator.
| | 00:59 | Now, if you omit this WHERE it will
update all of them, and that's probably
| | 01:04 | not what you want.
| | 01:05 | So we're going to go ahead and leave
the WHERE in there, and I'll put something
| | 01:09 | else in the quote here. I'll say
"Hasta la vista, baby," and I'll say Go.
| | 01:17 | And 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:28 | And it only updated the row that
we selected in the where clause.
| | 01:33 | And of course we can update
more than one column at a time.
| | 01:35 | And I want to leave this in here
this time. We'll say UPDATE t SET quote = ,
| | 01:46 | byline = and WHERE id = 4.
| | 01:52 | So this time we're going to give it a
difficult quote from a different movie.
| | 01:55 | We're going to say Rosebud, one of the
great lines from any movie ever, and that
| | 02:00 | was of course spoken by Charles Foster Kane.
| | 02:05 | And then we'll also SELECT FROM the
table at the same time, so we're updating
| | 02:09 | the row where the id = 4, which is
this one here that currently has the
| | 02:12 | Terminator's quote in it, and we're
setting both the quote and the byline, and
| | 02:17 | these don't have to be in the order they
were defined; they can be in either
| | 02:19 | order, and they're separated by commas.
| | 02:22 | So we'll say Go, and there we have it:
the row with the id number 4 says Rosebud,
| | 02:29 | and it says Charles Foster Kane, and
we select it again so we have that.
| | 02:34 | So really that's all there
is to the update statement.
| | 02:38 | The update statement is a simple and
powerful tool for updating data in a table.
| | 02:43 | Columns are specified with the SET
clause, and rows are selected using where.
| | 02:47 | Take care that you don't inadvertently
omit the where clause, and I am going to
| | 02:52 | just show you what happens if we do that.
| | 02:53 | If I take this WHERE clause out and
run this with the UPDATE SET like that
| | 02:59 | without a where clause, you'll see
every single row in the table gets
| | 03:03 | updated with that data.
| | 03:04 | So you want to take care that you don't
inadvertently omit the where clause, or
| | 03:09 | you may overwrite your entire table.
| | Collapse this transcript |
| Reading data with SELECT| 00:00 | The select statement is easily
the most common statement in SQL.
| | 00:04 | It is how you read data.
| | 00:06 | So we are going to look at the
select statement a little bit here, and
| | 00:10 | it's really a huge topic and in some ways the
entire course is about the select statement.
| | 00:15 | So you will see a lot of examples of a
lot of different ways that you can use
| | 00:19 | select throughout this course. And in
this movie we are just going to hit the
| | 00:22 | high points a little bit.
| | 00:24 | The most common thing that you'll see
done with select is SELECT * FROM, and we
| | 00:29 | will give it a table name. We'll say
album here, which is in the album database,
| | 00:33 | so I will just select that
from my Database dropdown and Go.
| | 00:37 | There we have all of the
albums in the album database.
| | 00:41 | Now you'll often see it done this way,
| | 00:44 | where instead of saying SELECT * with
the asterisk which selects all of the
| | 00:49 | columns, I will list some.
| | 00:51 | I will say title, artist, and label, and
if I say Go, then I just get the title,
| | 00:58 | artist, and label columns.
| | 01:00 | So, the first clause in select is what
I'm selecting, and the second is the FROM.
| | 01:06 | In fact, you can omit the FROM, and
you can just put literals over here. I can
| | 01:10 | say SELECT 'Hello, World' like that I
will get 'Hello, World', and it actually
| | 01:16 | titles the column 'Hello, World'
with quotes, because that's the literal
| | 01:20 | expression that it was given.
| | 01:21 | So it gives the expression as the name
of the column, and that actually is what
| | 01:26 | gets passed back if you're doing this
in PHP or in another language. What gets
| | 01:31 | passed back in the results there as
the name of the column is that expression
| | 01:36 | and so in this case we have the
quotes, and then there is the value.
| | 01:39 | So I could also say SELECT 'Hello,
World' and use the AS key word and then give
| | 01:44 | it a name. And if I say AS 'Bob,' then
this title up at the top there will be Bob.
| | 01:51 | So whatever goes after the AS is what
the title of the column is going to be,
| | 01:56 | and that's also the key that will
be given to the associative array.
| | 02:00 | If you're using associative arrays in
your PHP or hashes in another language,
| | 02:06 | that will be the key that string there.
| | 02:08 | So you can name it like that.
| | 02:09 | Now this comes in handy, and you will
see it is used a lot in joins and views
| | 02:14 | and subselects and things
like that later on in the course.
| | 02:18 | Another way that select can be used is
select can actually be used as a source
| | 02:23 | for data. This is called a subselect.
| | 02:25 | So if I say SELECT say id FROM album
WHERE artist is IN, and I will give it a
| | 02:34 | list: 'Jimi Hendrix', or 'Johnny Winter'.
| | 02:45 | If I give this SELECT statement, you
will see we will get 2 ids back: 11 and 16.
| | 02:49 | Now I can use that as a source, and I am
going to put all of this in parentheses
| | 02:54 | here, and I am going to say SELECT *
FROM track WHERE album_id is IN, and then I
| | 03:05 | give it this expression. And I'll often
indent this a little bit; sometimes I will
| | 03:09 | even do it like this here, so that it's
clear that this is a subselect, and that
| | 03:16 | this is related to that, and
it's not another statement.
| | 03:19 | So I will put a semicolon there, and now
we have all of the tracks from the track
| | 03:25 | table related to albums from these two
artists. And so this first one here, that's
| | 03:33 | the Jimi Hendrix in that West album,
and the second one here is the Johnny
| | 03:36 | Winter And Live album,
and I happen to know that.
| | 03:39 | Now if I really want to know how
they're related, then I can do what I call a
| | 03:44 | pseudo-join. Some people consider
this a join, and we will cover joins in
| | 03:48 | another movie in this chapter, but I
can say something like this. I can say
| | 03:52 | SELECT a.title AS album and t.title AS
track. And so 'a' represents the album
| | 04:04 | table and 't' represents the track table,
and we will see how we get that later
| | 04:07 | in the statement here, t.track_number.
And I'm going to indent this under this, so we
| | 04:14 | can see that its related, and I can say
FROM album AS a, and so I'm giving the album
| | 04:21 | an alias for use here, so when we say
a.title, we know that that's title column
| | 04:25 | from the album table and track AS t, and
then we have a WHERE, and this is where
| | 04:34 | it gets interesting.
| | 04:35 | a.id, so that's the id column from
the album table, is equal to t.album id.
| | 04:42 | And what this does is it gives
these results for conditions where those
| | 04:49 | two things are equal.
| | 04:50 | In actuality, and the engine does it a
little bit more complicated than this,
| | 04:54 | but you can think of it as if the
database engine is going row-by-row through
| | 04:59 | both tables in all possible
combinations and only giving you the results where
| | 05:04 | the condition is met, where one table has
a column that equals this other column
| | 05:09 | and this other table.
| | 05:10 | Then we will say ORDER BY because
we want them in a certain order.
| | 05:14 | We don't want them all mixed
up, a.title, t.track_number.
| | 05:18 | So it will be alphabetical, by album title,
and then track number within that sorting.
| | 05:25 | So let's go ahead and run this, and so
this is what I call a pseudo join, and
| | 05:28 | you see how joins are done in another movie.
| | 05:31 | But here we have album, track, and
track number, and they are all correlated.
| | 05:36 | And we actually have the name of the
album, even though that's not in the track
| | 05:40 | table; it's in the album table. And they
are joined together by this expression here.
| | 05:46 | It's like a poor man's join, and we
will see how to do REAL joins in another
| | 05:50 | movie, but that's another way that
you will commonly see this when you read
| | 05:53 | SQL written by other folks.
| | 05:55 | So that's a quick tour of select; of
course select is far more powerful and
| | 06:00 | flexible than this, but
these are the highlights.
| | 06:03 | It is easily the most common
statement in SQL, it's extremely flexible and
| | 06:07 | powerful, and while this movie does not
explore its full potential, most of it's
| | 06:10 | common uses are explored throughout this course.
| | Collapse this transcript |
| Reading data from multiple tables with JOIN| 00:00 | The power of a relational database is
its ability to store data in multiple
| | 00:03 | tables, and access those relationships.
| | 00:06 | How you access those
relationships is with the join clause.
| | 00:10 | As an example, we will
use the world database here.
| | 00:13 | Let's just take a look at those tables,
| | 00:14 | the SQLITE_MASTER table, and here is a
list of the tables in the world database.
| | 00:21 | You see there is City,
Country, and CountryLanguage.
| | 00:24 | So this CountryLanguage table, you
will notice it has a CountryCode, and you
| | 00:28 | will notice in the Country table,
there is a Code field, and that's the index
| | 00:34 | into the Country table. So that's the
three-letter international country code.
| | 00:38 | So the CountryLanguage is a list of all
the languages in the database, so if I
| | 00:44 | say CountryLanguage, you'll see we get
984 rows, so that's a lot of rows.
| | 00:52 | And this has all of these languages, so
there are several perk country, and there is
| | 00:56 | a percentage of how many people in
that country speak the language, and then
| | 00:59 | there is this CountryCode.
| | 01:00 | So if I want to know where this
language that I can't pronounce is spoken, it's
| | 01:05 | in this country that I don't know that code.
| | 01:07 | So if I want to know what that country
is, I would have to look that up in the
| | 01:10 | country table by code. Sounds confusing?
| | 01:13 | There is an easy way to do it,
and that's what join us for.
| | 01:17 | So let's do a join.
| | 01:18 | We will select c.Name, so that's the
name column from the country table, and
| | 01:25 | l.language, so that's language column
from the CountryLanguage table. And so we
| | 01:33 | will say, FROM CountryLanguage AS l,
and we will join Country AS c. So how this
| | 01:47 | join works is you have to have
something to join on, and so we will join ON
| | 01:51 | l.CountryCode = c.Code.
| | 01:58 | So we are selecting from the
CountryLanguage table, and we are calling it l for
| | 02:03 | reference purposes, and we are joining
on country as c where - and this on clause
| | 02:11 | is like where in the pseudo joins, and
so when l - that's the CountryLanguage
| | 02:17 | table.CountryCode, so that's the
CountryCode column in the CountryLanguage table -
| | 02:22 | is equal to the Code column in the
Country table, then these two tables will be
| | 02:27 | joined, and so that's how they are joined.
| | 02:29 | They are joined on that intersection.
| | 02:31 | So if I say Go, I now get a list of all
of the countries, in the order that they
| | 02:37 | are in the country table, and the
languages that are spoken there.
| | 02:41 | So if I like, I can say ORDER BY
c.Name, and we will get it ordered by the
| | 02:48 | country name in alphabetical order.
| | 02:50 | Now we still have a lot of data here.
| | 02:52 | Let's just say that I want to know what
languages are spoken in the United States.
| | 02:58 | So then I can just say WHERE c.Name =
'United States', and I can see just what
| | 03:08 | languages are spoken in the United States.
| | 03:10 | So this is how we access that
relationship between the two tables.
| | 03:14 | The relationship is joined on the
CountryCode, and so that's called CountryCode in
| | 03:19 | the language table, and it's called
code in the country table. And so we just
| | 03:23 | use join and on, and that allows us to
access both of those tables in a coherent
| | 03:29 | manner, and to be able to see the name
of the country from the country table and
| | 03:34 | the name of the language
from the language table.
| | 03:37 | Just as one more example, let's
switch to the album database, so you get an
| | 03:41 | idea of how this works.
| | 03:43 | We will look at the
relationship between tracks and albums.
| | 03:47 | So if I want to get a list of all of
the tracks and on all of the albums with
| | 03:52 | the names of the artists and the
names of the titles and the names of the
| | 03:54 | tracks, I can do that like this.
| | 03:58 | Select a.artist, so that's from the
album table, a.title, AS album, so that's
| | 04:07 | the title of the album.
| | 04:08 | I am just going to call it
album in the column headings.
| | 04:10 | t.title as track, so that's the title
of the track, and I am going to call it
| | 04:16 | track in the title headings, and t.track
_number, FROM track as t and JOIN
| | 04:26 | album AS a ON t.album_id = a.id.
| | 04:37 | The join intersection is where the
album_id is equal to the album_id column in
| | 04:45 | the track table, and we will ORDER BY a.artist,
album and t.track_number. So here we have it.
| | 04:58 | It's ordered by the artist and so f for Frank
Zappa comes first before j for Jimi Hendrix.
| | 05:05 | We have 63 rows, and there they are,
and that's all of the tracks in the
| | 05:09 | database and their associated
album and artist, from the album table.
| | 05:16 | So we will see a lot more
examples of joins throughout the course.
| | 05:19 | Use join to combine data from
two or more tables into one query.
| | 05:23 | Join is how you access the
relationships between tables in a
| | 05:27 | relational database.
| | Collapse this transcript |
| Deleting data with DELETE| 00:00 | The DELETE statement is used for
deleting rows from a table. As an example let's
| | 00:04 | take a look at the album database and
we'll SELECT * FROM track, and you'll notice
| | 00:12 | there are 63 rows in the table right
now, and we'll need to know that for later.
| | 00:17 | If you notice down here at the end,
there is an extra track. It's called Fake
| | 00:20 | Track, and its actually
part of the Jimi Hendrix album.
| | 00:23 | If you look up here, this is Jimi
Hendrix live album, called Hendrix in the West,
| | 00:28 | which is one of my favorite Jimi Hendrix albums.
| | 00:31 | So 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:44 | And if you want to delete that row, you
use exactly the same syntax with the FROM and
| | 00:48 | the WHERE, just instead of the SELECT
star, you just say DELETE, like that.
| | 00:54 | And I'll press GO, and then we will try
and look for it again with SELECT, and we
| | 00:58 | will see that the row is not there.
| | 01:01 | Now it's really important, and I can't
emphasize this enough, that when you use
| | 01:06 | the delete statement, you want
to always have a WHERE clause.
| | 01:10 | If you omit the WHERE clause, then
you can delete every row in the table.
| | 01:16 | So before we do this, and I want to
demonstrate this for you, but I want to make
| | 01:20 | sure that you're operating
on a copy of the database.
| | 01:24 | In your exercise files, in the SQL
folder, you will see these DB files,
| | 01:30 | and these are the originals of the database.
| | 01:35 | Now in my case I have got my sqlite3_
data folder over here, and I've actually
| | 01:41 | got copies of these databases
in there that I am operating on.
| | 01:45 | So if I go and delete every
row in this table, that's okay.
| | 01:50 | I can take this file here, this
album.db file, and I can copy it into my
| | 01:57 | sqlite3_data folder and replace it,
and I've got the entire original database
| | 02:03 | again. And in fact, if I do the SELECT
again, you will see that fake track is
| | 02:07 | back, because I have just copied the
original database back over into the working place.
| | 02:15 | So I am going to demonstrate deleting
the entire table for you, so that you can
| | 02:20 | see what happens, rather deleting all the
rows in the table, the table will still
| | 02:24 | be there and just be empty.
| | 02:25 | But I just want to make sure, if you
follow along, that you're working on a copy
| | 02:28 | of the database, so that you can get it back.
| | 02:31 | Of course, if you aren't working on a
copy of the database, you can probably
| | 02:35 | still download it from the lynda.com web site.
| | 02:37 | So if we say DELETE FROM track and GO,
this one query affected 63 rows because
| | 02:48 | I had put the original database back, and
so I have the 63 rows again instead of 62.
| | 02:53 | If I say now SELECT * FROM track, you'll
see that it's completely empty, elapsed
| | 03:00 | time 0.32 milliseconds, and
there are no rows in the table.
| | 03:05 | If I SELECT COUNT from track, this is
what often happens if I accidentally
| | 03:10 | delete all rows in a table, I go "What?
there can be nothing in it," and also I
| | 03:14 | COUNT and COUNT will come up with zero.
| | 03:17 | So that's a really easy mistake to make.
| | 03:20 | If you use the DELETE statement and
you forget to include the WHERE, you will
| | 03:24 | delete all of the rows from the table.
| | 03:27 | Now I am going to just go back over here,
and I'm going to restore my database
| | 03:34 | and do my SELECT again,
and now I have 63 rows again.
| | 03:38 | So the DELETE statement is used for
deleting rows from a table. Rows are
| | 03:42 | selected using the WHERE clause.
Take care that you don't inadvertently omit the
| | 03:47 | WHERE clause, or you may
delete all the rows in your table.
| | Collapse this transcript |
|
|
6. SQLite ExpressionsUnderstanding expressions in SQLite| 00:00 | An expression is anything
that returns a valid value.
| | 00:04 | A literal value is an expression, a
function that returns a value is an
| | 00:07 | expression, and expressions can be
used anywhere that a value can be used.
| | 00:12 | For example, in this SELECT statement,
five times five is an expression. Each of
| | 00:18 | the fives is a literal value, and in
either of those places, you could have
| | 00:22 | another expression. Or in this INSERT
statement, the value is two times two,
| | 00:28 | LAST_INSERT_ROWID with the parenthesis,
the function call for last insert ROWID.
| | 00:32 | The literal value 'your name' in
quotes, which is the parameter to the HEX
| | 00:38 | function call, these are all
expressions, and they all return values.
| | 00:43 | Expressions use values and operators,
| | 00:46 | so the values can be literal integers,
floating point, strings, BLOBs, NULLs or
| | 00:52 | other expressions, and the operators
include string concatenation operator, and
| | 00:58 | this might look odd if you are familiar
with other programming languages. The two
| | 01:01 | vertical bars are often used to
represent 'or,' but in the case of SQL, that's used
| | 01:05 | for string concatenation.
| | 01:07 | There is the arithmetic operators, the
comparison operators, and Boolean operators.
| | 01:15 | SQL also has some special
operators, including IS and IS NOT for
| | 01:20 | comparisons, LIKE and GLOB for pattern
matching, and GLOB is specific to SQLITE -
| | 01:25 | that's not a standard SQL
operator, the BETWEEN operator for pairing
| | 01:30 |
comparisons, as in x BETWEEN Y AND Z,
| | 01:35 | the IN and NOT IN operators for
testing inclusion in a list or query result.
| | 01:40 | Then there's the CASE expression, and
the CASE expression is, again, a little bit
| | 01:45 | different. SQL was designed by
engineers at IBM that were familiar with COBOL
| | 01:50 | and IBM's languages, which tended to
be very verbose and tended to be very
| | 01:54 | different than the languages that were
being used by microcomputer operators.
| | 01:59 | So most of us today are used to CASE
being used in SWITCH expressions, and in the
| | 02:05 | case of SQL, it's really much
more just like IF/THEN/ELSE.
| | 02:10 | So there's two forms of case: there's the
case X when THIS/THEN/THAT, and there's
| | 02:16 | the case when expression, then result,
and we'll take a look at both of these
| | 02:21 | in our examples later in this chapter.
| | 02:23 | So expressions are used for many
purposes in SQL. Anything that has a value or
| | 02:28 | returns a value is an expression.
| | Collapse this transcript |
| Comparing values with comparison operators| 00:00 | Comparison operators are used for
comparing the values of expressions.
| | 00:04 | For example, in the world database, if
you want to see all the countries with a
| | 00:09 | country code of GBR, which is Great
Britain, you could do something like this.
| | 00:14 |
| | 00:24 | Here I've used this comparison operator,
the Equals operator, which is the most
| | 00:29 | common comparison operator.
| | 00:31 | I've used this to select just the rows where
country code is equal to GBR. So I'll say, Go.
| | 00:37 | You get 81 rows, and these are the
cities in the city table where the country
| | 00:44 | code is GBR, and so these
are cities in Great Britain.
| | 00:48 | Another simple example would be if
I wanted to see countries where the
| | 00:52 | population is greater than 100
million. I could do something like this.
| | 00:57 |
| | 01:14 | So this will select those countries
where the population is greater than 100
| | 01:17 | million, and it'll put them in
order by population, with the larger
| | 01:22 | populations at the top.
| | 01:24 | And there we have the results, and
there are just 10 countries in the database
| | 01:29 | that have populations greater than 100 million.
| | 01:31 | What if we wanted to see those
where the population is in a range.
| | 01:36 | Here, instead of the greater than
operator we could use the between operator,
| | 01:40 | and we could say between 5 million
and 10 million, and we get 30 rows.
| | 01:53 | Now the interesting thing about the
Between operator is that it's just like
| | 01:58 | saying greater than or equal to 5
million and less than or equal to 10 million,
| | 02:04 | with the exception being there are the
population value is only evaluated once.
| | 02:09 | And there are some circumstances where
evaluating an expression would have a
| | 02:13 | side effect, and so you'd
only want to evaluate it once.
| | 02:16 | So where you mean to say between, I strongly
suggest that you use the between operator.
| | 02:22 | The between operator also
works with TEXT strings.
| | 02:27 | So, for example, if I wanted to see
all the countries where their name is
| | 02:32 | between G and R, I might do something like this.
| | 02:35 |
| | 02:49 | So this will select, from the country
table, those rows where name is between
| | 02:54 | capital G and capital R, and it
will just give us the name column.
| | 02:58 | So we'll go ahead and press Go.
| | 03:00 | And notice couple of things about this:
one, that none of the countries that
| | 03:06 | begin with R are actually included, and
that's because this is doing a value comparison.
| | 03:14 | So G by itself is before Gabon or Gambia.
| | 03:19 | R by itself is before anything that might
begin with R and so we're ending at R by itself.
| | 03:28 | If we're put in here a bunch of Zs
then we're getting up to and including
| | 03:34 | anything that might be spelled Rzzzzz. And so
if I select that, then we now include the Rs.
| | 03:43 | So the ways that strings sort is
important here, because that's really how
| | 03:47 | this is being evaluated.
| | 03:49 | It's also important to realize that a
lowercase letter is above all of the
| | 03:55 | uppercase letters, so if I put a
lowercase r here, I'm going to get everything
| | 04:00 | that start with a G all the way up to
everything that starts with a Z, because
| | 04:05 | all of these names start with a capital
letter, and lowercase letters sort after
| | 04:11 | all of the uppercase letters.
| | 04:13 | So this is not a case insensitive
sort, and at least for the purposes of
| | 04:17 | selecting the betweens and between operator.
| | 04:20 | The order by is case insensitive.
| | 04:23 | So that's an important distinction to understand.
| | 04:26 | SQL supports all of the
standard SQL comparison operators.
| | 04:30 | It's a simple set of operators, and you
will likely use them often in your SQL.
| | Collapse this transcript |
| Matching patterns with LIKE| 00:00 | The LIKE operator is used
for pattern matching in SQL.
| | 00:03 | For example, we will use the world
database, and it we're looking for all the cities
| | 00:10 | that begin with the letter Z,
we could do something like this.
| | 00:13 |
| | 00:26 | This statement will select all the
rows from the city table where the Name
| | 00:30 | column starts with the letter Z, and
we will notice the LIKE operator here.
| | 00:36 | So the LIKE operator will look for
cases where this column here, Name, will match
| | 00:43 | this pattern, which is a Z followed by
zero or more characters, so the percent
| | 00:49 | sign is used for zero or more characters,
| | 00:52 | so anything that starts with the
letter Z, including a field that just had a
| | 00:56 | letter Z in it alone.
| | 00:57 | So I am going to Go here, and we'll get
all of the rows with a city that starts
| | 01:02 | with the letter Z. Now you notice I
used a lowercase z there, and that's
| | 01:07 | because the LIKE operator, at least in
SQLite - and this is not necessarily true
| | 01:13 | in other databases,
| | 01:14 | but in this case, the LIKE
operator is case insensitive.
| | 01:17 | So at least for English, and it's not
necessarily guaranteed to work in non
| | 01:23 | western character sets, or any extended
Unicode character sets for that case.
| | 01:28 | It's really only designed to work with
the normal ASCII characters. But those
| | 01:33 | cases, it is case insensitive.
| | 01:36 | Now if we're looking to match the second letter,
| | 01:39 | for example, if I was looking for all
of the cases where the second letter is a
| | 01:43 | W, I could mark the first place with
an underscore, and the underscore will
| | 01:49 | match exactly one of any character.
| | 01:53 | So following exactly one of any
character, and where the next character is
| | 01:58 | letter W, upper or lowercase, followed by zero
or more anything, then it will match those rows.
| | 02:05 | So press Go, and now we have
everything with a W in the second position.
| | 02:11 | So that's pretty much how LIKE works,
and that's pretty much the extent of what
| | 02:15 | you can do with LIKE.
| | 02:17 | You can match zero or more characters
with a wildcard, or you can match one
| | 02:22 | character with a wildcard.
| | 02:24 | So most SQLs, they will extend this in
some way or another, and SQLite has its own
| | 02:30 | proprietary nonstandard operator
called GLOB, and it looks like this G-L-O-B,
| | 02:38 | and it works a little bit differently.
| | 02:40 | For example, if I wanted to do this
match of the second letter is a W, I could
| | 02:45 | do like this with a question mark in
place of that underscore, and it does
| | 02:49 | exactly the same thing, and an asterisk
in place of that percent sign, and that
| | 02:54 | does exactly the same thing.
| | 02:55 | But this case, be careful
because the letters are case sensitive.
| | 03:01 | So this will match any lowercase W,
which in all of these cases is the same.
| | 03:05 | So if press Go, we will
get exactly the same result.
| | 03:08 | Now if I were to put in a capital
W there, we would get zero results.
| | 03:14 | Likewise, if I wanted to match a first
letter Z, I would have to use a capital
| | 03:19 | Z, and there we get those 59 rows, but
a lowercase Z would not work at all in
| | 03:25 | that first position.
| | 03:27 | Now the GLOB operator is a
little bit more flexible.
| | 03:30 | The goal here is to work
exactly like UNIX command line GLOBs;
| | 03:34 | UNIX command line GLOBS
are not really standardized,
| | 03:36 | it depends on the shell.
| | 03:38 | But this does a lot of the same things.
| | 03:39 | For example, if I wanted find anything
that started with a Z or say a K, I could
| | 03:45 | do that, and put them in square brackets
like that. And now, I've got all the of
| | 03:50 | the Ks because they're alphabetical by name.
| | 03:54 | Then after the Ks, I've got all of Zs.
| | 03:58 | So that's the GLOB operator.
| | 04:00 | The GLOB operator is little bit more
flexible than LIKE operator, and SQLite has
| | 04:04 | the facility for, if you wanted to get
down inside of it and see or in some
| | 04:09 | other supported language to write your own
matching algorithms, it has some support for those.
| | 04:16 | So the LIKE operator and the GLOB
operator are used for simple pattern
| | 04:20 | matching in SQLite.
| | 04:22 | You'll use this operator when you
need to find text in a table that matches
| | 04:26 | a specific pattern.
| | Collapse this transcript |
| Building simple math with arithmetic operators| 00:00 | SQLite supports the
standard SQL arithmetic operators.
| | 00:04 | For example, I can say SELECT 5 * 30,
and I get a result that says 150, or I can
| | 00:12 | say SELECT 7 / 3, and I
get a result that says 2.
| | 00:19 | Now if I want a fractional result, I
can say 7.0 / 3, and I'll get it as a REAL
| | 00:25 | number, that'll say 2.3333 or, I can do
the integer division, but I can get the
| | 00:33 | remainder with the Modulus (%)
operator, say SELECT 7 % 3, and I get the
| | 00:39 | remainder of 1. So, some
applications for arithmetic operators in queries.
| | 00:45 | Let's say that we wanted to see the
population from our world database.
| | 00:50 | We want to see the populations of
countries expressed in millions.
| | 00:54 | We could do something like this.
| | 00:55 | I'll select the world.db, and
I'll type in this in this query.
| | 01:00 |
| | 01:20 | So in this query, I am selecting the
name and the population in millions, so
| | 01:25 | that's population divided by 1,000,000,
and I am naming it PopMM, for population in
| | 01:31 | millions, and then I am using the PopMM,
which is the already calculated result
| | 01:38 | in both the WHERE and the ORDER BY clauses.
| | 01:41 | So we'll say Go here, and we see our list
of descending order, population in millions.
| | 01:46 | So we have 1.2 billion from China and
a little over a billion from India,
| | 01:52 | and this is using the PopMM field, which is
calculated using the integer division operator.
| | 01:59 | As another example, in our test
database, we have a little sale table.
| | 02:05 | So if I select item ID and price from
the sale table, you see that the price is
| | 02:16 | expressed in integer cents. And so if I
want to see those expressed in dollars, I
| | 02:22 | can do something like this.
| | 02:24 | Now keep in mind that just dividing
price by 100 will not work. If I say price /
| | 02:29 | 100, then I'll get integer division, and
I don't get the cents; I just get this
| | 02:34 | for division result.
| | 02:36 | So first I need to cast price with
the CAST operator, and cast it as a REAL
| | 02:45 | number, and then when I do the
division, I will get the dollars and cents.
| | 02:50 | And I can just say AS Price here,
so I get a nice label on it.
| | 02:55 | Now I have dollars and cents.
| | 02:58 | So these are some examples of how you would
use these arithmetic operators in your queries.
| | 03:04 | SQLite supports all standard the SQL
arithmetic operators, and they can come in
| | 03:09 | very handy when you need
calculated results in your queries.
| | Collapse this transcript |
| Matching values in a list with IN| 00:00 | The IN operator is used to find
values that match a list of choices.
| | 00:04 | For example, if we wanted a list of all
the cities in both the United States and
| | 00:08 | England, we would select the
world.db here and do something like this.
| | 00:13 |
| | 00:28 | So this will give us all the rows from
the city table, where the country code is
| | 00:32 | in the list USA and Great Britain.
Of course, that list could be longer if we
| | 00:36 | wanted it to be, and we'll order them by name.
| | 00:39 | So we'll run that query, and there we have a
list of all the cities in both USA and GBR.
| | 00:47 | 355 rows.
| | 00:50 | Now, let's say that I didn't want to
type these codes out, but I wanted to find
| | 00:54 | the codes in the database. So IN can
take either a literal list like that, or it
| | 01:08 | can take the results of a select query.
| | 01:11 |
| | 01:29 | So this Select query will look for the
country code from the country table, where
| | 01:34 | the name is IN, and I gave it a list of
country names - United States and United
| | 01:39 | Kingdom, and so that will return a
list of the country codes, and then those
| | 01:44 | country codes are used for the IN
operator in the outer query, this IN operator
| | 01:49 | right here, that will look for
country code in the city table.
| | 01:53 | So this will give us exactly the same
results, but it's getting that list of
| | 01:58 | country codes from the country table,
rather than typing it in literally.
| | 02:03 | So there we have exactly the same,
355 rows, and it works exactly the
| | 02:08 | same, except that it's getting that list from
the table, rather than typing it in literally.
| | 02:13 | So the IN operator is flexible, in
that it can match results from a list
| | 02:19 | or from a subselect.
| | Collapse this transcript |
| Choosing from multiple conditions with the CASE expression| 00:00 | The case expression in SQL is
like if, then, else in other languages.
| | 00:04 | This is the only conditional structure in SQL.
| | 00:07 | For example, and I am using
the in-memory database here,
| | 00:10 | if I create a table, and this table
just has two columns: a and b, and then I
| | 00:20 | will insert into that table values of 1
and 0, and then we will select from that
| | 00:29 | table, and we get this here:
| | 00:35 | we get 1 and we get 0.
| | 00:36 | Now if we wanted to test those true and
false values, because that's what those are,
| | 00:42 | we would use the case expression.
| | 00:44 | So instead of just selecting star, we
could do something like this, CASE WHEN a
| | 00:54 | THEN 'TRUE' ELSE 'FALSE'.
| | 01:02 | So when I run this query, then I get
a true because there is a 1 in the a
| | 01:07 | position. And if I put a 0
there, then I get the word 'false.'
| | 01:12 | So the way this works is using this
case expression WHEN, and then this
| | 01:17 | expression is a Boolean Expression -
that would be true or false, and if it's
| | 01:22 | true, then the result from the THEN
clause is taken, and if it's false, then the
| | 01:27 | result from the ELSE clause is taken.
| | 01:30 | Now for a more practical example, we
will switch over to the album database, and
| | 01:35 | this is actually an example from the
application that uses the album database
| | 01:40 | that we will be looking at later in the course.
| | 01:42 | I am just going to cut and paste this in.
| | 01:45 | It's a bit long, and most of it here is
this subselect. So I am going to explain
| | 01:50 | it, and then we will see why and how we
are using this case expression up here.
| | 01:56 | So we are going to start at the inside
and go out, and that's the way that you
| | 01:59 | want to look at subselects.
| | 02:01 | There are actually two nested subselects here;
| | 02:04 | the first one is getting ID from the
album table where the artist is either Jimi
| | 02:09 | Hendrix of Johnny Winter -
| | 02:10 | so that's just selecting two albums
from the album table, and then inside that,
| | 02:14 | we are selecting a join select from the
album and track table to get a list of
| | 02:21 | artists and tracks and track numbers
and such. But also, more importantly, right
| | 02:26 | here, we are splitting up the duration field.
| | 02:29 | Now remember, the duration field is the
number of seconds. That's the duration of
| | 02:33 | the track. And we want to display that
as minutes and seconds in a nice way.
| | 02:38 | So we take the duration and divide it by
60 with integer division, and so we will
| | 02:44 | get the number of minutes out of that.
And we use as to name that m for minutes,
| | 02:49 | and then we take the same duration and
we use the module of 60 to get the number
| | 02:53 | of seconds out of it, and we
make that an s with the AS s.
| | 02:59 | So that subselect gives us an m and
an s that we can use here, so we have
| | 03:03 | artist, album, track, track
number, and then minutes and seconds.
| | 03:08 | So here we use the minutes, and we use
the concatenation operator, and we add a
| | 03:13 | colon to the end of that, and then we use a
concatenation operator, and we have a case.
| | 03:19 | If s is less than 10 - and remember WHEN
is like f, if the number of seconds is
| | 03:24 | less than 10, then we are going to
use a leading 0, concatenated with the
| | 03:28 | number of seconds; otherwise, if it's
10 or greater we are going to give just
| | 03:32 | the number of seconds.
| | 03:34 | And END ends the case
expression, and then we say AS duration.
| | 03:39 | So we have artist, album, track, track
number, and duration, and duration will be
| | 03:43 | nicely formatted in minutes and seconds.
| | 03:46 | So we'll say go here, and there we
have it. You would notice the duration is
| | 03:50 | minutes, colon, and seconds.
| | 03:53 | And that is accomplished with this
case expression. And the joining and the
| | 03:58 | selecting and all of that is done with the
subselects, and that's actually not very uncommon.
| | 04:02 | In other words, this is the kind of
a thing that you will see in production
| | 04:06 | databases quite often. But this little
trick here, this is a little bit specific
| | 04:12 | to SQLite because SQLite does not
have a lot of built-in functions.
| | 04:18 | It doesn't have something for
easily converting number of seconds into
| | 04:22 | minutes and seconds.
| | 04:23 | It doesn't have something for
formatting strings very easily.
| | 04:27 | So as a result, we end up using this
case expression to accomplish that.
| | 04:32 | The case expression can look a bit
awkward, but it's very useful, and it's a
| | 04:37 | good idea for you to become familiar
with it, so you can use case whenever you
| | 04:42 | need a conditional in SQL.
| | Collapse this transcript |
| Forcing a data type with CAST| 00:00 | There will be times when you'll require
a certain value to be a certain type.
| | 00:04 | The CAST operator is used to force a
value to a specific type. For example, if
| | 00:10 | we look at the TYPEOF, using the TYPEOF function,
| | 00:14 | and look at the TYPEOF the literal value 1,
| | 00:17 | we'll see that it is a TYPEOF integer.
| | 00:21 | Now, if we needed that to be some
other type, say TEXT, we could cast it
| | 00:27 | using the CAST operator.
| | 00:29 | Now, it looks like this.
| | 00:30 | The CAST and then in parentheses, we
have the literal value, and that's a number
| | 00:37 | one, not a letter L, As and then the type.
| | 00:41 | Any type that's valid in a type
definition in a create table statement,
| | 00:48 | any of those types are valid here.
| | 00:50 | So, we say TEXT, and according to the
same rules that will return the type in
| | 00:55 | a create statement,
| | 00:56 | we will get a type, so in
this case that type is TEXT.
| | 01:01 | Now, let's say we wanted it to be a
floating point number, we could say REAL, and
| | 01:06 | now we'll get a REAL.
| | 01:08 | So, in a case where we wanted to, say divide
7 / 3 with just integers, we get the number 2.
| | 01:16 | But if we take that 7 and we cast it and say
CAST (7 AS REAL) and then divide that by 3,
| | 01:27 | now we get the result that we are looking for.
| | 01:30 | It's no longer integer division;
now it's floating point division.
| | 01:33 | You know likewise if we have an
integer that represents a number of cents in
| | 01:38 | 1995, and we want that to
be in dollars and cents,
| | 01:43 | if we just divide that by 100 we are
going to get the dollar part, but we are
| | 01:47 | not going to get the cents part.
| | 01:49 | Instead, if we cast that first to a floating
point number and then divide it by number 100,
| | 01:59 | now we get the full dollars and cents.
| | 02:02 | One special case, if we cast a NULL
value as anything at all, we will get NULL,
| | 02:11 | and the type of the result will still be NULL.
| | 02:17 | So, this is actually a useful thing
if you're selecting values out of a
| | 02:22 | database and some of the values in a
column are NULL and some of them are not,
| | 02:27 | and you are casting that,
| | 02:29 | you can still test it for NULL
and know if you have NULL value.
| | 02:33 | The CAST operator is used to
force a value to a specific type.
| | 02:37 | SQL uses manifest typing so there are
times when the type of a value really matters,
| | 02:43 | for those times the CAST operator is
valuable, for making sure that the value is
| | 02:47 | the right type for its purpose.
| | Collapse this transcript |
|
|
7. SQLite Core FunctionsFinding the LENGTH of a string| 00:00 | When you need to find the length of a
string, for example as a parameter for
| | 00:03 | another function or as a key for
sorting, the SQL standard length function is
| | 00:08 | available in SQLite.
| | 00:09 | For example, if I wanted to find the
length of all of the names of countries in
| | 00:14 | the country table in the world database,
| | 00:17 | select the world database
here, and I can do that like this.
| | 00:21 |
| | 00:32 | So, this will give me all the records in
the table, and it will give me the name
| | 00:36 | and the length of the name and the
three-letter country code from each row.
| | 00:41 | So, we have 239 rows and for each row
we have the name, and we have the length
| | 00:47 | of the name and the Code.
| | 00:49 | Now maybe I don't want all of them.
| | 00:51 | I can select them by length as well.
I can save WHERE LENGTH (Name) >=10, and
| | 01:00 | this will just give me those
where the length is 10 or more.
| | 01:04 | So, now we have 86 rows.
| | 01:06 | I can also sort on it.
| | 01:07 | So let's go ahead and give this length
an alias and say AS Len, and that will
| | 01:13 | make this easier, and WHERE Len, and I can
use the BETWEEN operator, BETWEEN 10 and 12.
| | 01:24 | So, that will give me the lengths
between 10 and 12, and I can ORDER BY Len DESC.
| | 01:33 | That will give me the big ones at the top.
| | 01:34 | So, now I've just got 37 rows, and it's
got the longer ones at the top and the
| | 01:40 | shorter ones down further.
| | 01:43 | They are ordered by length.
| | 01:44 | So, the LENGTH function is useful for
finding the length of a string, and it may
| | 01:49 | be used anywhere an expression can be used.
| | Collapse this transcript |
| Changing case with UPPER and LOWER| 00:00 | There are times when you may need to
change the case of a string, or to treat a
| | 00:03 | string as all upper or lowercase.
| | 00:05 | For these situations, there are
the functions UPPER and LOWER.
| | 00:09 | For example, here I've created a table,
and the table has two columns: a and b,
| | 00:13 | and I have inserted values that are
mixtures of upper and lowercase As and
| | 00:17 | Bs. So when I select everything FROM an
ORDERED by a, you'll notice the problem.
| | 00:24 | A, B is sorted before Aa and aa.
| | 00:31 | The reason for that is that SQLite
defaults to a binary collation, and in a
| | 00:37 | binary collation all uppercase
letters appear before all lowercase letters.
| | 00:43 | So, even if I had a capital Z, it
would appear before a lowercase a.
| | 00:48 | One thing you can do about this of
course is to change the collation.
| | 00:55 | So here, I've changed the collation to
NOCASE collation, and that will sort it
| | 01:00 | the way that we expect.
| | 01:01 | Now, we have Aaa before
ABA, regardless of the case.
| | 01:07 | Another way you can do this though, if
you don't want to use the nonstandard
| | 01:10 | syntax of the collate directive,
| | 01:13 | You can simply Order it by UPPER case
(a), and that will get us exactly the same
| | 01:20 | result, even with the binary collation.
| | 01:23 | So, what the UPPER function does here
is it returns a copy of the string that
| | 01:27 | is in all uppercase.
| | 01:29 | So, for example, if I select
a, UPPER(a) then you can see the result here is
| | 01:38 | that UPPER(a) is all uppercase letters.
And the LOWER case function lower does
| | 01:44 | exactly the same thing, except with lowercase.
| | 01:49 | One thing to note here about ordering
by the UPPER case function is that in
| | 01:55 | my experiments, it does not seem to be any
slower than simply changing the collation.
| | 02:00 | In fact, changing the
collation probably does the same thing.
| | 02:04 | So, the UPPER and LOWER functions are
used for changing the case of a string.
| | 02:08 | You may also use these functions in an
index or in the ORDER by CLAUSE to force
| | 02:12 | case insensitive sorting.
| | Collapse this transcript |
| Reading parts of a string with SUBSTR| 00:00 | The substring function is for situations
where you need to operate on part of a string.
| | 00:04 | Like, for example, here I have a table
with two columns in it, and each of these
| | 00:10 | columns is encoded in a way where the
first couple of characters are a State
| | 00:14 | code on the a column and a Country
code on the b column, and then there is a
| | 00:19 | number after that, all in the same column.
| | 00:22 | This is very common, especially in
database systems where they have a fixed
| | 00:27 | amount of storage, so they're
packing information into columns, and the
| | 00:32 | substring function is how you deal with this.
| | 00:35 | For example, if I want to get the
State code out of the a column, I can say
| | 00:40 | SELECT SUBSTR(a, 1, 2) FROM t;
| | 00:48 | And the way this works is the first
parameter is the string coming in, and in
| | 00:53 | this case it's the column.
| | 00:55 | And the second parameter
is where you're star, and
| | 00:57 | this is 1-based, not 0-based, as you
would expect from a language like PHP or C,
| | 01:02 | but rather, this is 1-based
because SQL tends to be 1-based.
| | 01:07 | And the third parameter is the
length of the substring that you want.
| | 01:10 | And so I'll go ahead and run this
query, and you see we have these State
| | 01:14 | codes from that a column.
| | 01:17 | And so if we want to also get the number,
we can say SUBSTR(a, 3). And I don't
| | 01:25 | need to give it that third parameter,
because we're just going to take the
| | 01:27 | rest of the string.
| | 01:30 | So there we have the number as well.
| | 01:32 | And so I could name these. I could say
that this one is State, and this one is
| | 01:39 | StValue, and we can put
the AS keyword over here.
| | 01:44 | That's actually optional.
| | 01:45 | I tend to always include it,
because it's just a good idea.
| | 01:49 | And then we can we do the
same thing in the b column.
| | 01:52 | We can say SUBSTR(b, 1, 2) AS
Country, SUBSTR(b, 3) AS CoValue.
| | 02:08 | And there we have a nice little
result with all of those columns.
| | 02:12 | You could use this as a
subselect and pass it into a join.
| | 02:16 | You can do all kinds of great stuff with this.
| | 02:19 | And that substring
function is very, very useful.
| | 02:22 | So for circumstances where you need
part of a string, for example where the
| | 02:26 | string is encoded with positional data,
the substring function will allow you to
| | 02:30 | easily extract those parts of a string.
| | Collapse this transcript |
| Changing parts of a string with REPLACE| 00:00 | The REPLACE function provides
simple string matching replacements.
| | 00:03 | For example, I've got this table here with
two columns, and it has these strings in it.
| | 00:10 | So if I SELECT * FROM t, we see that
we get those strings in our result.
| | 00:16 | Now if I wanted to change the word 'Thing'
in column a, and make it say something else,
| | 00:24 | I can do this.
| | 00:25 | Instead of a SELECT, I can say SELECT
REPLACE, and the first parameter is the string.
| | 00:34 | In this case, it's the a column.
| | 00:37 | The second parameter is the string
to search for that will be replaced.
| | 00:42 | And the third parameter
is the replacement string.
| | 00:50 | So now column a here has Other
One, Other Three, and Other Five.
| | 00:54 | It doesn't have Two; all the even numbers are
in column b. So that's the REPLACE function.
| | 00:59 | The REPLACE function is very simple.
| | 01:01 | It only does simple
string matching replacements.
| | 01:03 | It doesn't do any pattern matching, or
regular expressions, or anything like that.
| | 01:07 | If you need something more complex, you
can create your own functions in SQLite
| | 01:12 | with PHP, and we'll cover that
in another movie in this chapter.
| | Collapse this transcript |
| Trimming blank spaces with TRIM| 00:00 | When you get input from users,
sometimes they may enter extra spaces at the
| | 00:03 | beginning or end of their input,
| | 00:06 | for handling this, there are the TRIM functions.
| | 00:09 | So for an example here, I've created a
table, and I've entered some strings with
| | 00:14 | some extra spaces at the
beginning and the ends of them.
| | 00:17 | In order to display these in SID,
I've put these extra brackets around.
| | 00:22 | You see, the problem is, I'll just show you here,
| | 00:27 | if you just display a string that has
spaces at the beginning or at the end in
| | 00:32 | HTML - and our output here in SID is HTML,
| | 00:36 | those spaces are typically folded out.
| | 00:39 | So in order to actually display the
spaces, I have to put some brackets around
| | 00:44 | it or something, and concatenate.
| | 00:46 | So this double vertical bar is
the concatenation operator in SQL.
| | 00:51 | I know that looks weird, but
that's the way that SQL is.
| | 00:54 | So I'm just concatenating a left square
bracket, and then the a, and the right
| | 00:59 | square bracket, and doing the same
with b, so that we get these spaces, and we
| | 01:04 | can actually see them.
| | 01:06 | So we can see the result
when I do something like this.
| | 01:08 | Now I'm going to trim the spaces
from a, using the TRIM function.
| | 01:12 | Now you can see that a here has no
spaces on either side of the string, and you
| | 01:17 | can see that because of the square brackets.
| | 01:19 | So there are three TRIM functions in SQLite.
| | 01:22 | These are mirrored in
most implementations of SQL.
| | 01:25 | There is TRIM, for trimming
both sides of the string;
| | 01:28 | there is LTRIM, which you can see just
trims the left side, and it leaves the
| | 01:32 | spaces on the right side; and there is
RTRIM, which just trims the right side
| | 01:39 | and leaves the left side alone.
| | 01:44 | So there is trimming both sides, with
just the TRIM function, and that's the way
| | 01:47 | that you'll most likely use it most of the time.
| | 01:50 | So when you need to make sure that
your data has no extra spaces at the
| | 01:53 | beginning or the end, the TRIM
functions can be very useful.
| | Collapse this transcript |
| Reading absolute values with ABS| 00:00 | The ABS function returns the
absolute value of a number.
| | 00:04 | For example, I created a table here that
has two columns in it, and in each of
| | 00:08 | these columns, I've inserted
some positive and negative numbers.
| | 00:12 | So when I SELECT FROM it,
you see the result there;
| | 00:15 | we have those positive and negative numbers.
| | 00:17 | Now if I want just the absolute value
of the numbers in the a column, I can say
| | 00:22 | ABS, and put the a in the parentheses.
| | 00:25 | That will give me the absolute value.
| | 00:28 | It'll take you to these negative
numbers, and simply change its sign, and
| | 00:32 | make it positive. And so the result then looks
like that. Or if you like, you can even ORDER BY.
| | 00:39 | I'll just put this one back here.
| | 00:41 | You can use it in ORDER BY or in
your indexes, or wherever you can use an
| | 00:45 | expression, you can say ORDER BY ABS (a).
| | 00:52 | Now it'll put the positive and
negative numbers together, so the ones are
| | 00:56 | ahead of the threes.
| | 00:57 | So when you need the absolute value of
a number, you can use the ABS function,
| | 01:01 | the ABS absolute value
function, to get that result.
| | Collapse this transcript |
| Rounding values with ROUND| 00:00 | When you need the rounded value of a number,
the ROUND function will give you that result.
| | 00:04 | For example, we have this table
here with some REAL numbers in it.
| | 00:08 | If I select them, you see those
are our results, and those are REAL
| | 00:11 | floating point numbers.
| | 00:13 | Now if I want the rounded value of a,
for example, I can say ROUND(a), like that,
| | 00:20 | and that will give me the number
rounded to zero decimal places. And it will
| | 00:25 | still give me a REAL number,
a floating point number.
| | 00:29 | If I want to get an INTEGER,
then I need to cast it.
| | 00:32 | So I can use CAST(Round(a) AS INT), like that.
| | 00:40 | Now, I get an INTEGER, instead
of the floating-point REAL number.
| | 00:43 | This is a different result than I would
get if I cast just the un-rounded number.
| | 00:49 | For example, if I ROUND(b), and CAST,
you could see I get 457 instead of 456.
| | 00:57 | But if I just CAST b, if I take this
ROUND function out, as an INTEGER, then I
| | 01:02 | just get the integer part of it.
| | 01:04 | It throws away the decimal point, but
it doesn't do any rounding, so I get 456.
| | 01:08 | Now if I want to round it to two
decimal places instead of zero decimal places,
| | 01:14 | I can use this second positional
argument for the ROUND function.
| | 01:17 | So I can say ROUND(b, 2), and that
will round it two decimal places.
| | 01:23 | Of course, I'll get a REAL number, because
I have a decimal in it. So I get 456.79.
| | 01:28 | So you see that it rounded that 8 up
to 9, because there is a 9 after it.
| | 01:34 | So when you need the rounded value of
a number, the ROUND function is used to
| | 01:39 | give you the results that you're looking for.
| | Collapse this transcript |
| Finding the data type of an expression with TYPEOF| 00:00 | Because SQLite uses manifest typing,
sometimes it's useful to know the type of a value.
| | 00:05 | The TYPEOF function returns a string
indicating the type of the value that was passed to it.
| | 00:11 | For example, I have this table here, and
it has these type affinities in it: INT,
| | 00:15 | REAL, and TEXT for the three columns a,
b, and c. So when we press Go here, we
| | 00:21 | see that we get these values,
and they are all different types.
| | 00:24 | We don't really know which types they are.
| | 00:27 | So I can use the TYPEOF function to find out.
| | 00:29 | I can say, a, TYPEOF(a), and do the same
for b and c. And then when I press Go,
| | 00:46 | we now have the value,
and the type of the value.
| | 00:50 | So in our INTEGER column, we
have integers, and we have text.
| | 00:55 | In our REAL column, we
have real, and we have text.
| | 00:58 | In our TEXT column, we have all text.
| | 01:01 | That's how the TYPEOF function works.
| | 01:03 | So the TYPEOF function is used for
finding the type of a value. Because SQLite
| | 01:08 | uses manifest typing, and type affinity,
| | 01:11 | this function can come in very handy.
| | Collapse this transcript |
| Finding the last inserted record with LAST_INSERT_ROWID| 00:00 | There are times when you'll want to
know what the last inserted row ID is.
| | 00:04 | SQLite provides the LAST_INSERT_
ROWID function for this purpose.
| | 00:07 | For example, we have a table here, and
it's been created with an INTEGER PRIMARY
| | 00:11 | KEY and three columns, and
we're inserting values into this.
| | 00:16 | If I say SELECT LAST_INSERT_ROWID,
like that, I'll get the number 5, because
| | 00:24 | one, two, three, four, five -
| | 00:26 | there are five rows in this table.
| | 00:29 | In fact, if I SELECT * FROM t, you'll
see that we have five rows, and the last
| | 00:37 | id there is number 5.
| | 00:40 | In fact, even if I don't have an
INTEGER PRIMARY KEY column, I will still get
| | 00:47 | that same result, because
it's using the built-in ROWID.
| | 00:51 | It's not depending upon your
INTEGER PRIMARY KEY column for that value.
| | 00:57 | So when you need to know the last
inserted row ID, even if you haven't declared
| | 01:00 | an INTEGER PRIMARY KEY column, the LAST_
INSERT_ROWID function will still give
| | 01:05 | you this value.
| | Collapse this transcript |
| Getting the version of your SQLite library| 00:00 | Sometimes you may need to know the version
of the SQLite library that you are using;
| | 00:04 | perhaps you want to use a particular
feature that's only available in later versions.
| | 00:08 | For this, there is the SQLITE_VERSION function.
| | 00:11 | It just looks like this.
| | 00:12 | You can say SELECT, or use it in any
way that you can use any expression, and
| | 00:18 | say SQLITE_VERSION, with
parentheses, because it is a function.
| | 00:24 | There you get the version. This that
I'm using here is 3.6.12, because that's
| | 00:29 | what's compiled into the
version of PHP that I'm using.
| | 00:33 | You don't always get to choose the
version of SQLite, unless you are compiling
| | 00:37 | everything yourself, and you know that
you are compiling in a certain version.
| | 00:40 | If you get something like I did here,
with XAMPP that came with the version that
| | 00:45 | it came with, or if your code is
running on a server at some place, or you are
| | 00:49 | distributing some code that's run by
different people in different environments,
| | 00:52 | you don't always know what version
of SQLite is compiled into that package.
| | 00:58 | So the SQLITE_VERSION function is
available to give you that value.
| | 01:02 | It returns a string with the value,
| | 01:04 | so if you need to find the version of
the SQLite library that you're using, the
| | 01:08 | SQLITE_VERSION function will give you a
string with the value of the version of
| | 01:13 | SQLite that's compiled into that package.
| | Collapse this transcript |
| Creating user-defined functions| 00:00 | When you need to perform a calculation
or a transformation on your data, you can
| | 00:03 | do so in a user-defined
function, sometimes called a UDF.
| | 00:07 | For SQLite, UDFs are written in the C or
in the host language, in this case in PHP.
| | 00:14 | Here we have an example of a
rather complex transformation.
| | 00:18 | The duration of tracks in the track
table, in the album database, are stored as
| | 00:24 | numbers of seconds, and yet when we want to
display them - go ahead and run this query,
| | 00:29 | we want to display them in minutes
and seconds like this, under TrackLen.
| | 00:34 | So in order to do that, I used the
subselect, which also does a join and does some
| | 00:39 | other things to make the data
convenient for the outer query. But you will
| | 00:44 | notice that in here I have separated
out the duration into minutes and seconds.
| | 00:49 | I have calculated the minutes by
dividing the duration by 60, and that's an
| | 00:54 | INTEGER division, and I have
got the seconds by using a modulo -
| | 00:58 | the remainder of that
division - to get the seconds.
| | 01:01 | So I have m for minutes and s for
seconds, and then in the outer query there
| | 01:06 | is still more transformation to be done.
| | 01:09 | I take the minutes, and I concatenate
on a colon. And then for the seconds, I
| | 01:15 | need to see if the seconds are less than
10, in which case I need to have a zero
| | 01:19 | concatenated with the seconds;
otherwise, use the seconds. And that allows us to
| | 01:24 | do something like this, which is two
minutes a colon a zero and seven seconds.
| | 01:30 | So this is a rather complicated transformation.
| | 01:32 | It's a bit obtuse, it's hard to read,
and it works well, and it performs well.
| | 01:37 | So I decided this is a great
case for a user-defined function.
| | 01:41 | So over here in the source code for SID,
we'll notice in the _init function where I
| | 01:47 | set up the database connection and
here's the case SQLite 3 - because sid
| | 01:51 | supports both MySQL and SQLite 3, and
here's the connection to the database, and
| | 01:58 | here I have this PDO function called
sqliteCreateFunction. And what this does is
| | 02:05 | it registers a function in SQLite,
so that you can use it from the SQL.
| | 02:12 | So the SQL name will be SEC_TO_TIME like
that, in all uppercase. And I used the
| | 02:16 | same name in lowercase for the PHP function.
| | 02:19 | So this PHP function will be called every
time I include SEC_TO_TIME in an SQL query.
| | 02:27 | So let's take a look at the SEC_TO_TIME function
here in PHP, and there it is. It is very simple.
| | 02:33 | It is five lines of code.
| | 02:35 | First, I check to see if the parameter
is NULL, and if so I don't bother to do
| | 02:39 | anything else - just return a NULL.
Then I make sure that it's INTEGER using
| | 02:43 | PHP's intval function, and then I split
out the minutes and seconds, just like I
| | 02:48 | did in SQL. And then I return, using
sprintf, the formatted string, minutes and
| | 02:55 | seconds very, very simple.
| | 02:57 | So now over, here in SID, because that
function is registered with SQLite, I can
| | 03:04 | replace this query with this query,
and I will just paste this in here.
| | 03:09 | Very simple. I am selecting all of
these columns, and I am renaming some of
| | 03:13 | them: album, track, trackno, and I
use the SEC_TO_TIME function with the
| | 03:19 | duration and call a TrackLen and
then the rest of the query, it's just a
| | 03:23 | normal, simple query.
| | 03:25 | You read this. It's obvious what it does.
And I say Go, and there we have pretty
| | 03:30 | much exactly the same results, and you
will notice that all of these times are
| | 03:34 | formatted very nicely.
| | 03:36 | So if I am going to have a function
that converts seconds to time, I might also
| | 03:40 | want to have a function that converts
times to seconds, so that when I update
| | 03:44 | the database, I can do so easily.
| | 03:47 | So here is a corresponding query that
updates the track with time from a time
| | 03:53 | string - and I will just change this
here, so I did changes in the database -
| | 03:56 | where id is 70, and I know which one that is.
| | 03:59 | That's this one that's called Fake Track
here, and you see it was 5:19, and I am
| | 04:02 | going to make it 9:17.
| | 04:04 | So I will go ahead and go, and now I
will query that track, and you'll see it
| | 04:13 | now says 9:17. And the
duration in the table is in seconds.
| | 04:17 | So there is a case where some data
transformation needed to be done.
| | 04:21 | The PHP code for it is very simple.
Here's the seconds to time, and here's
| | 04:26 | the time to seconds.
| | 04:27 | We simply register these functions,
when we initialize the database.
| | 04:34 | Here's both of them. Very simple to do.
| | 04:37 | User-defined functions are a
powerful way to perform data transformation.
| | 04:41 | Here we have covered scalar user-
defined functions, functions that operate on
| | 04:44 | single rows and columns.
| | 04:46 | Aggregate user-defined functions are
covered in another movie in this chapter.
| | Collapse this transcript |
| Building aggregate user-defined functions| 00:00 | Aggregate user-defined functions are
just like scalar user-defined functions,
| | 00:03 | but they operate on groups of rows.
| | 00:06 | For example, here we have a scalar user
-defined function, called SEC_TO_TIME,
| | 00:11 | which takes this duration field, which
is in number of seconds, and transforms
| | 00:17 | it into a form that we might want to look at,
in minutes and seconds, with a colon in between.
| | 00:22 | As an example of an aggregate function,
we can look at this version here, which
| | 00:28 | will aggregate the duration column and
give us a sum of all of them in hours,
| | 00:35 | minutes, and seconds. And in this case
we are grouping it by album_id, and so we
| | 00:40 | can see for each album that we
have hours, minutes, and seconds.
| | 00:44 | Now we could have just used the SEC_TO_
TIME function and aggregated it manually
| | 00:49 | using SUM, and taken the sum of duration
and displayed in seconds to time. And in
| | 00:56 | this case, we will notice that some
of these might be over an hour long.
| | 01:00 | So we may want to have a separate one
that gives us hours, minutes, and seconds
| | 01:03 | and does the aggregation by
itself, and that's what we have here.
| | 01:10 | So this is written in PHP, and it's
actually included in SID. And you are
| | 01:15 | welcome to look at it, and use it, and
change it, and do what you'd like with it.
| | 01:19 | And here we have CreateAggregate, and what
this does is it registers the aggregate
| | 01:24 | function with SQLite, and
allows us to write it in PHP.
| | 01:29 | Now remember for the scalar user-
defined functions, we passed it a name to use
| | 01:34 | with the SQL and the
name of the function in PHP.
| | 01:39 | In the case of the aggregate
function, we actually need two PHP functions.
| | 01:43 | So this is the name of the function
for SQL, and then we need a step function
| | 01:48 | and a finalized function.
| | 01:49 | So we actually need to write two
separate functions for the PHP side of this.
| | 01:54 | So we will take a look at these.
| | 01:55 | Here is the step function, and
here is the finalized function.
| | 02:00 | The 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:10 | passed, so you can have more out here.
And the finalized function takes the
| | 02:15 | $context and the $rownumber.
| | 02:17 | The step function returns
the context each time it's called.
| | 02:21 | So the step function will get called
for each row in the aggregation, and the
| | 02:26 | finalized function will get called at the end.
| | 02:29 | So in this case, all I do is I accumulate the
sum of the values, so for each value that
| | 02:35 | gets passed into the step, I
simply add it into the context.
| | 02:39 | When it's time to display it, then I
take the context and I split it apart into
| | 02:44 | minutes and seconds, and I set hours to
zero. And if minutes is greater than 60,
| | 02:49 | then I go ahead and I adjust the
minutes and calculate the hours and then
| | 02:54 | display it in the
string format using sprintf.
| | 02:57 | So it's very, very simple,
and as you can see, it works well.
| | 03:01 | In fact, if I want to want to include
it in a JOIN, it still works well, and it
| | 03:06 | actually gives us some
really useful results, like that.
| | 03:12 | So aggregate user-defined functions are
just like scalar user-defined functions,
| | 03:16 | but they operate on groups of rows.
| | 03:18 | User-defined functions in SQLite may
be coded in the host language, in this
| | 03:22 | case PHP.
| | Collapse this transcript |
|
|
8. SQLite Aggregate FunctionsUnderstanding aggregate functions| 00:00 | Aggregate functions are functions
that operate on multiple rows at a time.
| | 00:04 | For example, in this query the COUNT
function takes all the rows FROM the City
| | 00:10 | table and returns one result,
based on counting all of those rows.
| | 00:15 | So that makes it an aggregate
function because it's operating on an
| | 00:18 | aggregation of rows.
| | 00:20 | In this example, all the rows where the
WHERE clause is satisfied, that is all the
| | 00:26 | rows where CountryCode = 'USA';,
| | 00:28 | are taken and the (Population)
column from all those rows is combined and
| | 00:33 | averaged and one result is
returned based on all of those rows.
| | 00:38 | So that makes this also an
aggregate function or an aggregate query.
| | 00:42 | The GROUP BY clause is used to group rows
together, and then those aggregations are taken
| | 00:49 | on those groups of rows.
| | 00:50 | For example, in this query the
GROUP BY clause is GROUP BY CountryCode.
| | 00:56 | So all of the rows where the Country
Code is the same are taken together as a
| | 01:01 | group, and then the Population is
averaged for all of those rows, the
| | 01:05 | CountryCode is also displayed in the
result, and you get one row in your
| | 01:09 | results per group of rows where the
CountryCode is the same. So you will have
| | 01:14 | one row for this CountryCode, one row
for that CountryCode, and all of the rows
| | 01:18 | where that CountryCode is the same
will be taken together for the average and
| | 01:24 | for that row of the result.
| | 01:26 | The HAVING clause is used to select
which rows are going to be returned.
| | 01:32 | For example, in this query, we
are grouped by CountryCode again.
| | 01:36 | This is basically the same query as
above, but the returned rows will have an
| | 01:41 | average population of greater than one million.
| | 01:45 | So the query will have to be run for
all of the groups, in order for that AVG
| | 01:49 | (Population) to be calculated,
| | 01:51 | but the resulting rows where the HAVING
clause is not satisfied will be discarded.
| | 01:57 | We will get into the details of all of
this in the rest of chapter. For now,
| | 02:02 | understand that aggregate functions
make it possible to perform operations that
| | 02:06 | require multiple rows, like
sums, averages, and groupings.
| | 02:11 | Aggregates are an essential part of SQL.
| | Collapse this transcript |
| Counting rows with COUNT| 00:00 | When the question is 'how many?' the
answer is usually the COUNT function.
| | 00:04 | The COUNT function is probably the
most common aggregate function in SQL, and
| | 00:09 | it looks like this.
| | 00:15 | Now we will go ahead and
select the world database for this.
| | 00:19 | We are selecting COUNT FROM City, and so this
will count all of the rows in the city table.
| | 00:25 | The asterisk inside the COUNT
function is a special case, and that means
| | 00:31 | to count every row.
| | 00:32 | Normally COUNT will only count values
that are not NULL, and in this case with
| | 00:38 | the asterisk, it goes ahead
and counts every single row.
| | 00:42 | So this is very common, and it's
very fast; most database engines are
| | 00:46 | optimized to do this quickly.
| | 00:48 | So we return one row in the result, and
that's in fact what we have down here,
| | 00:52 | but the number in that row, 4079, is
the number of rows in the city table.
| | 00:58 | If I want to break this down by
district, I can say
| | 01:03 | GROUP BY District.
| | 01:06 | And if I say Go I will just get a
whole lot of numbers, one per row.
| | 01:11 | We will see there are 1,367 rows in the result.
| | 01:14 | We know there are four thousand and
something rows in the table, but it's just a
| | 01:18 | bunch of numbers, and
that's not really very helpful.
| | 01:20 | So we want to also know which number
represents which district, so we can say
| | 01:24 | SELECT District, COUNT (*) FROM City
GROUP BY District, and then we will get a
| | 01:30 | result where we have a district with each count.
| | 01:33 | This is an interesting result, and I
want to show you a wrong way to do this.
| | 01:37 | If we take out the GROUP BY and we say
SELECT District, COUNT (*) FROM City, we
| | 01:44 | will get one row in the result, and we
will get one district, and we have no
| | 01:47 | idea which district that is.
| | 01:49 | It's just the one that it happened to pick,
and I honestly don't know how it picks that.
| | 01:54 | It's obviously not alphabetical, but
it is wrong, because there is not one
| | 01:58 | district with 4079 entries.
| | 02:01 | So when you GROUP BY, then you know
that each group has one district, and so
| | 02:10 | whatever is displayed in that row for
that COUNT is the district for that row.
| | 02:16 | So you have to be careful, if you are
putting something that's not aggregated in
| | 02:19 | your results, that that be equal to what
it is that you are Grouping By, so that
| | 02:24 | you know that that result is accurate.
And we will get into some more examples
| | 02:28 | of how this can get a little bit tricky later.
| | 02:31 | This is a little bit more useful, but
it's probably not as useful as it can be,
| | 02:36 | so I am going to put this on another
line, and I am going to say ORDER BY the
| | 02:42 | Count, so that I am going to say as
COUNT here, ORDER BY the Count DESC;.
| | 02:48 | So this way I will get my counts top
to bottom, and I had to do this because
| | 02:53 | otherwise the column is named that,
and that's hard to put in the ORDER BY.
| | 02:57 | So we will go ahead and run this query,
and now we have the big ones at the top
| | 03:02 | and the small ones at the bottom.
| | 03:04 | We can further select this by saying
HAVING a Count > 10, and so instead of 1,300
| | 03:13 | rows now, we will get 68 rows, and
that's a manageable result, and there are all
| | 03:19 | of the districts that have more
than 10 entries in the city table.
| | 03:23 | So COUNT is a simple and powerful
tool for counting rows or groups of rows
| | 03:28 | in many circumstances.
| | Collapse this transcript |
| Building with the SUM and TOTAL functions| 00:00 | The SUM function is used for
adding the numeric values in a column.
| | 00:03 | For example, if I wanted to know the
sum total of all the population figures in
| | 00:09 | the Country table, I'd select the world
database and put in this query here.
| | 00:21 | And now I have a very big number, which is
the sum of all the population numbers in
| | 00:26 | the Population column from the Country table.
| | 00:28 | Now the thing about the SUM
function is that in SQLite it will give you
| | 00:34 | an INTEGER result if all of the values in
the column can be represented as an Integer.
| | 00:41 | If you want it to be a floating point
value, you can use the SQLite-specific
| | 00:47 | function called TOTAL, and this
is not an SQL standard function.
| | 00:51 | This is specific to SQLite. And if I
say TOTAL instead of SUM here, then I will
| | 00:57 | get a floating point, and you can see
0.0 because all of these values can be
| | 01:02 | represented as an integer.
| | 01:03 | So I will return this to SUM because
Integer is fine for my purposes here.
| | 01:09 | And now I might want to
make this a more useful thing.
| | 01:11 | I might want to sum the population by continent.
| | 01:15 | So I can say GROUP BY Continent, and I
can list the continent over here because
| | 01:22 | we are grouping by it.
| | 01:23 | And so I know that I will get an
accurate value and Go. And now I have, broken
| | 01:28 | down by continent, the Population, and
if I wanted to order those by the largest
| | 01:34 | ones at the top, I can say ORDER BY.
| | 01:37 | And now I need to name my SUM here
as something that I can use in the ORDER
| | 01:40 | BY clause, so I will just call it Pop,
and I will say ORDER BY Pop, and I will
| | 01:45 | say descending (DESC), so I get the big
numbers at the top. And there we have it:
| | 01:50 | ORDER BY Population, Grouped By
Continent, and you can see Asia is the most
| | 01:55 | populous continent.
| | 01:56 | SUM is a standard workhorse function
that you are going to use often whenever
| | 01:59 | you need to add a column of numbers.
| | 02:02 | TOTAL is a SQLite-specific
version of SUM that always returns a
| | 02:06 | floating point value.
| | Collapse this transcript |
| Finding minimum and maximum values with MIN and MAX| 00:00 | To find the maximum or minimum value in a
column, we'll use the MAX and MIN functions.
| | 00:05 | For example, if we want to find the
country with the greatest surface area from
| | 00:10 | the country table, go ahead in here and
select the world database and SELECT MAX
| | 00:17 | of the SurfaceArea column FROM Country,
| | 00:26 | so we get a number here.
| | 00:27 | Now let's say we want to know the country
with the largest surface area on each continent.
| | 00:32 | Then we can GROUP BY Continent,
and we have a list of surface areas.
| | 00:41 | So we want to know which continent that
is. Because Continent is in our GROUP BY
| | 00:45 | clause, we can put it over here on the
left-hand side, and we know that we'll
| | 00:49 | get the name of each continent for
each group, because once those are grouped
| | 00:54 | then every row in that group
has the same value for Continent.
| | 00:59 | So Africa has that number,
Antarctica has that number, et cetera.
| | 01:03 | If I want to know which country it is
within each of these continents that has
| | 01:09 | the largest surface area, that's
where this starts becoming tricky.
| | 01:13 | Because I am grouping by continent, if I
just put over here, and I say I want to
| | 01:18 | know the name of that country, the
result it will give me will be a name that's
| | 01:23 | in that group, but not the
name that has that maximum value.
| | 01:28 | So if I want to get that result, this
query becomes a little bit more complicated.
| | 01:33 | What we need to do is we need to
take this part of it here and we need to
| | 01:37 | make it into a subquery.
| | 01:39 | So I am going to do that like this,
put this in parentheses and say SELECT
| | 01:45 | Continent and MAX(SurfaceArea).
| | 01:47 | We'll go ahead and give that a name that
we can use elsewhere and say MaxSA FROM
| | 01:52 | Country and GROUP BY Continent.
| | 01:55 | So that will be my subquery, and
I'll name the whole thing AS csa.
| | 02:00 | So that's Continent SurfaceArea.
| | 02:04 | Now I can SELECT from
here, I can say csa.Continent,
| | 02:09 | so, that will give me this value for
my subquery, and I can say csa.MaxSA.
| | 02:18 | So, that will give me this
value here from my query.
| | 02:23 | I can say FROM, and I still have Name
out here, because I am going to get that
| | 02:27 | in a JOIN, and I am going to JOIN to
the Country table, and that's where I am
| | 02:33 | going to find that value AS
c, and here's the tricky part.
| | 02:38 | I put the c. here so we know what that is.
| | 02:41 | I am going to JOIN ON c.SurfaceArea,
| | 02:48 | so the SurfaceArea from the
Country table equals csa.MaxSA.
| | 02:55 | So where this ON condition is true,
that's where it'll get this Name.
| | 03:00 | So that will be the name of the
country within this subquery that has this
| | 03:06 | maximum value to it.
| | 03:07 | Now we can ORDER it BY MaxSA DESC, so
we get the biggest values at the top,
| | 03:16 | go ahead and run this query, and now
for each continent the maximum SurfaceArea
| | 03:21 | we get the Name of that Country.
| | 03:23 | So it's a little bit complicated.
| | 03:25 | It seems a little bit obtuse how we do this.
| | 03:28 | But this is because of
the way the GROUP BY works.
| | 03:31 | GROUP BY works by taking an expression
and giving you a group of all the rows
| | 03:37 | where that expression is exactly the same.
| | 03:40 | So, that allows you to use that
expression in the SELECT side of your SELECT
| | 03:46 | query in the left-hand side, in the
results side, and know that you will get the
| | 03:50 | result that you're looking for.
| | 03:51 | But when you're looking for anything
else, this MAX(SurfaceArea) is only true
| | 03:55 | for one of those values in that group.
| | 03:58 | The database engine doesn't really
have any way of giving that to you.
| | 04:02 | So what you need to do is you need to
group this as a subquery and then JOIN
| | 04:06 | it to your table and look for the row
where that maximum value is equal to the
| | 04:11 | value that you're looking for, and then use
that to extract the name, and that works.
| | 04:17 | So this is also an excellent
demonstration of how subselects work.
| | 04:21 | So the MAX and MIN functions are
effective tools for finding the maximum and
| | 04:25 | minimum values in a column.
Combined with JOINS and SUBSELECTS these functions
| | 04:30 | can be effective reporting tools.
| | Collapse this transcript |
| Finding averages with AVG| 00:00 | The AVG function, average, is used for
finding average values from numeric columns.
| | 00:05 | For example, if we want to find the
average population of all the cities in the
| | 00:16 | city table, we'll select the world
database, and there we have the average
| | 00:23 | population for all the cities in the city table.
| | 00:26 | Now if we want to group them by
district, we can say GROUP BY District, and
| | 00:33 | because we're grouping by District, we
can put District in the left-hand side as
| | 00:37 | well, and now we have it
grouped by district: 1367 districts.
| | 00:44 | These seem to be ordered by district as
well, so we can change that order. We can
| | 00:49 | say ORDER BY and if we want this to be
order by our average population, we can
| | 00:53 | name this as AvgPop and then say ORDER
BY AvgPop, and make it Descending so that
| | 01:03 | it has got the big ones at the top.
And now we have the largest districts at
| | 01:09 | the top, and we still have 1300 of them
so why don't we go ahead and break this apart
| | 01:16 | into separate lines, make this a little bit
easier, and we're going to use the HAVING clause.
| | 01:24 | The HAVING clause goes
between GROUP BY and ORDER BY here.
| | 01:29 | HAVING is like WHERE, but for
aggregates, and so I can say HAVING AvgPop
| | 01:35 | greater than 100000.
| | 01:36 | Now we have just got 75 rows.
| | 01:41 | We've a manageable list, and
there we have our averages.
| | 01:44 | So you will use the Average function
when you need to find the average value
| | 01:48 | in a numeric column.
| | Collapse this transcript |
| Grouping results with GROUP BY| 00:00 | The GROUP BY clause is used to
create groups of rows for use with the
| | 00:03 | aggregate functions.
| | 00:05 | For example, I'll select the world database
here for my examples, and I'll just put
| | 00:09 | in a simple aggregate function, the
COUNT function FROM the City table. And what
| | 00:17 | this will do is this will count all of
the rows in the City table, and we'll get
| | 00:21 | one result here, 4,079, and we see
our SELECT query returned one row.
| | 00:26 | So if I wanted a separate COUNT for
all the cities in each district, I can say
| | 00:32 | GROUP BY District, and now I will
get 1,367 rows, each of them with a
| | 00:41 | different number in them.
| | 00:43 | So the way this works is the database
engine will typically order the table by
| | 00:48 | the GROUP BY expression, and then for
all the rows where the result of that
| | 00:52 | expression is exactly the same, it
will group those rows together and perform
| | 00:57 | the aggregate functions on those rows.
| | 01:00 | So in this case, our expression is just
a simple column name. So for all of the
| | 01:05 | rows where that column has exactly the
same value, it will run this aggregate
| | 01:11 | function here, the COUNT function.
| | 01:13 | So because the District column is the
entire GROUP BY expression, I can put it
| | 01:18 | over here on the left-hand side as well,
and I know that the value that will be
| | 01:26 | displayed will be correct for each
row in the results, because that value is
| | 01:31 | going to be exactly the same for
every row that's aggregated together.
| | 01:35 | So there is only one choice for
the database engine to give me.
| | 01:39 | So if I say Go here, I now get
a district for each of these.
| | 01:42 | If I were to put something else over
here, like say Name, that is not in the
| | 01:50 | GROUP BY expression, then I'll get
just some random value over here.
| | 01:56 | So the name, for example, now let's
find one with a good number, in this row
| | 02:01 | here there's 15 different names in
this group, and I have no way of knowing
| | 02:06 | which one was selected.
| | 02:09 | So if you're going to put something on
the left-hand side, you need to know that
| | 02:12 | it has a value that's the same
for all the rows in the group.
| | 02:17 | So the GROUP BY clause is how you
group sets of rows together for use by
| | 02:21 | aggregate functions.
| | Collapse this transcript |
| Selecting for aggregation with HAVING| 00:00 | The HAVING clause is to
aggregations what WHERE is to rows.
| | 00:03 | For example, if I select the world
database, and let's say I have a query that
| | 00:09 | gives me the average population grouped
by district, from the City table, so it
| | 00:14 | will look like this, and I'll select Go here,
| | 00:32 | now I get 1,367 rows because that's how
many different districts there are in
| | 00:38 | this table. Maybe I don't want that
many results. Maybe I just want those
| | 00:42 | results where the average
population is greater than say a million.
| | 00:46 | So I can use the HAVING clause here,
I could say HAVING, and I can use this
| | 00:52 | aggregated result, average population
greater than 1000000, and now when I submit
| | 00:59 | my query again, I've got only 75 rows.
| | 01:03 | In fact, I can ORDER this BY as well so
that I have the bigger ones at the top,
| | 01:09 | and make that descending so the bigger
ones are at the top, and there we have
| | 01:14 | the average population in each of
these groups of cities grouped by district.
| | 01:20 | Now this is HAVING because we're
operating on an aggregated result.
| | 01:25 | If I were to put WHERE here instead,
I'll get a syntax error because WHERE
| | 01:32 | doesn't actually even go there.
| | 01:34 | WHERE would go right after the FROM, and
so I'll just take that out, and I'll put
| | 01:39 | it in here instead. And now I get a
misuse of aggregate because this result here
| | 01:47 | is an aggregate result, and WHERE can't
handle that. So really, what it needs to be
| | 01:53 | is a HAVING clause, and it needs to go
after GROUP BY, and that will give us the
| | 02:00 | result that we want.
| | 02:01 | So HAVING is for aggregates what WHERE
is for rows, and the HAVING clause is
| | 02:10 | essential in selecting which
rows of the aggregated result
| | 02:14 | you want to return from an aggregated query.
| | Collapse this transcript |
|
|
9. SQLite Date and Time FunctionsUnderstanding SQLite support for dates and times| 00:02 | SQLite does not have a specific data
type for representing dates and times.
| | 00:07 | Dates and times in SQLite may be
stored in one of three formats, depending
| | 00:11 | upon the representation.
| | 00:13 | TEXT strings are used to
represent standard ISO 8601 values,
| | 00:19 | floating point numbers are used
represent standard JULIAN DATES, and INTEGERS are
| | 00:24 | used to represent UNIX epoch dates.
| | 00:27 | Conversion functions are available for
converting between types, in particular
| | 00:31 | the JULIANDAY FUNCTION for generating
JULIAN numbers and STREFTIME FUNCTION for
| | 00:37 | generating all kinds of formats,
in particular here for generating the UNIX epoch Dates.
| | 00:43 | Let's look at some examples. So, Let's
create a table, and we will just use the
| | 00:48 | in-memory database right now. And we
will just give it a couple of columns for
| | 00:54 | dates, d1 and d2, and we
will insert some values.
| | 00:58 | We use the DATETIME function for
generating the ISO date strings, and now
| | 01:08 | means the date and time right now, and
for the second column, we will say
| | 01:13 | DATETIME and now "+ 7" days, and now
when we select * from "t" you will see we get
| | 01:26 | these two date and times.
| | 01:27 | This is the ISO string format, ISO
8601, and we have year, month, day, and the
| | 01:35 | time in hour, minutes, and seconds.
| | 01:37 | So that's very readable, and it's
also very sortable, which is nice.
| | 01:40 | Now, if we want JULIAN Days, where it says
DATETIME here, we can just say JULIAN DAY.
| | 01:47 | You can do the same thing here,
and you will notice that we get these
| | 01:56 | floating point numbers, and they have
quite a bit of resolution. And these
| | 02:00 | represent the number of days
since that date, a long time ago, four
| | 02:05 | thousand something years B.C.E.
| | 02:07 | And finally, if we want UNIX epoch
time, and this is handled little bit
| | 02:11 | differently. We are going to declare
these as INTEGER columns; otherwise, these
| | 02:17 | INTEGER numbers will be stored as TEXT,
and we're going to use STREFTIME for generating
| | 02:25 | UNIX epoch numbers and %s as the format.
| | 02:28 | We will get into details of
STREFTIME in another movie in this chapter.
| | 02:37 | And there you see, we have these UNIX
epoch numbers, and if we also select the
| | 02:42 | TYPEOF, say d1 and TYPEOF d1 and d2 and
TYPEOF d2, then we can we see that both
| | 02:58 | are integers because we have given
them these INTEGER declarations here.
| | 03:01 | Now, if we want to convert these back,
and this is what I'm saying the UNIX epoch
| | 03:06 | times are handled little bit differently,
| | 03:08 | if we want to convert these back
into DATETIME, we can simply say SELECT
| | 03:13 | DATETIME of d1 and give it this UNIX
epoch, so that it knows that that's what it
| | 03:21 | is, and do the same thing with column 2.
| | 03:35 | You see now, we get those nice string
representations, whereas if we wanted to
| | 03:40 | convert back from the JULIAN Days,
I'll just make these REAL for that purpose,
| | 03:49 | and just make this JULIAND Day of
now and JULIAN Day of now Plus 7 days,
| | 03:59 | now we don't need this UNIX epoch here.
| | 04:02 | It'll know what they are,
| | 04:03 | it will recognize what they are,
and convert them back like that.
| | 04:07 | So we have REAL numbers, and they
get converted back just with the
| | 04:10 | DATETIME FUNCTION like that.
| | 04:12 | So dates and times in SQLite are
represented in any of these three different
| | 04:18 | formats: as TEXT, REAL Numbers, or
INTEGERs, and they are easy to convert
| | 04:23 | back and forth using the built-in
functions JULIANDAY, DATETIME, and the
| | 04:28 | STREFTIME function.
| | Collapse this transcript |
| Getting readable, sortable dates and times| 00:00 | When you need a good date and time
representation that is readable and also sorts well,
| | 00:04 | the ISO 8601 standard representation is
used by the DATE, TIME and DATETIME functions.
| | 00:11 | So using our in-memory database here,
we'll create a table, and here we have
| | 00:24 | two columns: d1 and d2, and they are both text,
and we are going to insert values into them.
| | 00:46 | And then we will take a look at
those values, and there we have it.
| | 00:53 | So these are standard ISO 8601
date and time representations.
| | 00:59 | We have the year, month, day and hour,
minutes, second, and this one on the left
| | 01:05 | is in UTC, which is Greenwich mean time,
and the one of the right is in local
| | 01:10 | time, which for my computer in Ventura, California.
| | 01:14 | So that's Pacific Daylight Time right
now, and these are generated by this day
| | 01:18 | DATETIME now and DATETIME now local time.
| | 01:24 | So what goes inside of the parentheses
here for DATETIME is any date and time
| | 01:30 | representation and/or modifiers.
| | 01:34 | The modifiers are listed on this web
page here at sqlite.org. Down around the
| | 01:40 | middle of the page, you see a subheading
called Modifiers, and it tells you all
| | 01:44 | of the modifiers that you can use.
| | 01:46 | So you can say plus or minus hours,
minutes, seconds, years, months, so, for
| | 01:52 | example, if I wanted to say local time +3 days,
| | 01:58 | I can do that, and I'll get local time +3 days.
| | 02:03 | So this is on the 19th, it
would be on the 16th. And you can string
| | 02:08 | together as many modifiers as you like,
and that works really for all of the
| | 02:13 | date and time functions in SQLite.
| | 02:14 | For example, and I will just put this
back to local time, and if instead of
| | 02:21 | SELECT * FROM t, if I said SELECT TIME
(d2) FROM t, that would give me just the time.
| | 02:32 | If I say TIME (d2), '+5 hours' then it
would give me that time five hours later,
| | 02:42 | or I can save DATE (d2, '+5 days, and
it will give me the local time +5 days,
| | 02:51 | which would be the 21st, because today is the 16th.
| | 02:55 | The DATE function just gives you the
date in the same ISO format: year, month,
| | 03:00 | day, the TIME function just gives you
the time, where DATETIME will give you both,
| | 03:09 | as we saw when we put them into the
columns in the table. And just like all of
| | 03:15 | SQLite functions, it will take any kind of
modifier in any kind of sequence that makes sense.
| | 03:21 | The DATE, TIME and DATETIME
functions are a simple way to get readable,
| | 03:25 | sortable date strings.
| | 03:27 | These strings also work well with
the STRFTIME function for conversion to
| | 03:30 | other formats, and the STRFTIME
function will be covered in another movie in
| | 03:34 | this chapter.
| | Collapse this transcript |
| Getting high-resolution dates and times with JULIANDAY| 00:00 | The Julian Day format is the standard
among astronomers, and is recommended by
| | 00:03 | the International Astronomers Union.
| | 00:05 | When represented as an IEEE floating
point number, it has good resolution and can
| | 00:10 | represent thousandths of a second.
| | 00:12 | To demonstrate the JULIANDAY
function, we're going to switch to the test
| | 00:16 | database, and I'm just
going to paste in some SQL here.
| | 00:20 | What this does is it creates a table,
and it inserts a number of values in
| | 00:24 | Julian Day format. The two columns in
the table are both declared as REAL, which
| | 00:28 | is the best format for storing a JULIANDAY.
And then at the end, we select each
| | 00:33 | of these columns along with a STRFTIME
formatting representation, which formats
| | 00:38 | them as ISO standard DATE strings with
floating point seconds, and the STRFTIME
| | 00:44 | function is covered in
another movie in this chapter.
| | 00:47 | So in inserting all of these rows one
at a time, you'll notice that because
| | 00:52 | this is a rather fast computer, we don't
give much difference in the time between them.
| | 00:56 | So these two came out of the same
thousandths of a second, while each of these
| | 01:00 | is just 1/1000th of a second apart,
but it does represent how good the
| | 01:05 | resolution is of the JULIANDAY
function and how valuable that can be for
| | 01:09 | timestamping in a log file.
| | 01:11 | If you look at the SQLite
documentation for the JULIANDAY function, it
| | 01:15 | mentions that the Julian Day is the
number of days since noon in Greenwich on
| | 01:20 | November 24, 4714 B.C.
| | 01:23 | And it says, according to the proleptic
Gregorian calendar, and if you like me
| | 01:27 | look at that, and you say wait a minute
Julian day, that should be the Proleptic
| | 01:31 | Julian calendar and so what I was able
to find out about this is the Julian days
| | 01:36 | themselves are very, very useful
because they convert well to IEEE
| | 01:40 | floating point, and they give a lot of
resolution over a wide period of time.
| | 01:44 | So they don't have some of the
problems that the UNIX Epoch time has, which is
| | 01:49 | common in computer systems.
| | 01:51 | On the other hand, the ISO 8601 standard
specifies that you must use a Gregorian
| | 01:58 | calendar for ISO 8601 compliance for
computer dates, and that also make sense
| | 02:03 | because the Gregorian calendar lends
itself much better, in consistency, to
| | 02:08 | computer dates and times.
| | 02:10 | So that's why there's a discrepancy there.
| | 02:12 | It uses the Julian day format, and it
uses the proleptic Gregorian calendar for
| | 02:17 | compliance with ISO 8601.
| | 02:21 | So the JULIANDAY function is very easy to use.
| | 02:23 | It uses all of the same modifiers the are
available in all the other functions in
| | 02:27 | SQLite and STRFTIME automatically
recognizes these values when converting
| | 02:33 | back to other formats.
| | 02:35 | So in SQLite, JULIANDAY are stored as
real numbers and have better resolution
| | 02:40 | than TEXT or INTEGER representations,
and these values are efficient and well-
| | 02:44 | suited to timestamps and other
applications where resolution is important.
| | Collapse this transcript |
| Formatting dates and times with STRFTIME| 00:00 | The STRFTIME function is useful for
formatting time and date in formats other
| | 00:04 | than the standard DATE, TIME,
DATETIME or JULIANDAY functions.
| | 00:08 | For an example, we will be using the
in-memory database here, and we'll go ahead
| | 00:11 | and create a table. And we will insert
some values, and then we'll take a look
| | 00:41 | at it with STRFTIME.
| | 00:55 | Now I'll put just a simple format in
here. We will use a percent capital 'Y' in
| | 00:59 | this one, and that will give us the
year and a percent capital 'H' in this one
| | 01:05 | and only was the current hour, and when I
say Go, we get 2010 for the year and 18
| | 01:13 | for the hour because it's a little after
6 p.m. here in California right now.
| | 01:17 | And so you can see that STRFTIME will
take these DATETIME formatted strings,
| | 01:23 | if I go ahead and SELECT d1 here and
d2 here, you will be able to see that
| | 01:27 | those are ISO formatted date strings, because
those were provided by the DATETIME function.
| | 01:33 | And so STRFTIME can take those strings,
it can also take JULIANDAY strings and
| | 01:37 | format them however you like.
| | 01:39 | In fact, STRFTIME is used for creating
both JULIANDAY strings, the capital 'J'
| | 01:46 | there for Julian day or lower case 's'
for UNIX Epoch strings. And in fact, all of
| | 01:54 | the standard formatting functions,
DATETIME, DATE and TIME and JULIANDAY are
| | 01:59 | implemented using STRFTIME.
| | 02:00 | So if I just say go here, you'll see
that we have the JULIANDAY there and the
| | 02:05 | UNIX Epoch time there.
| | 02:07 | All of the formatting options are
available online at the sqlite.org web site on
| | 02:13 | this page here, and it's about that far down
the page over there. And you see it's
| | 02:18 | based on the STRFTIME library function
from the standard 'C' library. It does not
| | 02:23 | implement the entire library, and in
particular any of the functions that would
| | 02:27 | print out localized names, like days,
or weeks, or names of months; those
| | 02:32 | functions are not implemented.
| | 02:33 | This list here is the complete list of
functions that are implemented, and if you
| | 02:38 | try to use anything else, you will
likely get a NULL value or an error message.
| | 02:44 | So you will use this STRFTIME function
if you have special formatting needs for
| | 02:48 | your dates and times.
| | 02:49 | If you need a format with a month, or
weekday names, you'll need to use the
| | 02:54 | functions in PHP, or your
host scripting language.
| | Collapse this transcript |
|
|
10. Sorting and IndexingUnderstanding collation| 00:00 | Collation order, sometimes called
collation sequence, is a term to describe the
| | 00:04 | rules a database system
uses to put things in order.
| | 00:07 | SQLite supports three coalition orders:
| | 00:10 | BINARY, which uses the numeric
value of the Binary content;
| | 00:14 | NOCASE, which treats the 26 uppercase
ASCII characters as equivalent to their
| | 00:20 | lowercase counterparts; and RTRIM
which works exactly like BINARY but
| | 00:25 | ignores trailing spaces.
| | 00:27 | It's important to note in this context
that NOCASE, which is the one that treats
| | 00:32 | upper and lowercase characters as the
same, only works with ASCII characters;
| | 00:36 | it does not work with extended Unicode
characters or other character sets.
| | 00:41 | Collation may be specified in the
create table statement in the column
| | 00:45 | definition like this or collation may be
specified using the COLLATE operator in your query.
| | 00:51 | As we discussed sorting and indexing, it
can be important to understand how SQLite
| | 00:56 | uses collation sequences to order its records.
| | Collapse this transcript |
| Sorting results with ORDER BY| 00:00 | When you need your results to be in a
certain order, you'll use the ORDER BY
| | 00:03 | clause to get sorted results.
| | 00:05 | For example, from the world database, we
have this simple query: SELECT * FROM Country;
| | 00:15 | You'll see we have lot of results
that are not in any particular order.
| | 00:20 | Now if we wanted them ordered by say
Region, so it would be this column here, you
| | 00:25 | can say ORDER BY Region,
and it's as simple as that.
| | 00:31 | Now they're in order by region, so we
have all the Antarcticas together, the
| | 00:35 | Australias together, the Baltics together, etc.
| | 00:38 | Now if we wanted them ordered by
population within region, we could put in a
| | 00:44 | comma here and say Population.
| | 00:47 | That's this column here.
| | 00:48 | Now of course, there is no population
in any of the Antarctica ones, but if
| | 00:53 | we look at Australia and New Zealand, we'll see
that we have 600, 2000, 2500 and 3.8 million.
| | 01:00 | So those are now in order.
| | 01:02 | Now perhaps we want the population to be
descending within region. We just put in
| | 01:07 | the descending keyword, DESC, after
Population and say Go, and now we have
| | 01:15 | Australia at the top and then New
Zealand, and so it goes larger, smaller,
| | 01:19 | smaller, smaller, like that.
| | 01:22 | So that's how ORDER BY works.
| | 01:24 | ORDER BY is a simple and powerful way
to get sorted output from your queries.
| | Collapse this transcript |
| Removing duplicate results with DISTINCT| 00:00 | To ensure that you only get unique
rows in your results, use the DISTINCT
| | 00:04 | modifier with your SELECT statement.
| | 00:07 | For example, using the world database,
if I select Region from the Country
| | 00:13 | table, see I get a long list of 239 rows
with just one value of the region in them.
| | 00:22 | And you'll see there is Central Africa,
| | 00:23 | there is Central Africa again, Middle East
several times, and so it's not very useful.
| | 00:29 | If I say DISTINCT, SELECT
DISTINCT Region FROM Country,
| | 00:35 | now I get a list of 25 rows, and
there're all the regions, and they also happen
| | 00:40 | to be in alphabetical order, which most
database engines will do that, and it's
| | 00:44 | part of how they accomplish the DISTINCT.
| | 00:46 | They first sort it into order, and
then just eliminate the duplicates.
| | 00:51 | Now as another example, if I wanted
to see all the CountryCodes that are in
| | 00:56 | the City table, do that, and I get 232 rows,
which is all the CountryCodes in the City table.
| | 01:06 | Now maybe I also want the DISTINCT,
selecting distinct CountryCode, and DISTINCT
| | 01:13 | from City. Now, I get 1,412 rows.
| | 01:16 | Now, I know that there's 4,000 and
something rows in the City table.
| | 01:19 | So this is not everything, but what
I have here is each row is a unique
| | 01:24 | combination of CountryCode and District.
| | 01:28 | So the DISTINCT operator operates on all
of the expressions in the Select clause.
| | 01:36 | So in other words, no matter what I
have here, it's going to make sure that
| | 01:39 | each row is distinct.
| | 01:41 | It's not going to just make
sure each CountryCode is distinct;
| | 01:44 | it's going to make sure each row is distinct.
| | 01:46 | If you need something different
than that, you're probably looking for
| | 01:50 | the GROUPBY clause.
| | 01:52 | DISTINCT is very much like GROUPBY,
but it doesn't do the aggregation.
| | 01:56 | So DISTINCT is useful for ensuring
that every row of output is unique, but if
| | 02:01 | you need real aggregated results,
you'll need to use GROUPBY instead.
| | Collapse this transcript |
| Understanding indexes| 00:00 | Indexes can be a powerful tool for
improving the performance of your queries.
| | 00:04 | There are also costs involved
in implementing indexes.
| | 00:09 | A table consists of many rows and many
columns, where an index consists of many
| | 00:16 | rows and one column.
| | 00:18 | A table is optimized for storage, and
an index is optimized for searching.
| | 00:24 | The purpose of an index is to be able
to quickly find rows in the table, without
| | 00:29 | having to search through the
massive amount of data in the table.
| | 00:32 | Indexes are optimized for searching,
usually using binary trees or some
| | 00:38 | derivative of binary trees,
| | 00:40 | so that it's very quick to locate a
particular record, and then that will point
| | 00:44 | out the row in the table
that you're looking for.
| | 00:47 | The down side of indexes is they take
up space, and they slow down inserts.
| | 00:51 | So you'll need to do your own
real-world tests to make sure you're getting the
| | 00:55 | best performance possible for your situation.
| | 00:58 | Let's take a look at an example.
| | 01:00 | So I'm going to select the world database,
and I'm just going to paste in a query here.
| | 01:07 | So this is a joined query.
| | 01:09 | We're looking for the average
population of the cities in the City table,
| | 01:13 | grouped by country, and we're
displaying both the name of the country and the
| | 01:18 | average population.
| | 01:20 | And so this joined query is getting the
country name from the Country table and
| | 01:27 | doing the group by and the
average on the City table.
| | 01:30 | And so this is how it performs just as
it is. That took about 300 milliseconds.
| | 01:36 | And if I run the query a few times,
we'll see that that's fairly consistent,
| | 01:41 | right around 295, 296, 300 milliseconds.
| | 01:46 | Now I'm going to create a couple of
indexes on the Code column in the Country
| | 01:50 | table and CountryCode column in the City table.
| | 01:53 | Now watch how this impacts the performance.
| | 01:56 |
| | 02:15 | So I'll create these indexes, and
you see it took 25 milliseconds, 24 1/2
| | 02:20 | milliseconds to create those two indexes.
| | 02:23 | Now, I'll just paste that query back in,
and we will run the query, and we see
| | 02:29 | that the query now happens
in about 15 milliseconds.
| | 02:33 | So that's 5% of the time that
it took without the indexes.
| | 02:37 | It's 200% faster. And even with the
cost of creating the indexes, it's
| | 02:43 | still significantly faster, an
order of magnitude faster than it was
| | 02:47 | without the indexes.
| | 02:49 | But now that the indexes are made,
every time I run this query, I don't have to
| | 02:53 | index the entire table again.
| | 02:55 | So in this case, that index is saving me
a lot of time, and judging by the amount
| | 03:00 | of time it took to create
the indexes in the first place,
| | 03:03 | those inserts aren't going
to be impacted too badly.
| | 03:06 | So, indexes are a powerful and useful
technique for improving the performance
| | 03:10 | of database queries.
| | 03:12 | Be mindful of the cost and disk
space and insert update times when
| | 03:16 | designing your indexes.
| | Collapse this transcript |
| Working with primary key indexes| 00:00 | A primary key index is used to create a
unique and primary index for the table.
| | 00:06 | Usually the primary key is the index
that will be most commonly used for finding
| | 00:11 | records in that table.
| | 00:13 | In SQLite, a primary key index works
exactly the same as a unique index, except
| | 00:19 | that only one primary key
index is allowed per table.
| | 00:23 | Let's take a look at how this
works and what it looks like in SQLite.
| | 00:26 | We'll create a table, and we're using
the in-memory database here, and it'll
| | 00:31 | have a column called code, which is primary key.
| | 00:35 | And first column in the table
doesn't have to be the primary key, but it's
| | 00:39 | traditional for the
primary key column to come first.
| | 00:42 | Then we'll call the next one value and
make it a TEXT column and the third one
| | 00:47 | perhaps ycode, and we'll call it UNIQUE.
| | 00:51 | And ycode, that might be a
column that's used to index into another table.
| | 00:55 | Now if we take a look at the SQLITE_
MASTER pseudo-table, we'll see that we've
| | 01:04 | created this table, and there is the
definition, and we have two indexes.
| | 01:09 | The way that they are
represented here is pretty much the same.
| | 01:12 | The only difference between these
two is if we call this one PRIMARY KEY,
| | 01:15 | we'll get a syntax error.
| | 01:18 | It says General error:
1 table "t" has more than one primary key.
| | 01:22 | So we call this one UNIQUE.
| | 01:25 | Really, when you think syntactically
about the words "PRIMARY KEY," we're talking
| | 01:30 | about the primary way that other
tables will find records in this table.
| | 01:35 | So that's what it means.
| | 01:36 | It has that meaning, even though
operationally it's no different than a unique.
| | 01:41 | So go ahead and insert some rows in
here, and we don't need this anymore.
| | 01:45 | So we will say INSERT
INTO t VALUES, and we will
| | 01:51 | say '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:16 | Now we'll SELECT * FROM t;, and
we'll get an error because we have
| | 02:22 | inadvertently put the same value in
all of these different rows for primary
| | 02:26 | key, and those have to be unique.
| | 02:28 | So we say Integrity constraint violation:
| | 02:30 | 19 column code is not unique, and
| | 02:33 | it says query 3, 4 and 5, so we know
which one's they are there, this one and,
| | 02:36 | this one, and this one.
| | 02:37 | So, we need to make these unique, and so
I'll just put b and c and d. Now we have
| | 02:45 | four rows in our table, thing
one, thing two, a, b, c, and d.
| | 02:48 | You'll notice, also, that if I put an X
here, it's still allowed of course, but it
| | 02:53 | doesn't change the order of things.
| | 02:55 | Some databases will also order by that
column by default; SQLite doesn't do that.
| | 03:01 | You'll also notice that I am
allowed to put a NULL in here.
| | 03:06 | In fact, I can put two NULLs in here.
| | 03:10 | So two things that we need to notice
about this is one, NULLs are not disallowed
| | 03:15 | in primary key columns in SQLite.
| | 03:17 | They are in most databases. In fact, the
SQL-92 standard requires that NULLs be
| | 03:24 | disallowed, and there is a note on
the SQLite web site about why this is.
| | 03:28 | It was basically a mistake that's been
perpetuated, and they don't want to break legacy code.
| | 03:33 | They say they may it in the
future, but they probably won't.
| | 03:37 | So this is a behavior that is
non-standard that you need to be aware of.
| | 03:40 | You can, and actually most people do, put NOT
NULL. Even though it's disallowed in most databases,
| | 03:48 | you'll still see in most cases where primary
key is used, you'll say NOT NULL PRIMARY KEY.
| | 03:55 | In fact, now that is not
allowed in either place.
| | 04:02 | So that's how primary key works.
| | 04:04 | Primary key is often used to define
the primary index for a table, and it
| | 04:08 | creates an index where each
value is guaranteed to be unique.
| | Collapse this transcript |
| Understanding how to use the INTEGER PRIMARY KEY function| 00:00 | SQLite has a very easy, fast, and
powerful feature for implementing ID fields.
| | 00:06 | Every row of every table has
a sequential ID called rowid.
| | 00:10 | You can access this by selecting for it,
using rowid, or one of its aliases, oid
| | 00:17 | or rowid with underscores on either side.
| | 00:19 | Let's take a look at how this works.
| | 00:22 | Using the in-memory database, I'm going
to create a table, and it'll just have
| | 00:28 | a few general columns, and
we'll insert some data into it.
| | 00:42 | And the data doesn't really matter here.
| | 00:43 | We'll insert a few rows, and
then we'll select those rows.
| | 00:51 | So, there we have our table, and
there we have the rows for our table.
| | 00:55 | Now, if I want to, in addition to the
rows, and I can just name them here,
| | 00:59 | I can select rowid (SELECT ROWID), like that.
| | 01:04 | So if I if I just do it in lower
case (rowid), it's exactly the same.
| | 01:07 | ROWID is guaranteed to be unique, and
it is also sequential, although under
| | 01:15 | some circumstances
| | 01:16 | it's not really guaranteed to be
sequential; most of the time it is.
| | 01:21 | Now you can access that. You can use rowid.
| | 01:23 | You can say oid like this.
| | 01:25 | You get exactly the same result, or
you can say _rowid_ like that and get
| | 01:33 | exactly the same result.
| | 01:35 | Now that's very convenient and very easy,
but it's also not terribly compatible
| | 01:39 | with the way that other database systems work.
| | 01:41 | So instead, I can define a column in
the table and call it whatever I like and
| | 01:46 | say INTEGER PRIMARY KEY, exactly like that.
| | 01:51 | If I spell it differently or if I
just say INT instead of INTEGER, if I do
| | 01:55 | anything differently at all, it won't
be the same thing. But what this does is
| | 02:00 | this creates a column in the table,
that's not really in the table.
| | 02:04 | It works just like it were in the table,
and it does everything exactly like it
| | 02:09 | were in the table, except that
it's really the rowid column.
| | 02:12 | It's an alias for the rowid column.
| | 02:14 | Now because I now have four columns in the
table, I can't insert my values like this anymore.
| | 02:19 | I now have to say a, b, c, like this,
and I don't need rowid here anymore;
| | 02:28 | in fact, I can just do that.
| | 02:33 | Now I have an ID column
which is this ID column here.
| | 02:38 | It doesn't actually take
up any space in the table.
| | 02:40 | It's really just an alias for the
rowid column that's already there.
| | 02:44 | It doesn't cost anything extra on
inserts, so unlike primary key, it's not
| | 02:49 | creating a separate index.
| | 02:50 | In fact, if I do SELECT * FROM SQLITE_
MASTER, you'll see that we just have the
| | 03:00 | table there, and it doesn't
actually even have a separate index.
| | 03:04 | So this is fast, it's easy, and it's
virtually free, and I include it, just on
| | 03:09 | general principle, in every single table
that I create, and I suggest that you do too.
| | 03:14 | Now, a couple of
interesting properties about this.
| | 03:17 | If I were to come along here and delete a
row, and I'll delete one from the middle,
| | 03:26 | I'll delete row with the ID 3,
| | 03:29 | then I'll go ahead and insert another row,
| | 03:35 | You'll see that there is a hole in
the middle, and the next one that it
| | 03:37 | inserted was number 6.
| | 03:39 | But if the one that I delete is
number 5 because at this point, that's the
| | 03:43 | highest one, I have one, two, three, four, five,
| | 03:47 | then I insert another row,
| | 03:49 | instead of 6 there, I'll give 5
because what it does is it uses the value one
| | 03:54 | higher than the highest value in the table.
| | 03:57 | Now that behavior can be changed
by using the keyword AUTOINCREMENT.
| | 04:00 | Now, you might be used to another
database system, like MySQL, where autoincrement
| | 04:09 | actually creates that automatically
incrementing ID field, like Integer Primary
| | 04:14 | key does in SQLite. In the case of SQLite,
what AUTOINCREMENT does is it changes
| | 04:19 | the behavior of the Integer Primary key.
| | 04:22 | So, now instead of using the value
that's one higher than the highest value in
| | 04:26 | the table, it'll use the value that's
one higher than the highest value that has
| | 04:30 | ever been in the table.
| | 04:32 | So in this case, while I have deleted
number 5, it's still going to use number
| | 04:36 | 6 for the next one.
| | 04:39 | It's created another table here in the
database called sqlite_sequence, which is
| | 04:44 | just one column, one row, to
hold that last highest value.
| | 04:55 | Scroll down here, and you
will see that it's got a 6 in it.
| | 04:59 | Now if I were to insert another row here,
and I will insert it here, and I will
| | 05:06 | actually also put an ID in
it and give it a value of 25.
| | 05:15 | Now it's going to use 26 for the next
highest value, and down here, we have a 26
| | 05:19 | in that because the highest value that
has ever been in that column was 25, and
| | 05:24 | then when we inserted
another one and so it became 26.
| | 05:28 | So the behavior with AUTOINCREMENT is,
quite simply, that the next value used for
| | 05:33 | the ID will be one more than the highest
value that has ever been in that table,
| | 05:38 | and it uses the sqlite_
sequence table to keep track of that.
| | 05:42 | So there is a cost to this.
| | 05:43 | It's not a great cost, but if you
need that behavior, it's available
| | 05:46 | with AUTOINCREMENT.
| | 05:48 | Id fields are very useful, especially
in applications with several tables that
| | 05:52 | interact frequently.
| | 05:53 | Using SQLite's Integer Primary key
feature, they cost virtually nothing.
| | 05:57 | I strongly recommend that you use
ID fields in most of your tables.
| | Collapse this transcript |
|
|
11. TransactionsUnderstanding transactions| 00:00 | Transactions are used to enhance
reliability and performance in your SQL databases.
| | 00:05 | SQLite is ACID-compliant, and that
means that each transaction is atomic.
| | 00:11 | It's either completed and
committed, or it's entirely discarded.
| | 00:15 | The database is always in a consistent state.
| | 00:18 | Transaction data is isolated.
| | 00:21 | It's not available until a
transaction is completed.
| | 00:25 | Completed transactions are durable.
| | 00:27 | They will survive a system failure.
| | 00:29 | SQLite accomplishes this by using transactions.
| | 00:33 | A transaction is one or more
operations that may modify the database.
| | 00:38 | A transaction is only written to
the database when it is complete.
| | 00:42 | A transaction may be rolled
back if it cannot be completed.
| | 00:46 | The database is locked while
a transaction is in progress.
| | 00:50 | Transactions may be used to improve
performance because one write is faster
| | 00:56 | than a hundred writes.
| | 00:57 | Transactions are used to ensure
data integrity and improve performance.
| | 01:01 | SQLite supports transactions using the
BEGIN and COMMIT statements, or the PHP
| | 01:06 | functions beginTransaction and commit.
| | Collapse this transcript |
| Using transactions in SQLite| 00:00 | Transactions are used to ensure data
consistency and reliability, and to
| | 00:04 | improve performance.
| | 00:06 | Let's take a look at some
examples of how this is done.
| | 00:08 | First, we're going to look at a
traditional use of transactions, and I'm going
| | 00:12 | to paste into this window here some SQL.
| | 00:15 | This defines three tables: an item table,
an inventory table, and a sale table.
| | 00:21 | The item table has name of the
item, description of the item.
| | 00:25 | The inventory table has a link to
the item table through item ID, and
| | 00:30 | a quantity for inventory.
| | 00:32 | The sale table has an
item ID, quantity, and price.
| | 00:37 | Now obviously, in real life, these tables
will be a lot more complicated, but for
| | 00:40 | illustration purposes, this will do the trick.
| | 00:43 | Then we insert a few rows into the item
table, and a few rows into the inventory
| | 00:48 | table, and then we'll take
a look at what we've got.
| | 00:51 | So, here we have a join of the inventory, and
item tables, showing what we have in inventory.
| | 00:57 | We have 127 monitors, 12 external storage,
768 printers, and 42 tower computers.
| | 01:05 | So, now let's make a sale.
| | 01:07 | So the way a sale works is we will
insert a sale into the sale table, and
| | 01:12 | we will update the inventory table
to update the quantity on hand for the
| | 01:17 | items that we just sold.
| | 01:19 | Now, you want to do this atomically.
| | 01:22 | That means you want to do both of these
transactions as one unit, so that if for
| | 01:28 | some reason, the inventory table cannot
be updated, you don't get a sale without
| | 01:34 | an update to the inventory.
| | 01:36 | So, either both of these things happen
or neither of these things happen, and
| | 01:40 | the way that happens is with a transaction.
| | 01:42 | Start the transaction by saying BEGIN.
| | 01:45 | That's the statement that begins a transaction.
| | 01:47 | You can also optionally say BEGIN
TRANSACTION, and SQLite will accept that, and
| | 01:53 | that's not entirely standard.
| | 01:54 | So, we'll just use BEGIN because
that's the standard way to start a
| | 01:58 | transaction in SQL.
| | 02:00 | I'll say INSERT INTO sale.
| | 02:02 | We're going to insert the item_id, the
quantity, and the price, and give these VALUES.
| | 02:12 | So the item ID will be 4, and that'll
be the printers, the quantity will be 12,
| | 02:19 | and the price will be 19295.
| | 02:25 | So that's a price in cents because
our price field is an integer.
| | 02:30 | Now we'll update the inventory table,
and we'll set the quantity equals, and
| | 02:38 | here we'll use a subselect to get
the existing quantity so that we can
| | 02:41 | subtract from that.
| | 02:43 | So we'll select quantity from inventory
where ID equals 4, and we'll subtract 12
| | 02:52 | from that, and where id equals 4 for
the update as well. And then commit.
| | 03:00 | And then we'll go ahead, and we'll
look at the inventory table again.
| | 03:05 | So I'll copy and paste this.
| | 03:07 | And we'll also look at the sale table,
and I'll paste that in over here.
| | 03:12 | So, Go, and here we have our inventory
table before the sale, and there is our
| | 03:18 | inventory table after the sale.
| | 03:20 | You can see that printer quantity is
reduced by 12, and there are 12 items
| | 03:24 | in the sales table.
| | 03:26 | So, that's the way it works normally,
but let's say that the inventory database
| | 03:31 | is on another machine in another state, and
| | 03:35 | that update fails because the
Internet connection gets broken.
| | 03:40 | So I want to comment that out.
| | 03:42 | Our program, it detects that,
that wasn't able to happen,
| | 03:47 | so, we roll it back.
| | 03:50 | So, instead of committing,
we execute a ROLLBACK.
| | 03:53 | We've got this INSERT INTO sale.
| | 03:55 | We've already done that, but the
inventory couldn't happen, so we rolled it back.
| | 04:00 | Now what happens is the inventory does not get
updated, and there is nothing in the sale table,
| | 04:06 | because the transaction, the BEGIN
and either COMMIT or ROLLBACK makes that
| | 04:11 | whole transaction one thing.
| | 04:13 | It's either all or nothing; it's atomic.
| | 04:16 | It's either completed and committed,
or the entire transaction is discarded.
| | 04:21 | So, this is the traditional use of transactions.
| | 04:25 | When people think of transactions, they
think of something like this, of being
| | 04:28 | able to group together a number of
database actions that would otherwise make
| | 04:32 | changes to your database, so that if any one
of them fails, the entire thing is discarded,
| | 04:39 | so you don't get an update to one table
without a corresponding update to another.
| | 04:44 | So, this is common in inventory applications.
| | 04:46 | It's common in general ledger
applications, and in things like that, where a
| | 04:50 | number of things are tied together.
| | 04:52 | There is one other use of transactions,
which is equally powerful and equally
| | 04:58 | important, and we're going
to talk about that right now.
| | 05:02 | This is just a small insert of
a thousand records into a table.
| | 05:07 | So I'm going to go ahead and
take this entire file and select it.
| | 05:11 | I'm pressing Command+A on my Mac.
| | 05:13 | You could press Ctrl+A if you're on a PC.
| | 05:16 | I'm going to copy it and paste it in here,
and we're going to select the test database.
| | 05:21 | We're going to go ahead and create that
table and insert a thousand values. Go.
| | 05:27 | So that took a little over a second,
and now I'm going to do exactly the same
| | 05:32 | thing, but I'm going to put it in a transaction.
| | 05:33 | I'm going to say BEGIN at the top of it,
| | 05:35 | and then I'm going to scroll down to the
end, and I'm going to put COMMIT at the end.
| | 05:41 | So, it was 1,061 milliseconds
without the transaction, and here we go.
| | 05:47 | It's 41 milliseconds with the transaction.
| | 05:51 | The reason for that is that one write to
the hard disk is much, much faster than
| | 05:56 | a thousand writes to the hard disk.
| | 05:58 | One big write is faster than a thousand
small writes, and maybe this is so much
| | 06:02 | data that it takes a handful of big writes.
| | 06:05 | That's still much, much faster
than a thousand small writes.
| | 06:09 | So, these are the two
major uses for transactions:
| | 06:13 | First is atomicity to have a number
of database transactions as one atomic
| | 06:19 | transaction, to keep the
database consistent and durable,
| | 06:23 | and another is simply speed - performance,
because writing a few times is a whole
| | 06:29 | lot faster than writing a lot of times.
| | 06:32 | Transactions are used to ensure data
integrity and to improve performance.
| | 06:36 | SQLite supports transactions
using BEGIN and COMMIT statements.
| | Collapse this transcript |
|
|
12. Subselects and ViewsUnderstanding subselects| 00:00 | Sometimes, when you're planning a query,
you find yourself wishing that your
| | 00:03 | data were organized differently, or
just wondering how to get a piece of data
| | 00:06 | that seems buried or just
unavailable in the way that you want it.
| | 00:10 | Subselects are often the
answer in these circumstances.
| | 00:14 | Subselects, or subqueries, are
SELECT statements used in place of other
| | 00:19 | expressions and data sources.
| | 00:21 | Subselects are effectively
nested SELECT statements.
| | 00:24 | For example, in this query, the
highlighted SELECT statement is nested within
| | 00:30 | the outer SELECT statement, and the
inner SELECT statement is actually used as a
| | 00:34 | data source for the outer SELECT statement.
| | 00:37 | Subselects may be nested at many levels.
| | 00:40 | In this case, there is a subselect
nested way inside there, and that's a data
| | 00:45 | source for this outer SELECT, and
that in turn is a data source for this
| | 00:51 | outer, outer SELECT.
| | 00:53 | Subselects may be stored as views,
so that they may be easily reused.
| | 00:59 | In this example, this SELECT
statement is being stored as a VIEW
| | 01:02 | called JoinedAlbum.
| | 01:05 | Views may be used
wherever a subselect may be used.
| | 01:09 | In this case, this JoinedAlbum view is
being used as a data source for this query.
| | 01:14 | To see what views have been stored, you
may query the SQLITE_MASTER table, like this.
| | 01:20 | Subselects are a very convenient way of
making your data available in different
| | 01:23 | forms, while keeping your database
schema simple and well organized.
| | Collapse this transcript |
| Creating a simple subselect| 00:00 | To create a subselect, you first
create the inner SELECT statement that gets
| | 00:04 | you the data the way that you want it; then
plug it into your outer query as a subquery.
| | 00:09 | For example, imagine that we're
getting data from an outside data source, and
| | 00:12 | that data is packed into a very compact format.
| | 00:15 | This is a common thing.
| | 00:17 | This will often happen when data has to
be transmitted from one place to another.
| | 00:21 | It's often in a very compact format.
| | 00:23 | In this particular table, what we have
over here under the B column is we have a
| | 00:29 | country code packed in with
some kind of a useful number.
| | 00:33 | We'll just call that a country value.
| | 00:36 | So, if we wanted to just take those two
values out and use that as a data source
| | 00:41 | in another query, we
could do something like this.
| | 00:44 | We'll select the substring of b,
and starting at the beginning for two
| | 00:50 | characters, and we'll call that Country,
and the substring of b, and starting at
| | 00:59 | column 3 and for the rest of
the string as country value.
| | 01:05 | So we'll go ahead and we'll run this
query, and we've created the table there.
| | 01:11 | And we've got this little subquery here
that we're going to use as a data source
| | 01:15 | in our larger query.
| | 01:17 | So I'll get rid of this, and now
we'll build a query around this, and
| | 01:21 | we're going to join it with our
Country table, so that we can display the
| | 01:25 | country names over here.
| | 01:26 | It's a very simple application, but
you can see from this, the technique of
| | 01:31 | building a query to get you the data
the way that you want to get it, and then
| | 01:34 | using that as a data source in your outer query.
| | 01:37 | So I'm going to select, I'm going to
call this subquery as tt, so I'll be
| | 01:44 | selecting from that, and
then I'm also going to join it.
| | 01:48 | So I'm just going to format this the way
that I tend to format subqueries like this,
| | 01:53 | so that it's very obvious to me how I'm using it.
| | 01:57 | Then I'm going to do a JOIN from the
Country table as co, and so now I know my
| | 02:04 | aliases, and that makes it
easy, ON tt.Country = co.Code.
| | 02:12 | And this is actually Code2 in my
country table because these are
| | 02:16 | two-letter abbreviations.
| | 02:19 | What I'm selecting from here, now that
becomes easy because I have all of my aliases.
| | 02:23 | I can say co.Name, that's the name
from the Country table, and tt.CoValue, so
| | 02:29 | that'll be the value
from the tt table, like this.
| | 02:33 | That's from my subquery, which I have aliased tt.
| | 02:38 | So now I actually get something just
like this, except with the country name.
| | 02:42 | There we have country name, United States,
United Kingdom, and France, and CoValue.
| | 02:48 | So, here I took a query, and I got it
just the way that I want it, with exactly
| | 02:53 | the data, in exactly the format that I
needed it for my outer query, and then I
| | 02:57 | use that as a data source, even to create a join.
| | 03:00 | This is a very simple technique for getting
your data available in the form that you need it.
| | 03:05 | So, creating a subselect is simply a matter of
breaking down a problem into two or more parts.
| | 03:10 | First, create your inner SELECT
statement as a separate problem, and then create
| | 03:15 | the outer statement that uses it.
| | Collapse this transcript |
| Searching within a result set| 00:00 | When you have data from multiple
sources in multiple formats and you need
| | 00:03 | to use one data set to search another, you
can use a subselect to perform your search.
| | 00:08 | For example, if I have this data, which
is coming from an outside source, and
| | 00:12 | oftentimes data from outside sources, if
it needs to be transmitted in some way,
| | 00:17 | is often packed like this,
| | 00:19 | and in this B column here I have a
country code followed by some sort of a
| | 00:25 | number that's associated
with that country and code.
| | 00:27 | I'm calling that a country value in this case.
| | 00:30 | So I may want to use that to find out,
what are all the cities in all of the
| | 00:36 | countries within that code.
| | 00:38 | The first thing I'm going to do is I'm
going to create to SELECT that simply
| | 00:41 | gets that code out of that, and
that's easy to do with SUBSTR.
| | 00:45 | So it's B, and it's from the first
position, and its two long, and so if I run
| | 00:52 | this query here, we'll see I
now have just those country codes.
| | 00:56 | And so I'm going to get rid of this part,
and I can use that as an index to search
| | 01:01 | through my local tables.
| | 01:04 | So I'm going to have a SELECT, and I
will be selecting stuff, and I'm going to
| | 01:09 | say, where co.Code2 is IN this data set.
| | 01:18 | And I'll go ahead, and I will format
this like I normally format my subselects,
| | 01:24 | and so my outside query is going
to look something like this, co.Name AS
| | 01:28 | Country, and the city name FROM city.
| | 01:35 | Search my primary query, AS ci, and
joined with country table, AS co ON
| | 01:43 | ci.CountryCode = co.Code, and
where the co.Code2 is in this list.
| | 01:55 | So I'm using this list as the data source,
and I'm searching through my existing
| | 02:00 | tables based on what's in this list.
| | 02:03 | And so this will give me a list of
all the cities in all the countries
| | 02:07 | where those country codes are
included in this list from an outside source,
| | 02:13 | and there's my query.
| | 02:14 | So I have three. You will remember
| | 02:16 | there was United Kingdom, there was
France, and there was the US, and so there is
| | 02:19 | United Kingdom, and there is
France, and there is the US.
| | 02:24 | So subselects are a very convenient
method for searching one data set from
| | 02:28 | another, and this technique is
especially helpful when your data is coming from
| | 02:31 | multiple sources and may be in a packed format.
| | Collapse this transcript |
| Searching within a joined result| 00:00 | A subselect may be just about any
SELECT statement, including a joined query.
| | 00:04 | For example, we have a query here
which is joining the track table and the
| | 00:10 | album table and coming up with this result,
which shows us artist and album and track.
| | 00:14 | And you'll notice that the duration
here is a number in seconds, and that's not
| | 00:19 | really very readable for your average person.
| | 00:21 | They want to see it in minutes and seconds,
all nicely formatted with a colon in between.
| | 00:25 | And we're going to use a
subselect in order to do that.
| | 00:29 | And so from this query here, the first
thing we are going to do is we are going
| | 00:32 | to separate out the minutes
and seconds from this duration.
| | 00:35 | So I'm going to put it over on this
line by itself here, and I am going to say
| | 00:39 | duration divided by 60 AS m, for minutes.
| | 00:43 | And so that's an integer division, and
it will leave off the remainder rather
| | 00:47 | than turning it into a fraction.
| | 00:50 | And then we're going to take
t.duration again, and we are going to use the
| | 00:53 | modular operator, which gives us the remainder
of that division, and we'll call that seconds.
| | 00:59 | So now we have a same query, but over
here where it says duration, instead of
| | 01:03 | that, we are going to have minutes and seconds.
| | 01:05 | And there we have minutes and seconds, so
that's already getting to be more readable.
| | 01:09 | Well we would like to format that just
right, and in order to that, we're going
| | 01:13 | to take this query, and we're
going to turn it into a subselect.
| | 01:18 | So I'm going to go ahead and format this
differently, and I am going to say FROM
| | 01:23 | because we are going to
use this as a data source.
| | 01:29 | And we'll go ahead and indent this, as I
like to do to keep things nice and clear.
| | 01:36 | And we'll have a select, and one of the
things that is going to be in the select is
| | 01:39 | we are going to format these minutes
and seconds the way that we want them with
| | 01:42 | a colon in between and a leading zero
on the seconds and things like that.
| | 01:46 | So I'm going to say m
concatenated with a colon, and that's the SQL
| | 01:53 | concatenation operator like that.
| | 01:55 | And then for the seconds, if the seconds
are less than 10, I want a leading zero,
| | 01:59 | so I am going to need to use CASE
statement and so CASE in SQL is like if, then,
| | 02:05 | else in other languages, so I am going
to say CASE when S is less than 10, then
| | 02:11 | we are going to use a leading zero,
| | 02:14 | I have to have then in here, and that
will be concatenated with the value of the
| | 02:18 | seconds, else the seconds by itself.
| | 02:23 | And we'll call that whole thing
duration. And I'll also want, in my select, I
| | 02:28 | want artist and album
and track and track number.
| | 02:36 | And so this trackno is the alias
from over here in our subselect, and artist
| | 02:41 | is this one from our subselect, and album
is this one here, and track is this one here.
| | 02:47 | So we have all these nicely aliases as
in our subselect, and we can use those
| | 02:51 | in our outer query.
| | 02:53 | Then finally, I'm going to do in ORDER
BY, and we'll make it artist and album
| | 03:00 | and track number.
| | 03:05 | And then we'll go ahead and run the
query, and it looks like I've got a syntax
| | 03:09 | error here some place.
Yup, I forgot a comma right there.
| | 03:14 | We go ahead and run the query.
| | 03:15 | And here we have exactly the same results.
| | 03:18 | They're ordered by artist,
album, and track number.
| | 03:21 | And over here in the fourth column, we
have our duration, formatted exactly how we
| | 03:25 | want it, with the minutes, and the
seconds you have there nicely leading zero on
| | 03:29 | the seconds that are less than 10.
| | 03:31 | We have the colon and everything.
And this is all accomplished because we
| | 03:35 | formatted our data in a way that
it works for us, in the subselect.
| | 03:40 | And so we have the minutes in the
seconds broken out, and that makes it easy for
| | 03:44 | us to use the concatenation operator to
build that string exactly how we want it.
| | 03:50 | So subselects may be, and
often are the complex queries.
| | 03:53 | Feel free to construct your
subselects in any way that makes sense for
| | 03:57 | your purpose.
| | Collapse this transcript |
| Creating a view| 00:00 | Anytime you want to reuse a
query, you can create a view.
| | 00:03 | A view is simply a saved query
that may be used in place of a data source.
| | 00:07 | For example, and we'll be
doing this in the world database,
| | 00:12 | here we have a table that we have been
using in some of these examples in this chapter.
| | 00:16 | And this table has packed a data in
it, and it may come from another data
| | 00:19 | source, where it's common to pack data.
| | 00:22 | And we have a SELECT statement that
has a number of substrings for extracting
| | 00:27 | the data points out of that data.
| | 00:29 | So if I select GO here, we see that we
get this State, State value, the Country,
| | 00:34 | and the Country Value from that table.
| | 00:37 | If I want to save this query as a view, I
simply say CREATE VIEW and give the view a name,
| | 00:45 | and the keyword AS, and then I like to
indent here, but that's certainly not required.
| | 00:55 | And that will create a view named
unpackData that will perform this query.
| | 01:00 | So I'll go ahead and execute that,
and now I can simply say SELECT * FROM
| | 01:08 | unpackData, and I get that query.
| | 01:14 | Now this data source can be used
anywhere that you can use a data source.
| | 01:18 | In fact, I can do something like this,
and use it as part of a join, if I want to.
| | 01:24 |
| | 01:40 | And so now I'll be looking at this data.
| | 01:42 | I'll be looking at the Country name
from the Country table, and the Country
| | 01:47 | Value from the unpackData data source.
| | 01:52 | So this will be joined with the Country
table, and so this query works just as if
| | 01:56 | this were any data source, a table, or a
subselect, or anything, and it's just as
| | 02:04 | named query, stored as a view.
| | 02:07 | So views are easy to create and easy to use.
| | 02:10 | You may use a view wherever
you would like to use a query.
| | 02:13 | Views are especially
useful in place of subqueries.
| | Collapse this transcript |
| Searching within a joined view| 00:00 | There are many uses for views.
| | 00:01 | One common use is to hide the
complexity of a complicated subselect.
| | 00:06 | For example, here we have a select that
breaks out the minutes and seconds from
| | 00:13 | a duration field that was originally
entered in seconds, and here we have the
| | 00:18 | result of it, and you can see that it
separated minutes and seconds, and they're
| | 00:22 | separated by this division
and a corresponding modulus.
| | 00:26 | So if we'd like, we can
create a view based on this.
| | 00:30 | So we just CREATE VIEW, call it
JoinedAlbum, and I'd like to indent this.
| | 00:41 | It's not required. And now we're
creating a view with this query in it, and now
| | 00:46 | we can use that view
instead of that complicated query.
| | 00:50 | We can simply say SELECT artist, album,
track, and trackno, and then this now
| | 01:01 | familiar making the minutes and
seconds out of these separated values.
| | 01:06 |
| | 01:21 | So that takes the minutes, and it
concatenates a colon, and then it
| | 01:26 | concatenates the seconds with or
without a leading 0, depending on whether the
| | 01:31 | seconds is less than 10.
| | 01:33 | Then we simply say FROM JoinedAlbum, and
all the complexity of that is hidden and
| | 01:40 | can be looked up if you want to.
| | 01:43 | Here's our query, with the duration all
combined as the minutes and seconds, like
| | 01:48 | that, with the colon.
| | 01:50 | If you want to see, if you forget, or
you're looking at this after the fact, and
| | 01:54 | you see that JoinedAlbum, you
can SELECT from SQLITE_MASTER.
| | 02:03 | I need a FROM in here.
| | 02:04 | So, you can SELECT from SQLITE_MASTER,
and you can see to CREATE VIEW statements
| | 02:12 | and so you can see what
that JoinedAlbum represents.
| | 02:15 | So views are a useful tool in
designing and using relational databases.
| | 02:19 | When you have a complex subselect that
you'll be using regularly, a view is a
| | 02:24 | good way to hide that complexity and
make your code clear and understandable.
| | Collapse this transcript |
|
|
13. TriggersUnderstanding triggers in SQLite| 00:00 | Triggers are operations that are
automatically performed when a specific
| | 00:03 | database event occurs.
| | 00:06 | Common uses of triggers include:
preventing changes to a record after it's been
| | 00:10 | reconciled, logging and auditing
changes to a table, enforcing date and time
| | 00:16 | stamps, enforcing business rules, and
enhancing performance by maintaining
| | 00:22 | derived tables in a
format used for common queries.
| | 00:25 | SQLite supports row-level triggers.
| | 00:27 | These are triggers that are
performed for each affected row.
| | 00:31 | SQLite does not support
statement-level triggers.
| | 00:35 | Triggers can be notoriously difficult to debug.
| | 00:39 | Effects of triggers almost
always look like side effects,
| | 00:43 | so documentation, logging, and
careful design will go a long way toward
| | 00:47 | preventing problems when
debugging a database with triggers.
| | Collapse this transcript |
| Automatically updating a table with a trigger| 00:00 | A common use for triggers is to force
a table to be updated whenever a row is
| | 00:04 | inserted or updated in another table,
and this is usually done to enforce
| | 00:08 | some business rules.
| | 00:09 | For example, in this example we'll
be updating the customer table with a
| | 00:14 | last_order_id whenever a sale is made,
whenever a new record is inserted in the sale table.
| | 00:21 | So we're going to start with creating
the tables and creating some customers.
| | 00:26 | I'm just going to copy and paste this into SID.
| | 00:31 | We'll run in, and we can see that we
have this customer table that has three
| | 00:35 | customers in it, and the last_order
_id field is NULL in all of those.
| | 00:40 | We've also created a sale table.
Before we insert some sales though, we want
| | 00:44 | to create a trigger.
| | 00:45 | So the way this works is we use the
CREATE TRIGGER statement, and we name the
| | 00:50 | trigger newsale, and the trigger
will happen AFTER INSERT ON sale.
| | 00:55 | So first, the record will be inserted
into sale, and then we will perform the
| | 01:00 | statements in the trigger.
| | 01:01 | So those statements in the trigger are
bracketed by BEGIN and END, and there is
| | 01:05 | a semicolon at the end of END.
| | 01:07 | There's also a semicolon at the end of
each of the statements inside the trigger.
| | 01:13 | So there can be more than one statement
in here; for the purposes of this example,
| | 01:16 | there's just the one.
| | 01:18 | So this statement will update the
customer table, and it will set the
| | 01:22 | last_order_id column to NEW.id, and I
will get back to that in a moment, where
| | 01:29 | customer.id = NEW.customer_id.
| | 01:32 | So NEW is a pseudo-record.
| | 01:36 | It's a virtual table that contains the
values from the event that triggered the trigger.
| | 01:43 | In other words, we've
inserted something on sale.
| | 01:47 | So that inserted row in sale is
available as NEW in this virtual table.
| | 01:53 | So the id field from sale
will be this INTEGER PRIMARY KEY.
| | 01:58 | So the id field will be the id of the
newsale, and so that will be assigned to
| | 02:03 | last_order_id, which is exactly
what we want to have happen.
| | 02:07 | Customer_id from the newsale row will be
the customer.id that we want to update.
| | 02:12 | So, we're saying where customer.id
= NEW.customer_id. And when we're all
| | 02:17 | done, we're going to go ahead and
take a look at this trigger in the
| | 02:21 | sqlite_master table,
| | 02:22 | so you can see what that looks like.
| | 02:23 | So I'm going to go ahead
and create the trigger here.
| | 02:25 | I am just going to paste
this in after all of this.
| | 02:28 | So we have to run this every time,
because we're using the in-memory database.
| | 02:32 | So each time you run it, it
all starts with a fresh database.
| | 02:35 | So I am going to insert that trigger there.
| | 02:37 | I'll just make a little space here,
| | 02:38 | so that is easy to see, and so that'll
create the table, and then we're going to
| | 02:42 | look at sqlite_master.
| | 02:44 | We still will be displaying this,
because that was already there.
| | 02:46 | We're just going to be
accumulating results down here.
| | 02:49 | So sqlite_master now has the two tables that
we've created, and it also has this trigger.
| | 02:54 | So triggers appear in sqlite_master,
and this is very important to understand.
| | 03:00 | Triggers are a source of a lot of debugging
headaches, because they create side effects.
| | 03:05 | The things that happened as a result of
a trigger always look like a side effect.
| | 03:08 | So you might be looking at your
database and going, "Oh my God, how come that
| | 03:12 | table is getting updated," when you
don't remember, or you didn't create this
| | 03:16 | problem in the first place, and you're
not familiar with the database schema.
| | 03:20 | So looking at sqlite_master is a great
thing to do, because you can see what
| | 03:24 | your triggers are, and there's the
whole trigger definition right there.
| | 03:27 | So you can find out exactly what it looks like,
and exactly what's going on in your database.
| | 03:33 | So after we've created this trigger,
let's go ahead and insert some sales records.
| | 03:38 | So we're going to insert three sales,
and then we'll take a look at the sale
| | 03:42 | table, and we'll look at the
customer table again, and we'll see what has
| | 03:46 | happened to the customer table.
| | 03:47 | So I'm going to paste
this in, and we'll click Go.
| | 03:51 | Now we have the sale
table, which has the item_id.
| | 03:55 | We don't have an item table yet, but it's
got a item_id, customer_id, quantity, price.
| | 04:01 | Look at our customer table.
| | 04:02 | Now it has these last_order_id.
| | 04:05 | So the first order, which is id 1, was
given to customer number 3, and customer
| | 04:09 | number 3 is Fred, and you
can see he's got order_id 1.
| | 04:12 | The second one is 2, went to
customer number 2, and so we have that there.
| | 04:17 | And the third one was 3, and it went to
customer number 1, and so we have that there.
| | 04:22 | So you can see that our customer table
is getting updated based on the trigger.
| | 04:28 | So this is getting executed every time
we insert something in the sale table.
| | 04:32 | Finally, just so that you know, when you're
done with the trigger, obviously in the
| | 04:36 | in-memory database it's not going to
matter much, but you can say DROP TRIGGER
| | 04:43 | and name it, and then I'll just take a
look again at the SQLITE_MASTER table, so
| | 04:51 | that we can see that the trigger
has been dropped. I forgot the FROM.
| | 05:04 | There you can see that the trigger is
gone, because we executed the DROP TRIGGER.
| | 05:10 | A trigger can be an excellent way to
enforce business rules that require a table
| | 05:14 | to be updated whenever another table is updated.
| | Collapse this transcript |
| Logging transactions with triggers| 00:00 | Using triggers to log transactions
can be a very useful tool for auditing
| | 00:04 | purposes, and for debugging.
| | 00:06 | For this example, we're going to create
three tables: a customer table, a sale
| | 00:10 | table, and a triggerlog table.
| | 00:12 | We'll start by populating the customer
table and selecting from it to show that
| | 00:18 | we're successfully creating these
tables and populating the customer table.
| | 00:22 | So we'll go ahead and paste that into SID.
| | 00:24 | We're using the in-memory database,
| | 00:26 | so each time we run this example
we'll have to create all these tables and
| | 00:31 | populate them anyway.
| | 00:32 | So there we see that we're
successfully populating the customer table,
| | 00:36 | it has these three records in it, and last
_order_id is NULL for all three of them.
| | 00:41 | So now we'll create the trigger.
| | 00:42 | We start with the CREATE TRIGGER
statement, and we name the trigger newsale, and
| | 00:47 | this trigger will happen
after insert, on the sale table.
| | 00:50 | The body of the trigger has an UPDATE
statement that updates the customer table
| | 00:55 | and sets the last_order_id to the
NEW.id, and NEW, of course, is the virtual table
| | 01:01 | created by the trigger that's in effect
while the trigger is being run that has
| | 01:06 | the data from the row
that's being inserted into sale.
| | 01:10 | Then we have the triggerlog.
| | 01:12 | Here we insert a row into the
triggerlog table, and we set the timestamp, the
| | 01:18 | events name, which in this case is
UPDATE last_order_id - that's why we're naming
| | 01:22 | the event that's being triggered -
and the trigger name itself, which is
| | 01:26 | newsale, the name of the table that's being
updated, which is customer, and the NEW.customer_id.
| | 01:33 | So we'll take this trigger
definition, and we'll also inserts some rows
| | 01:38 | into sale, and we'll select from all three
of these tables so we can see what happened.
| | 01:43 | I'll copy that and paste it into
SID here and run it, and now we see we
| | 01:50 | inserted these rows into the sale table,
and that automatically updated the
| | 01:55 | customer table, and here's our log.
| | 01:58 | So this can be very useful, not just
for auditing purposes, but of course the
| | 02:02 | suits will love it because they have
an audit trail that says exactly what
| | 02:06 | happened in the database records,
| | 02:09 | but also for debugging purposes for
later on after you've designed a million
| | 02:14 | other database and don't remember what
happened here, or for somebody else who
| | 02:17 | comes along to maintain this, you have
a log of your trigger events, which will
| | 02:22 | help you know what was certainly not
a side effect and to be able to follow
| | 02:27 | the path of execution and find out what's
going on when you need to debug all of this.
| | 02:32 | So triggers are a great way to ensure
that transactions are logged properly for
| | 02:36 | auditing purposes, and these logs can
also be a very useful tool for debugging.
| | Collapse this transcript |
| Improving performance with triggers| 00:00 | For a busy database it can be
useful to set aside some tables for
| | 00:03 | reporting purposes only.
| | 00:05 | These tables can have data that is
accessed frequently, in a format useful
| | 00:09 | for your users, and that will direct heavy
traffic away from the more critical tables.
| | 00:14 | Triggers are an excellent way to
keep these reporting tables updated.
| | 00:18 | For our example, I am going to
create a number of tables here:
| | 00:22 | a customer table, a sale table item,
an item table, and a report table.
| | 00:28 | We'll populate the customer table, we'll
populate the item table, and then we'll
| | 00:33 | take a look at those.
So, we'll get started by doing that.
| | 00:35 | There is our customer table, with
last_order_id as NULL, and there is our item table.
| | 00:45 | Now that we have that,
we're going to create a trigger.
| | 00:48 | Now, this trigger is a little bit more
busy than some of the other triggers that
| | 00:52 | we have gone before.
| | 00:54 | We'll call it newsale, and
it's AFTER INSERT ON sale.
| | 00:57 | The first statement will update the
customer table and set the last_order_id.
| | 01:02 | The second statement will insert into a
report using a joined query as a data source.
| | 01:07 | Then we'll go ahead and insert some
records into sale, and we'll select from
| | 01:12 | report to see what we get there.
| | 01:14 | So, we'll copy and paste.
| | 01:18 | So there is our trigger, and
there is our sales, and there we go.
| | 01:24 | Now in our report table we have item, we
have the customer, the quantity, and the price.
| | 01:31 | So the people in the sales department, or
in whatever department need these reports,
| | 01:37 | they have their own table, and they
can query that as often as they want.
| | 01:40 | They can query it in
different ways that they want,
| | 01:42 | they can make it busy,
| | 01:43 | they can make it slow, and it's not
necessarily going to impact the performance
| | 01:48 | of the tables that get updated frequently.
| | 01:51 | So using triggers, you can keep a
separate set of reporting tables updated to
| | 01:54 | improve performance, both for
reporting and performance-critical tables that
| | 01:58 | otherwise would be slowed
down by reporting traffic.
| | Collapse this transcript |
| Preventing unintended updates with triggers| 00:00 | Triggers are often used to prevent
changes to rows that have already been
| | 00:03 | reconciled, or should not be
changed for other reasons.
| | 00:05 | For example, we have here our customer
table and sale table, and you'll notice
| | 00:10 | this time the sale table has this extra
reconciled column, and that's an integer,
| | 00:15 | as SQLite uses the
integer type for Boolean values.
| | 00:19 | So it'll be a 1 or 0, representing true or false.
| | 00:22 | So we'll go ahead, and we'll define
these tables, and we'll populate them, and
| | 00:25 | we'll display the sale
table, so we can follow it.
| | 00:31 | So here's our sale table.
| | 00:32 | You'll notice the quantities are
set at 5, 3, and 1 for ids 1, 2, and 3.
| | 00:37 | Now, we're going to go ahead and
define a trigger, and you'll notice that the
| | 00:42 | CREATE TRIGGER statement this time
says BEFORE UPDATE. In some of our other
| | 00:46 | examples that we've been doing after insert,
| | 00:48 | but in this case we want to trigger the
trigger before the update actually happens.
| | 00:54 | So this'll happen when you try to do an
update, like these updates down here, and
| | 00:59 | before the update is actually executed,
this trigger will be triggered. So in
| | 01:04 | this trigger we're selecting the RAISE
function, which raises an exception, and in
| | 01:08 | this case we're using the ROLLBACK
exception so that the change will actually
| | 01:13 | not occur, and we're returning a
message along with the exception.
| | 01:18 | The message says, 'cannot update table "sale."'
| | 01:21 | We're doing this select from the table
sale so that we can test the reconciled flag.
| | 01:26 | So our select is WHERE id = NEW.id,
| | 01:30 | so we're looking for the ID of the row
that's going to be updated, and we check
| | 01:35 | for the reconciled flag to be true.
| | 01:37 | So if both of those conditions are true,
if that reconciled flag is set to true
| | 01:43 | for the row that's being updated,
then we'll raise the ROLLBACK exception.
| | 01:47 | So let's go ahead and add
some updates to our script.
| | 01:50 | We're going to update all three of these
rows, and we're going to set all of the
| | 01:53 | quantities to 9, just so that
we can see what's going on.
| | 01:56 | So we'll go ahead and add these updates.
| | 02:01 | Why don't we put the trigger
in there also? That might help.
| | 02:04 | Put that trigger in there.
| | 02:06 | So we've got our trigger defined,
and we're going to update our rows.
| | 02:10 | Now, you'll notice that we get this
error for query number 12, and that's the
| | 02:15 | update for id 2, and it
says cannot update table sale.
| | 02:19 | That's our message.
| | 02:20 | So our trigger got triggered and our
exception got thrown just for that row.
| | 02:25 | So there are the three updates, setting
quantity to 9 for all three of them, and here we have,
| | 02:30 | in our results, we have quantity as 9
for the first row and for the third row,
| | 02:34 | but the second row is unchanged, and
that's because our trigger prevented it.
| | 02:40 | So triggers are often used to prevent changes
to tables or rows that should not be changed.
| | 02:46 | It's a simple usage, and
it's a valuable business tool.
| | Collapse this transcript |
| Adding automatic time stamps| 00:00 | Triggers may be used to create
timestamps in the table you're triggering from, as
| | 00:04 | well as in other tables.
| | 00:06 | We'll start by creating some tables
here, and inserting some customers in
| | 00:10 | the customer table,
| | 00:11 | so we have a customer table, a sale table,
and a log table that we're creating.
| | 00:16 | Then we'll select from the
customer table to see that that worked.
| | 00:19 | We're using the in-memory database here,
| | 00:21 | so we need to run this CREATE TABLE
statements each time we run the queries,
| | 00:27 | because this database goes away every
time we use it. And we'll go ahead and run
| | 00:31 | that query, and we see that the
customer table is populated with three records:
| | 00:36 | Bob, Sally, and Fred.
| | 00:37 | And so now we'll create our trigger.
| | 00:39 | Now this trigger is going to
insert time and date stamps.
| | 00:44 | We'll call this trigger newsale,
and it's AFTER INSERT ON: sale.
| | 00:47 | The first statement updates the sale
table itself and sets the timestamp.
| | 00:53 | The second one updates the customer
table, and in addition to setting the last
| | 00:58 | order id, it also sets a timestamp.
| | 01:02 | The third one logs the event, and
you'll notice that as we insert into the sale
| | 01:06 | table, we're using a transaction here.
| | 01:08 | We have BEGIN and COMMIT, and the reason
for this is we're actually updating the
| | 01:13 | row in the sale table right after we insert it.
| | 01:17 | This can create some disk churning.
| | 01:19 | So by putting this inside of a transaction,
we will minimize the number of writes to disk.
| | 01:25 | So this could otherwise be a bit of
an expensive way to do this, but by
| | 01:28 | putting it inside of a transaction, the
performance should be fine for most circumstances.
| | 01:34 | Then we will go ahead and select from all
these tables, so we can see what happened.
| | 01:38 | So will create the trigger here and execute it.
| | 01:44 | So we have our three sales, and you'll
notice that they have the timestamps in
| | 01:48 | them. And the customer table now has
these timestamp saying that was updated at
| | 01:53 | this time and date, with the last_order_id.
| | 01:57 | You'll notice, also, we've our logs,
and this has the same timestamps.
| | 02:00 | So we've exactly the same
timestamp for all of these events.
| | 02:04 | So you can use triggers to create
timestamps in the table you're inserting or
| | 02:09 | updating, and in other tables too.
| | Collapse this transcript |
|
|
14. PHP InterfacesChoosing an interface| 00:01 | There are two interfaces
available for using SQLite with PHP 5.
| | 00:06 | The PDO interface, PHP Data Objects is
a normalized interface that works across
| | 00:12 | platforms, with many different database engines.
| | 00:16 | The SQLite 3 interface is a native
interface that mirrors SQLite's C
| | 00:21 | language interface.
| | 00:23 | So let's talk first about PDO.
| | 00:26 | PDO is a normalized interface that
works mostly the same for a number of
| | 00:30 | different database engines.
| | 00:32 | PDO is constantly being
improved, tested, and debugged.
| | 00:36 | It has a rich set of methods for
handling all functions of SQLite, as well as
| | 00:41 | some that are not supported by the
SQLite 3 driver-specific interface.
| | 00:46 | PDO's performance is comparable, and
even better in some areas, compared to the
| | 00:50 | driver-specific SQLite 3 interface.
| | 00:53 | The SQLite 3 driver-specific interface
is designed to work much like the native
| | 00:58 | C language interface for SQLite 3.
| | 01:01 | It is not a complete implementation of
the C language interface, and it does
| | 01:06 | not fully support exceptions for error handling.
| | 01:09 | The PDO interface is clearly the
superior interface for new code.
| | 01:13 | Use SQLite 3 interface where required,
or where it would involve too much work
| | 01:18 | to convert existing code.
| | Collapse this transcript |
| Using the SQLite3 interface| 00:00 | The SQLite 3 interface is the native driver
interface for the SQLite 3 database engine.
| | 00:06 | This a small test script, using
the SQLite 3 interface with PHP.
| | 00:12 | This is using my little sandbox
framework. Down here in the main function,
| | 00:16 | we have everything inside of a try and
catch block, so that we can catch any errors.
| | 00:22 | It's just worth noting that some errors
using this interface don't get caught.
| | 00:28 | So we instantiate the database like
this: new SQLite 3 database, and the
| | 00:33 | database constant here is defined up
here with the path to the database, and
| | 00:39 | make sure you put in your own path here
instead of using mine, because it will not work.
| | 00:45 | Then we use the dollar db object
that gets created by the new call.
| | 00:50 | That $db object is now the SQLite 3 interface.
| | 00:54 | The exact method is used for executing
SQL that doesn't require a result like
| | 01:00 | this DROP TABLE IF EXISTS, CREATE TABLE.
| | 01:03 | Then we'll put out a little message
that we successfully created the table, and
| | 01:07 | then we insert some values.
| | 01:09 | Now, we use the Prepare to prepare a
statement, and we use these placeholders,
| | 01:14 | and all the different kinds of
placeholders are available, and then we use the
| | 01:17 | bindValue function on this statement
handle. The prepare returns a statement
| | 01:21 | handle, and the statement
handle is now an object.
| | 01:25 | We use the bindValue method, and you
have to bind each value individually with
| | 01:29 | a separate call, and then execute,
and then bindValue, and execute and
| | 01:33 | bindValue, and execute.
| | 01:34 | So we are using this one statement
with three different sets of parameters.
| | 01:38 | So we're executing it three times, and
the first one we're putting in a, b, c
| | 01:43 | characters, and then second time we're
putting in 1, 2, and 3 numbers, and the
| | 01:47 | third time we're putting in
one, two, and three strings.
| | 01:51 | We call execute for each of those.
| | 01:53 | Then we prepare a SELECT statement to
simply select all of the columns in the
| | 01:59 | table, and then we call execute, which
prepares a result object, and then we need
| | 02:05 | a While loop because result is not
an iterator, to go through and call
| | 02:09 | fetchArray for each row.
| | 02:12 | So we run this, and it looks like that:
| | 02:16 | Table t successfully created, and there
is our result rows in the message call
| | 02:21 | here for each of the rows in the While loop.
| | 02:24 | So in a nutshell, that's how
the SQLite 3 interface works.
| | 02:29 | The SQLite 3 interface is
a rudimentary interface.
| | 02:32 | It doesn't have the feature set of the
PDO interface, but it does closely match
| | 02:37 | the native C interface for the SQLite 3 library.
| | Collapse this transcript |
| Using the PDO interface| 00:00 | The PDO interface is a modern cross-
platform interface, designed to make your job
| | 00:05 | easier as you write your database applications.
| | 00:08 | This is a little test script written with
the PDO interface, using my sandbox framework.
| | 00:15 | Up here at the top, you'll see we
defined the database constant, and you'll want
| | 00:20 | to make sure that you put your path
in here, because using my path on your
| | 00:23 | computer will not work.
| | 00:25 | Down here, we have the main function, and
the first thing we do is instantiate the PDO
| | 00:32 | class into an object called $db, and all
of this is inside of a try block so that
| | 00:37 | we can catch exceptions.
| | 00:39 | You'll notice that the
very first line after that
| | 00:42 | setAttribute(PDO::ATTR_
ERRMODE, PDO::ERRMODE_EXCEPTION);
| | 00:47 | and what this does is it turns on
exception handling for the entire PDO
| | 00:51 | library. And it's pretty thorough. It works
about 99% of the time, and occasionally
| | 00:56 | there are errors that do not get caught.
| | 00:58 | Then we use the exec method to DROP
TABLE IF EXISTS and create the table, and
| | 01:04 | then we'll send out a message that
the table is created successfully.
| | 01:08 | Then here comes the fun part.
| | 01:09 | The prepare statement will prepare a
query that can be used over and over, and
| | 01:15 | here we use the question mark placeholders.
| | 01:17 | Other types of
placeholders are supported as well.
| | 01:20 | That returns a statement handle, which is
an object that has this execute method.
| | 01:25 | The execute method will take an array
with all of these positional parameters.
| | 01:30 | I tend to use the question mark parameters;
| | 01:32 | you can use named parameters, and in some
cases that's really handy where you are
| | 01:36 | going to have one value
that's used in more than one place.
| | 01:39 | So we execute that three different
times, with three different sets of
| | 01:43 | parameters. We have the characters a, b,
and c, we have the numbers 1, 2, and 3,
| | 01:48 | and we have the strings 1, 2, and 3.
| | 01:50 | Then we go ahead and select.
| | 01:52 | So, here we prepare another statement,
and this one doesn't have any parameters.
| | 01:56 | It's just SELECT * FROM t. We set the
fetch mode to be an associative array.
| | 02:03 | Then we run execute.
| | 02:05 | After we run execute, that
statement handle becomes an iterator.
| | 02:09 | So we can just save foreach statement
handle as row and get all of the rows.
| | 02:15 | So you can see there is a lot of
thought put into this library to make your job
| | 02:18 | easier, and many of these things you
can even expose as you create your own
| | 02:23 | library, and we'll talk about that in
a movie on creating your own library.
| | 02:27 | So there is our script, and let's
go ahead and run it, and there it is.
| | 02:33 | Table t successfully created, and
there are the rows, and those rows, again,
| | 02:37 | using this iterator,
| | 02:39 | it's very easy to step through those
without having to read an entire table into
| | 02:43 | an array, which is not going
to work a lot of the time.
| | 02:46 | So the PDO interface is
rich and well-implemented.
| | 02:50 | It's cross-platform.
| | 02:51 | It's mature, modern, object-oriented
implementation with many useful features,
| | 02:56 | including iterators for
stepping through results.
| | Collapse this transcript |
| Creating a library| 00:00 | I strongly recommend that you create
your own normalized database interface to
| | 00:04 | make your job easier as a programmer.
| | 00:06 | The PDO interface is mature, and
well-written, but it's still not tailored to the
| | 00:10 | way you work, or to your applications.
| | 00:13 | Having your own interface will
increase your productivity, and it will also
| | 00:16 | serve as a starting point for those
times when you need a library for a
| | 00:19 | specific application.
| | 00:21 | What we are looking at here is the
test program for my interface that I call
| | 00:27 | bwSQLite 3, and here you can see
where it's included in this file.
| | 00:31 | I want to start with this test script.
Because this is where I start when I
| | 00:35 | write these database interfaces,
because I've written them in several
| | 00:38 | different languages and at several
different parts of my career, and sometimes
| | 00:42 | even in a language where I already have
one, I'll just throw it away and write
| | 00:45 | a new one, because the way that I work changes,
or just because I've evolved as a programmer.
| | 00:51 | So this is one that I
actually wrote this year in PHP.
| | 00:54 | I had done this before in PHP a number
of years ago, and then when PHP went from
| | 00:58 | number four to number five, I
just translated it a little bit.
| | 01:03 | But I decided to sit down and do it
again and to make it work the way that I
| | 01:06 | work these days, which is much more
object-oriented, and in my mind much more
| | 01:10 | evolved as a programmer.
| | 01:12 | So I start with the test script,
because this tells me how I wanted to work, and
| | 01:17 | then I build the library to fit the test cases.
| | 01:19 | For example, when I instantiate the
object, all I want to give it is a file
| | 01:23 | name in a table, and I want both of
those parameters to be optional, so that I
| | 01:27 | can use setter getters to set them
later, and I will show you that in the
| | 01:30 | library when we get to it.
| | 01:31 | I gave it a file name obviously, because
SQLite 3 uses a file name as its database.
| | 01:37 | I gave it a table, because I do have
some simple CRUD operations that are in
| | 01:42 | the library, that allow me to use
associative arrays as records and pass those
| | 01:46 | back and forth to the CRUD, and have it insert
records and delete records, and do things like that.
| | 01:51 | So I need a table name to be able to do that.
| | 01:53 | Here I have a simple version method
for getting the version, and I also if, I
| | 01:57 | pass it the sqlite3 string, it
will give me the version of sqlite3.
| | 02:02 | Then we get into the SQL stuff.
| | 02:04 | You notice I have sql_do, which
is sort of a workhorse function.
| | 02:08 | It's for executing SQL statements that
don't require a return value, and so in
| | 02:14 | this case, I drop table if exists, I
begin transaction, and then I use sql_do
| | 02:20 | again to create the table
and insert some records.
| | 02:23 | You'll notice that I've combined the
functions of the PDO prepare and execute.
| | 02:29 | So I can just pass it the SQL with
the placeholders, and then pass it the
| | 02:33 | replacement parameters and
do that all in one statement.
| | 02:37 | So this makes my job easier as a programmer.
| | 02:39 | This is the kind of thing
that I am talking about.
| | 02:41 | Then I commit and I close the exception.
| | 02:44 | I have a timer_start function, because
I knew I was going to use that a lot in
| | 02:48 | benchmarking and in
testing my code as I write it.
| | 02:53 | Then I test the table_exists function.
| | 02:56 | I know that a lot of times when I am
writing code, I just want to check if a
| | 02:59 | table exists without having to
do a create table if not exists.
| | 03:05 | Here I am starting the timer again,
because I want to test the speed of this
| | 03:09 | query. And it's just a little select
from the table, but you'll notice that the
| | 03:14 | entire query is the iterator. And this
is easy to implement, because I am using
| | 03:19 | PDO, and I am just
returning the iterator from PDO.
| | 03:22 | But again, this is something that I can
do all in one statement, and I can even
| | 03:26 | put parameters in this, if I want to.
| | 03:28 | It just makes my job easier.
| | 03:30 | Really, that's the entire point of all
of the choices that I made is I just want
| | 03:35 | it to work in a way that makes my job easier.
| | 03:37 | I have sql_query_row, which returns a
single row for queries where I know that I
| | 03:42 | am just getting one row back.
| | 03:44 | I have sql_query_value for queries
where I know where I am just getting one
| | 03:47 | value back, and that returns a
scalar value that's not an array at all.
| | 03:52 | Now we get into some of the CRUD methods.
| | 03:55 | Get_rec and I give it a record number.
| | 03:58 | All of my CRUD methods depend on by
having that integer primary key as the first
| | 04:03 | column in the table named id.
And so I say get_rec, and I am getting one
| | 04:09 | record, and I am using it
as an associative array.
| | 04:13 | Get_recs with an s returns an
iterator so I can use it in a foreach.
| | 04:19 | Insert, I just pass it an
associative array, and here I am creating one
| | 04:23 | on the fly with the array function.
| | 04:26 | Here I am using get_recs in a
different way. Instead of using get_recs as the
| | 04:30 | iterator, I use it to set
a global iterator. It's just not an option.
| | 04:34 | This is one that I had used in
some legacy code if I was writing this
| | 04:38 | completely from scratch, and not
requiring it to be backwards compatible, I
| | 04:42 | might not have done it this way.
| | 04:43 | But that sth is a statement handle object,
and it's just a function that returns
| | 04:49 | a statement handle rather than doing
it directly from the get_recs function.
| | 04:53 | Update will update an existing record,
and I give it the record number here
| | 04:57 | and the record as an array there, and
then I use get_rec again, and delete
| | 05:03 | works the same way.
| | 05:04 | Again, this is a CRUD function,
and it just takes a record ID.
| | 05:08 | So that's the test case, and here is the library.
| | 05:12 | Let's go ahead and take a
look at the test case running.
| | 05:16 | I will go ahead and run test.
| | 05:18 | There we see, again, it's using my
sandbox framework, and it just gives me all
| | 05:23 | of these results, so I can look at them
and make sure that they're all working.
| | 05:27 | If I was running a unit test, I might
have used some sort of assertions to just
| | 05:31 | give me messages that they succeeded or failed.
| | 05:34 | But in this case, I really want to do
experiment with it while I was building it.
| | 05:38 | So, that was the purpose of this test script.
| | 05:41 | So, here is the library itself.
| | 05:43 | It creates a class called bwSQLite 3,
and everything else is just methods within
| | 05:49 | that class, or they are all public function.
| | 05:51 | The constructor is very simple.
It defaults using an in-memory database, and
| | 05:56 | defaults to a NULL table name, and
it sets up the PDO database object.
| | 06:03 | You'll notice that it uses an alternate
method of setting this error mode, using
| | 06:08 | it as a fourth parameter to the PDO constructor.
| | 06:12 | Then if I pass it a table name, it sets the
table name, which is this private variable.
| | 06:16 | I use private variables for these things.
| | 06:18 | It's just one of those
object-oriented habits that I have gotten into.
| | 06:22 | If I want to access any of those
variables, I'll use a setter getter;
| | 06:25 | I don't like to have
public variables in an object.
| | 06:29 | Again, it's my own style.
| | 06:30 | Feel free to use this as a basis for your
own libraries, and to do them in your own style.
| | 06:36 | So I have setter getters for the
table name and a setter getter for a
| | 06:40 | PDO database handle,
| | 06:41 | so I can actually change the
PDO database handle if I want to.
| | 06:46 | A setter getter for a statement handle.
| | 06:48 | If I wanted to I could set something
up in PDO, pass it the statement handle,
| | 06:51 | and then use my own CRUD methods.
| | 06:54 | Then we get into the meat of it.
| | 06:55 | Sql_do, this is the one
that I probably use the most.
| | 06:59 | You'll notice that it
doesn't have any parameters here;
| | 07:01 | instead, it uses PHP's func_get_
args, which allows me to have a variable
| | 07:06 | number of arguments.
| | 07:08 | I have one set up for multiple
queries, which just passes a query to exact.
| | 07:13 | This does not do any parameter
substitution whatsoever. And then there is SQL
| | 07:17 | query, which returns to the statement handle
from PDO and allows it to be used as an iterator.
| | 07:24 | query_all is a very dangerous one that
actually calls fetchALL, and will give
| | 07:28 | you all of the rows of
the database in a variable.
| | 07:31 | This is a great way to crash your
program. Only ever use this if you know exactly
| | 07:35 | how many rows you're expecting,
and it's not a large number.
| | 07:39 | Sql_query_row returns one row. sql_query
_value returns one value. Then we have
| | 07:47 | begin_transaction and commit.
These are just passed through,
| | 07:50 | but it's handy to have them.
| | 07:52 | Finally, we get into the CRUD methods.
| | 07:55 | The CRUD methods actually
create the queries on the fly.
| | 08:00 | So get_rec is very simple.
get_recs is also very simple.
| | 08:04 | Insert a record actually
has to create the insert SQL.
| | 08:09 | So it does it based on the keys and
the values of the associative array
| | 08:12 | that's passed to it.
| | 08:14 | Likewise with update;
| | 08:15 | it has to create that SQL query on the fly.
| | 08:19 | Delete is very simple.
| | 08:20 | It just deletes from the table name,
based on the id that's passed to it.
| | 08:25 | Get_next is a pseudo-
iterator that is useful in an array.
| | 08:29 | Again, this is legacy, so that it
would work with some of my older code.
| | 08:33 | count_recs is self-explanatory;
| | 08:35 | it will just count the number of recs.
| | 08:36 | It does select count.
| | 08:38 | Table_exists actually looks in the
SQLite master table with type set to table to
| | 08:43 | see if it gets back a result.
| | 08:46 | I did some experimenting with
different ways of checking to see if a table
| | 08:49 | exists, and this actually
turned out to be the fastest way.
| | 08:52 | You'll notice over here that I was
timing these table_exists as I tried the
| | 08:56 | different ways of doing it.
| | 08:57 | Version just returns the versions.
| | 08:59 | timer_starts sets micro
time, and timer ends the timer.
| | 09:03 | So that is my library.
| | 09:06 | Again, I strongly suggest
that you create your own.
| | 09:09 | Feel free to use my bwSQLite 3 interface as a
starting point in writing your own interface.
| | 09:15 | Having your own interface can be very
helpful for your own productivity, and the
| | 09:19 | process of writing it
can be educational as well.
| | Collapse this transcript |
|
|
15. A Simple CRUD ApplicationDefining CRUD| 00:00 | The term CRUD stands for
Create Read Update and Delete;
| | 00:03 | these are the four basic
functions of a database system.
| | 00:06 | This CRUD application is a
demonstration of how those functions work in
| | 00:10 | a relational context.
| | 00:12 | This application also demonstrates the
use of two tables that are related, in
| | 00:16 | this case the Album table and the Track
table. An album contains tracks and so
| | 00:22 | an album has a one-to-many
relationship with tracks.
| | 00:26 | So when you first bring up the
CRUD application, you get this screen.
| | 00:29 | It counts the number of albums in the
database and tells you how many there are.
| | 00:33 | It displays the albums in the database,
along with some information about each
| | 00:37 | album, and it allows you to add an album,
to edit an album, or delete an album.
| | 00:42 | So those are the four functions, Create:
| | 00:45 | add an album, Read:
| | 00:47 | display the list of albums, Update:
| | 00:50 | edit an album, or Delete, to delete an album.
| | 00:54 | So, let's go ahead and add
a new album to the database.
| | 00:57 | We'll call this My New Album, and give
it an artist name, and a label, and we'll
| | 01:08 | give it a release date, and
we'll go ahead and press Add Album.
| | 01:17 | So we get the message, Album "My New Album"
has been added, and you may now add tracks below.
| | 01:22 | The cursor is put in the first field
of this Tracks row, and we'll just start
| | 01:26 | adding tracks, track number 1, "This is
track number one," and give it a duration
| | 01:34 | of 29 minutes and 42 seconds.
| | 01:37 | I'm just going to press the Return key here.
| | 01:40 | I could click on the Add button,
but I'm not using my mouse.
| | 01:43 | I'm just going to click on the Return
key, and you'll notice that the track
| | 01:47 | gets added, we have the message at the
top, and the cursor is put in position
| | 01:51 | for the next track to be added.
| | 01:53 | So 2, "Another track for this amazing
album," and 14 minutes and 27 seconds.
| | 02:05 | And track number 3, we'll just put
in one more, and give it a duration.
| | 02:16 | Now, I'll just press on Done up here,
and we have added an album to database.
| | 02:24 | Here it is, My New Album, and we can edit it
if we want to, and we could add another track.
| | 02:35 | We can delete a track.
| | 02:36 | Let's delete this second track.
| | 02:38 | We get a confirmation page, and we
can press Done, and there it all is.
| | 02:44 | Now all of these changes are happening
in the Album database, which, of course,
| | 02:47 | is also available in SID.
| | 02:49 | So, if I switch over here to the album.
db and I select * from album, you'll see
| | 02:58 | that we have a list of all the albums.
| | 03:00 | ID for my new album is ID number 19,
so I can say select * from track where
| | 03:07 | album_id = 19, and there are all those tracks.
| | 03:12 | So all this data is available in SID,
so you can use SID to look at what's
| | 03:17 | happening while you're
operating on the database in CRUD.
| | 03:21 | Now, if I delete this album, and I
just press the Delete over here, I can
| | 03:25 | delete the entire album,
| | 03:27 | this will actually delete the album
from the Album table and all of its tracks
| | 03:31 | from the Track table.
| | 03:32 | So I press Delete, and there it is.
| | 03:35 | If I go into SID, I'll see
that all those tracks are gone.
| | 03:40 | If I select * from album, you'll
see that that album is gone, too.
| | 03:47 | So a CRUD application is a great way
to learn about database operations.
| | 03:52 | This application can be repurposed for
any number of relational uses, or it can
| | 03:56 | be used as a basis for further
experimenting and learning about SQL relational
| | 04:01 | databases, or database applications in general.
| | Collapse this transcript |
| Using PHP to open and use an SQLite database| 00:00 | This CRUD application uses the PDO
interface so that the same code can be used
| | 00:04 | with either SQLite or the
MySQL database management system.
| | 00:08 | So you'll see one of the first
things we do here up at the top of the
| | 00:10 | application is we define a constant,
called DBENGINE, and that is set in
| | 00:15 | this case to SQLite 3
| | 00:17 | instead of MySQL, if you are using this
using this application with a MySQL database.
| | 00:22 | Then for SQLite, we define DBFILE,
which is the location of the database file
| | 00:28 | in the file system.
| | 00:29 | Be sure to edit this line and use the
path to the database on your system, as
| | 00:35 | this path will not work on your system.
| | 00:37 | This is set up for this set for
this system here in the recording booth.
| | 00:41 | You'll notice that this is
commented out, the for MySQL stuff.
| | 00:45 | We don't need any of this USER,
PASS, DB for the SQLite database.
| | 00:51 | Moving on to the init function,
| | 00:53 | this where we open the database.
| | 00:56 | You'll notice I'm using a switch
construct here, because I plan to support more
| | 01:00 | databases in the future.
| | 01:02 | I'll probably be adding
support for Postgres next.
| | 01:06 | Right now, it's got support
for SQLite3 and for MySQL.
| | 01:10 | So, here in this PDO constructor, this uses
what's called a DSN format for the database.
| | 01:18 | It first has the name of the database
engine in their tables, which in this case
| | 01:23 | is sqlite without a 3.
| | 01:24 | If it were sqlite version 2, it would
have a 2 there, and a colon, and then
| | 01:29 | that's concatenated with, using the PHP
concatenation operator with DBFILE, which
| | 01:34 | is the path to the file on the file system.
| | 01:38 | Then once we have constructed the PDO
object, which is kept here in this dbh
| | 01:44 | variable, we then use that to run
PDO's sqliteCreateFunction method to create
| | 01:51 | two functions and bind those to SQLite.
| | 01:53 | Those are user-defined functions that
are used here in this application, and
| | 01:58 | one aggregate, the SUM_SEC_TO_TIME.
| | 02:00 | So SEC_TO_TIME, TIME_TO_SEC, and SUM_
SEC_TO_TIME are user-defined functions.
| | 02:05 | SUM_SEC_TO_TIME is an aggregate function.
| | 02:07 | These are used for the
track length in the track table.
| | 02:12 | The track length is stored in the
duration column of the track table, and it's
| | 02:17 | stored as a number of seconds, and
these functions convert back and forth
| | 02:21 | between a time format.
| | 02:22 | When we look at the CRUD application
you'll notice one of the things you see
| | 02:26 | here is the duration of the entire album.
That uses the SUM_SEC_TO_TIME function.
| | 02:32 | And you'll notice, if you do run this
with MySQL, that doesn't show up at all,
| | 02:37 | because that's only used for the
SQLite version of this application.
| | 02:42 | You will notice if we go ahead and
edit one of the albums, you'll notice that
| | 02:45 | these durations are displayed in minutes
and seconds, even though in the database
| | 02:51 | they're stored as number of seconds,
and that conversion is done by this
| | 02:55 | SEC_TO_TIME function.
| | 02:57 | So this sqliteCreateFunction method
allows you to use a PHP function to create
| | 03:04 | user-defined functions
that are used from SQLite.
| | 03:07 | It's a terrific capability.
| | 03:09 | It's one of the things I love about SQLite.
| | 03:12 | So that's how we connect to the database
using SQLite and PDO in this CRUD application.
| | 03:18 | We'll talk about the rest of the four
functions of the database: Create, Read,
| | 03:22 | Update, Delete in the
other movies in this chapter.
| | 03:24 | SQLite makes it easy to open and use a database.
| | 03:28 | This example shows how you can easily
support multiple database engines for
| | 03:32 | flexibility and
scalability in your applications.
| | Collapse this transcript |
| Using PHP to insert into an SQLite database| 00:00 | In this movie, we will look at how
to use SQL to insert into a table in
| | 00:04 | an SQLite database.
| | 00:06 | Here around line 590 in the CRUD
application, you will see there is a comment
| | 00:11 | that says database interface functions and SQL.
| | 00:14 | So I tried to, in this
application, because it's a lot of code -
| | 00:19 | there is about 1000 lines of code here -
| | 00:20 | I've tried to section it off, so
that certain types of things happen in
| | 00:25 | certain areas of the code.
| | 00:27 | So in this section of the code,
this is where all the database
| | 00:30 | interface functions are.
| | 00:31 | And so you see we have the get_albums_
sql, the get_tracks_sql, and down here, we
| | 00:37 | have the insert_album_sql.
| | 00:39 | This is very simple, and it's a great
place to start because we can look at the
| | 00:42 | structure of it, rather than the details of it.
| | 00:45 | So we define the query, and
this is the actual SQL part of it.
| | 00:49 | It's a simple insert statement, and you
will see that it uses these placeholders
| | 00:53 | for the data that's actually being inserted.
| | 00:56 | This is a very important technique.
| | 00:58 | By using this technique, instead of
using variable substitution, in other words
| | 01:03 | instead of just putting the variables in
there from PHP directly, by allowing the
| | 01:08 | interface to do the interpolation and
the quoting of the data, then we are
| | 01:14 | allowing the database engine to do what
it does best. And one of those things is
| | 01:18 | to protect us from SQL injection
attacks and things like that, by using the
| | 01:22 | facilities that are provided to
us for the variable substitution.
| | 01:26 | So it's really valuable to do it this way.
| | 01:29 | It's important that you do it this
way as often as you can, instead of doing
| | 01:33 | something like having a quoted string,
and having your INSERT statement, and
| | 01:48 | then actually putting the data in
using quotes and using the variable
| | 01:53 | substitution that comes from PHP,
like doing something like that.
| | 01:58 | So it's really important that you do
not do something like this where you
| | 02:01 | type the SQL into a double-quoted
string, and actually put the variables in
| | 02:07 | the string, like here.
| | 02:13 | If you do it this way, then you are a
lot more exposed to the security risks on
| | 02:17 | the Internet, allowing people to do
things like create SQL injection attacks and
| | 02:22 | other nefarious activities to exploit your code.
| | 02:27 | This is why we use variable
substitution in the database engine.
| | 02:31 | These question marks are replaced in the
PDO driver and also with the facilities
| | 02:37 | of the database code itself, which is
designed to protect us from the threats and
| | 02:43 | the exploits that are out there in the wild.
| | 02:46 | So it's really important to use
this technique and not the language
| | 02:49 | variable substitution.
| | 02:51 | The Prepare method in DBO, it takes
the query and prepares it, and then the
| | 02:56 | Execute method actually
does the variable substitution.
| | 03:00 | So here we pass in an array, and we
pass in all of these variables from the
| | 03:05 | screen, and let the database drivers
do their job in replacing them into the
| | 03:10 | places of these placeholders in the SQL.
| | 03:13 | Finally here, we check for errors, we
report the errors if we have to, and
| | 03:17 | finally, the lastInsertId method which
returns the ID of the row that was just
| | 03:22 | inserted into the table, and
allows us to operate with the relational
| | 03:27 | properties of the database.
| | 03:29 | This is the code for insert_track_
sql, which is pretty much the same.
| | 03:33 | You see we use the same variable substitution.
| | 03:36 | It's very, very similar code, and
it also returns the lastInsertId.
| | 03:39 | So you'll notice that this code is
not different for SQLite and MySQL.
| | 03:45 | SQLite uses standard SQL.
| | 03:49 | There are some cases you will see, in
the rest of this code, where we do have to
| | 03:52 | use different SQL for SQLite.
| | 03:55 | But in most cases, the same SQL is
going to work across database engines.
| | 04:00 | That makes it very easy for us to
support scalability, because SQLite is an
| | 04:05 | excellent choice for applications that
will run on one server at a time.
| | 04:09 | But with PHP and PDO, it makes it easy to
support multiple database engines in your
| | 04:14 | application for flexibility and scalability.
| | Collapse this transcript |
| Using PHP to read from an SQLite database| 00:00 | Let's take look at how to use PHP and
SQLite to read from tables in the database.
| | 00:05 | In the get_albums_sql function,
| | 00:08 | this is really very simple,
| | 00:09 | it has a simple select and prepare and execute.
| | 00:14 | There's really not much to this one.
| | 00:16 | In the get_tracks_sql, this one
is a little bit more complicated.
| | 00:21 | You will notice that there are two
different queries for MySQL and for SQLite.
| | 00:27 | The MySQL version uses this proprietary MySQL
function CONCAT_WS to build the time string.
| | 00:34 | SQLite uses our user-definable
function SEC_TO_TIME to build the time string.
| | 00:41 | The problem is, if we look in the
database, and here is a set of tracks from the
| | 00:45 | database, and you will notice that the
duration is stored in number of seconds,
| | 00:50 | and we have displayed it in CRUD,
here is the same data in crud.
| | 00:54 | You will notice that it's
displayed in minutes and seconds.
| | 00:57 | So that needs to be converted.
| | 00:59 | The data is stored in one
format, and displayed in another.
| | 01:01 | So MySQL uses this proprietary string
function to do it, which is certainly one
| | 01:06 | way, and you will notice how
much cleaner the code is for SQLite.
| | 01:09 | So we use our
user-definable function SEC_TO_TIME.
| | 01:13 | So if we take a look at that function,
that's actually a PHP function, we go up
| | 01:18 | to _init, we will see that it's
registered with SQLite, using the sqliteCreateFunction.
| | 01:25 | The first argument is the name of the
function, for SQL purposes; the second
| | 01:29 | argument is the name of the function in
PHP; and the third argument here tells
| | 01:35 | how many arguments there are.
| | 01:38 | If we go down here to the actual
function in PHP, we see that it's a very
| | 01:43 | short piece of code.
| | 01:45 | It simply takes the number of seconds,
make sure it's an integer, does a little
| | 01:49 | bit of math on it, separates out the
minutes and seconds, and returns it using
| | 01:54 | sprintf to return a formatted string.
| | 01:58 | So if we look again at the get_tracks_sql,
that function is called right here in
| | 02:05 | the SQL, and it makes the entire query
very simple to understand, very clean.
| | 02:11 | So SQLite, with its flexible user-defined
functions facility, makes it easy to get
| | 02:16 | data from your table in a
format that fits your application.
| | Collapse this transcript |
| Using PHP to update an SQLite database| 00:00 | Now let's take a look at how to use PHP
and SQLite to update tables in a database.
| | 00:05 | This is really the simplest of all the
database operations in this application.
| | 00:10 | It's simply a matter of submitting the
standard SQL, which is exactly the same
| | 00:14 | for both the MySQL and SQLite engines.
| | 00:18 | You will notice we are using
the placeholders here again:
| | 00:20 | 1, 2, 3, 4, 5, and so we have the 1, 2, 3,
4, and 5 arguments, and those are all
| | 00:31 | passed as an array to the execute method.
| | 00:34 | That's all there is to it for the album.
| | 00:36 | The track SQL is virtually the same.
| | 00:39 | We have four arguments here:
| | 00:40 | the track number, title, duration, and
the ID for updating the correct row in
| | 00:47 | the table, and that's really all there is to it.
| | 00:50 | The Update function is the
simplest function in the application.
| | 00:53 | It's really just a matter of submitting a
simple update query to the database engine.
| | 00:57 | SQLite and PHP make it easy to
perform simple operations like this.
| | Collapse this transcript |
| Using PHP to delete from an SQLite database| 00:00 | When deleting records from a set of
related tables, you need to make sure that
| | 00:04 | the subordinate rows are
deleted, as well as the primary rows.
| | 00:07 | First, we'll take a look at deleting
tracks, because this is very simple.
| | 00:11 | There aren't any subordinate rows involved.
| | 00:13 | When you delete a track, you simply delete the
track where the track ID is a particular row.
| | 00:20 | Deleting albums is a little bit more
complicated because you want to make sure
| | 00:24 | that the tracks for the
album are deleted as well.
| | 00:28 | So we have two queries here.
| | 00:29 | We have the delete tracks query, which
deletes the tracks where the album_id is
| | 00:34 | the ID that's been passed to function.
| | 00:36 | Then delete the album after that.
| | 00:38 | So first we delete the tracks,
because these are the subordinate rows.
| | 00:42 | Once the album has been deleted, those
tracks will be orphaned, and it would be
| | 00:46 | a challenge to clean them up afterwards.
| | 00:48 | So first we delete the tracks.
| | 00:50 | This way, even if the application is
interrupted in the middle of deleting
| | 00:54 | the tracks, they're not completely orphaned,
because the album record is still there.
| | 00:59 | After we've deleted the tracks,
then we can delete the album.
| | 01:03 | Deleting rows is a little bit more
complicated when there are related tables involved.
| | 01:07 | This CRUD application provides an
example of a simple solution to this problem.
| | Collapse this transcript |
|
|
16. An Application for Web Site TestimonialsAn overview of the testimonials application| 00:00 | The Testimonials application is an
example of a real-world application that uses
| | 00:04 | many of the techniques covered in this
course to leverage the power of SQLite
| | 00:08 | and PHP, to create significant
functionality with a minimum of code.
| | 00:13 | This is what the Testimonials
application looks like on a web site.
| | 00:16 | I actually use this on my web site, this
exact same application, to show testimonials.
| | 00:20 | Whenever somebody sends me an e-mail or
something telling me that they like my
| | 00:24 | work, I'll take an excerpt of it,
and I'll drop it in the database.
| | 00:28 | There would be testimonials
that will show up on the page.
| | 00:31 | So every time you hit Reload,
you get a different set of quotes.
| | 00:35 | For our purposes here, I've just
put some witty sayings in there,
| | 00:38 | but this is actually an
application that I use on my web site.
| | 00:42 | At the backend of the application -
that's this db.php, this is where you can
| | 00:47 | enter and edit the testimonials.
| | 00:49 | It's got a little paging feature here,
so you can show just so many of them on
| | 00:53 | the screen at a time.
| | 00:54 | I've got it set to five at a time,
because we have limited screen real estate
| | 00:58 | here for the training purposes.
Bottom line, I've got it set to some higher
| | 01:01 | number, and so you can go directly to the
page, or you can page back and forth like this.
| | 01:07 | You want to add a new testimonial,
| | 01:09 | you just put in something witty here,
| | 01:10 | you say "Something witty or inspirational," and
you put in a Byline, so this was said by me, and you add it.
| | 01:21 | Then you see it shows up down here.
| | 01:23 | These are alphabetical by the author.
| | 01:26 | If you want to edit it, you press Edit, and say
"Something else," and say Update. It updates there.
| | 01:34 | It's very simple.
If you want to delete it, you press Delete.
| | 01:37 | You get a little confirmation page here.
| | 01:38 | You press Delete, and it's gone.
| | 01:41 | So Create, Read, Update, Delete;
| | 01:43 | it's the fundamentals of database programming.
| | 01:46 | So, this is the backend.
| | 01:48 | This is the way you manage the
Testimonials database, and the testimonials
| | 01:53 | themselves - I have to get at it this way,
| | 01:57 | the testimonials themselves are
displayed with this testimonials.php.
| | 02:01 | You see that this just brings up raw HTML.
| | 02:04 | If I do the View Source on the
browser here, you see it's got these class
| | 02:09 | attributes, so that it
can be formatted with CSS.
| | 02:12 | That's exactly what's showing up in
the test page that has the Lorem ipsum.
| | 02:17 | It's exactly the same HTML there,
but it's just formatted with the CSS.
| | 02:21 | If you look in the Testimonials folder, in the
example files, you see the layout of it here.
| | 02:27 | You see testpage.php.
| | 02:29 | If I open that up in my editor, you'll
see the testimonials gets included here
| | 02:35 | in this one line of PHP.
| | 02:36 | The rest of this page is just HTML.
| | 02:38 | It just gets included
with this include statement.
| | 02:43 | The number of testimonials to
display is set with this variable here.
| | 02:47 | So it chose three testimonials at a time.
| | 02:49 | Then the db.php, we'll
look at that in another movie,
| | 02:54 | that is the code for the backend, which is 300
and something lines of codes. It's very small.
| | 03:00 | The testimonials.php, we'll
look at it in another movie.
| | 03:03 | It's also much, much smaller than db.php.
| | 03:07 | That just displays the testimonials.
| | 03:09 | In the lib folder is the bwSQLite 3.php library.
| | 03:14 | We are using that for this application.
| | 03:17 | That's part of why it can be as small as
it is, because it uses the CRUD features.
| | 03:21 | In the data directory is
the testimonials database.
| | 03:25 | If you are on a Mac or on a UNIX system,
you want to make sure that this data
| | 03:28 | directory has Read & Write permissions
for everyone, like it does here, and also
| | 03:35 | that the testimonials.db file has
Read & Write permissions for everyone.
| | 03:40 | That's really important so that the
application can write to the database.
| | 03:46 | Both of those, the directory and the
database itself, have to have Read & Write
| | 03:49 | permissions for everyone.
| | 03:51 | Then the assets folder has little
snippets of HTML, like for instance this one
| | 03:56 | here, which are used to
assemble the pages for the backend.
| | 04:04 | The main page is right here.
| | 04:05 | It is just very small, and it's
got the space for the content on it.
| | 04:10 | It's just a lot of variable
replacement, placeholders.
| | 04:15 | So, that's the structure
of the application here.
| | 04:18 | So you take all of this stuff,
| | 04:20 | you drop it on your web site,
| | 04:22 | you edit the paths in db.php and
testimonials.php, and include it in your web
| | 04:29 | site using this testpage.php as an example, and
| | 04:33 | this thing will run on your web site.
| | 04:35 | So the Testimonials application
demonstrates how to use the techniques covered
| | 04:39 | in this course to create a small
and functional database application.
| | 04:43 | You may use this as a starting point
for your own small database applications.
| | Collapse this transcript |
| Managing the database in PHP| 00:00 | The db.php application
represents the database backend.
| | 00:04 | This is where the database is maintained.
| | 00:07 | So, here at the top of the
file, we set some constants:
| | 00:10 | the TITLE, VERSION, things like that.
| | 00:13 | The PAGELIMIT is set here.
| | 00:15 | This is something that you might want to change.
| | 00:17 | This is the number of items that
are displayed on the page in the
| | 00:20 | Testimonials database here.
| | 00:22 | The bwSQLite 3 library is included.
| | 00:27 | This application does use that library
to keep the database operations simple.
| | 00:31 | You'll need to define the
location of the database.
| | 00:34 | If you use the same structure, you
probably won't even have to change that name.
| | 00:38 | This is the name of the
table for the database itself.
| | 00:42 | Down here in the init function is
where the initialization happens.
| | 00:48 | Variables are set up and
initialized for the page display engine.
| | 00:53 | The database itself is
constructed using the bwSQLite 3 constructor.
| | 00:58 | It just takes the database name, which is the
file name and file location, and the TABLENAME.
| | 01:03 | That's really all there is to that.
| | 01:06 | The database routines
themselves start here with add.
| | 01:10 | You'll see they're all very simple,
because we're using the CRUD interface in
| | 01:14 | the bwSQLite 3 library.
| | 01:18 | So we populate an associative array,
and pass that rec on to the CRUD routines.
| | 01:24 | This one inserts a record.
| | 01:28 | This one here gets a
record for confirming a delete.
| | 01:34 | This one here performs
the delete based on the ID.
| | 01:37 | So these are all very simple
because we're using the CRUD interface.
| | 01:42 | Here is the update.
| | 01:43 | It populates an associative array, and passes
down along with the ID to the update method.
| | 01:50 | And edit, just like delete, it uses the ID to
get a record and displays that for editing.
| | 01:57 | Then the real work of the page
display is done here in listrecs.
| | 02:02 | This is what creates this area of
the page down here, with its paging.
| | 02:07 | You can go forward a page, back a page.
| | 02:09 | You can jump to a page.
| | 02:12 | That's all done in this routine here.
| | 02:15 | It first takes a count of the records.
| | 02:20 | It does some calculations to decide
how many records we have, how many we're
| | 02:25 | going to display, and which page
we're on, and so which records will be
| | 02:29 | displaying on the page.
| | 02:30 | It's a really very simple
arithmetic to figure that out.
| | 02:34 | Then it constructs a very simple
SELECT statement using LIMIT and OFFSET.
| | 02:39 | This LIMIT and OFFSET is nonstandard SQL,
but it uses the same syntax as MySQL
| | 02:45 | uses for this same function.
| | 02:47 | LIMIT says how many records to return
for a SELECT statement, and OFFSET says
| | 02:52 | how many off from the beginning of the
set to offset those number of rows that
| | 02:58 | you're going to return.
| | 02:59 | Then that's passed into sql_query
using the calculations that we got earlier.
| | 03:06 | sql_query returns an iterator.
| | 03:09 | It returns a PDO statement object, which
can then be used in this foreach loop,
| | 03:16 | so the call is actually
right there in the foreach loop.
| | 03:19 | That makes the syntax very simple and compact.
| | 03:23 | That's really all that's involved.
| | 03:25 | I mean, there is some other support
code here for displaying the pages
| | 03:28 | and things like that,
| | 03:29 | but that's the meat of how
this is done. It's very simple.
| | 03:33 | It's very straightforward.
| | 03:34 | Having a library that does the basic
functions that you need to do commonly in
| | 03:40 | your style of coding is a real great asset.
| | 03:43 | It helps to keep the line count down.
| | 03:44 | It helps to keep the code simple and
compact enough, so that the logic of what
| | 03:49 | it is that you're doing is prominent.
| | 03:51 | It just makes it easy to code and to maintain.
| | 03:54 | So the db.php application is the
heart of the Testimonials database.
| | 03:59 | This is an example of how you can
create functionality and usability with
| | 04:03 | a minimum of coding.
| | Collapse this transcript |
| Displaying the testimonials using PHP| 00:00 | This is the part of the application that
displays the testimonials on the web page.
| | 00:04 | Because the work has already been done
in other parts of the application, this
| | 00:07 | part is remarkably simple.
| | 00:09 | So, all this does is it displays these
quotes, or testimonials, on the screen and
| | 00:13 | every time the page is reloaded
you get a different set of them.
| | 00:17 | Here's the code that does that.
| | 00:18 | It's just very short and sweet.
| | 00:20 | It's just 90 lines of code, including
comments, and it starts with a set of
| | 00:24 | constants, just like the backend does, and it
calls init, which in this case is very short.
| | 00:30 | It really just instantiates the
database using bwSQLite 3, so that makes it
| | 00:35 | really easy. And the bulk of the
work is done here in the main function.
| | 00:41 | So, it grabs RECCOUNT from the host PHP
file, and if we look at the testpage.php -
| | 00:47 | this is where that Lorem ipsum is,
| | 00:49 | you'll see that the testimonials.
php is included here. And before it's
| | 00:53 | included RECCOUNT is defined, and
that gives us the number of records to
| | 00:59 | display on the page at a time.
| | 01:02 | Then over here in testimonials.php, we
define that as a global so that we can get
| | 01:06 | that from the enclosing PHP namespace, and then
we check to see if it's set. If it is, great;
| | 01:13 | if not, we default to 3.
| | 01:15 | We check to make sure
that we've got the database.
| | 01:20 | Then we query the database, getting
all of the IDs, and we loop through this
| | 01:24 | query, because sql_query returns an iterator.
| | 01:27 | We can put it right there in the foreach.
| | 01:30 | We loop through that and populate
our idlist with all of the ids from the
| | 01:34 | database. Very quick and very simple.
| | 01:37 | So, we check out how many we have.
| | 01:40 | We make sure that we have a number that
we can use. Given the RECCOUNT, you want
| | 01:44 | to make sure that there is enough
records to give it decent randomness. And the
| | 01:49 | meat of it is right here.
| | 01:50 | We use the array_rand function from PHP.
| | 01:53 | This is really handy little function.
| | 01:55 | PHP has a rich set of functions for
dealing with arrays, and this one will give
| | 02:00 | us a set of random keys from an array.
| | 02:03 | You tell it how many you want, and it will
give you that many random keys from the array.
| | 02:07 | So, you want three of them,
| | 02:08 | we grab those, and then we loop
through that resulting array of keys, and
| | 02:14 | we call printrec for each of the IDs that
we grab from our idlist using those keys.
| | 02:19 | Printrec simply displays a
record from the database.
| | 02:23 | So that's all there is to it.
| | 02:25 | It's really very simple and very short.
| | 02:28 | The entire Testimonials application
is an example of a simple and usable tool,
| | 02:33 | written with a minimum of
code, using SQLite 3 in PHP.
| | Collapse this transcript |
|
|
ConclusionGoodbye| 00:00 | In this course, my goal was to give you
a good understanding of SQLite so you
| | 00:04 | can use it with PHP to build powerful
and compelling applications for yourself
| | 00:08 | and for your clients.
| | 00:10 | I've covered the basic features of
SQLite, its unique data model, its
| | 00:14 | functions, its use of SQL, and
| | 00:16 | I've walked you through some real PHP
applications that use SQLite, so you can
| | 00:20 | apply your knowledge to building
your own projects with SQLite and PHP.
| | 00:25 | I've really enjoyed creating this course
for you, and I sincerely hope that it's
| | 00:29 | as useful for you as it has been fun for me.
| | Collapse this transcript |
|
|