Besides the standard math operators that MySQL uses (for addition, subtraction, multiplication, and division), there are about two dozen functions dedicated to formatting and performing calculations on number columns. Table 6.2 lists the most common of these, some of which I will demonstrate shortly.
Table 6.2. Here are the most-used number functions, omitting the various trigonometric and exponential ones.
Function and Usage
Returns the absolute value of num.
Returns the next-highest integer based upon the value of num.
Returns the integer value of num.
Returns num formatted as a number with y decimal places and commas inserted every three digits.
Returns the remainder of dividing x by y (either or both can be a column).
Returns the value of x to the y power.
Returns a random number between 0 and 1.0.
Returns the number x rounded to y decimal places.
Returns a value indicating whether num is negative (-1), zero (0), or positive (+1).
Calculates the square root of num.
I want to highlight three of these functions: FORMAT(), ROUND(), and RAND(). The firstwhich is not technically number-specificturns any number into a more conventionally formatted layout. For example, if you stored the cost of a car as 20198.2, FORMAT(car_cost, 2) would turn that number into the more common 20,198.20.
ROUND() will take one value, presumably from a column, and round that to a specified number of decimal places. If no decimal places are indicated, it will round the number to the nearest integer. If more decimal places are indicated than exist in the original number, the remaining spaces are padded with zeros (to the right of the decimal point).
The RAND() function, as you might infer, is used for returning random numbers. Specifically, it returns a value between 0 and 1.0.
A further benefit to the RAND() function is that it can be used with your queries to return the result in a random order.
SELECT * FROM tablename ORDER BY RAND()
To use numeric functions:
Display the invoices by date, formatting the amounts as dollars (Figure 6.9
Figure 6.9. Applying two functions and the right formatting to the invoice_amount column generates the better amount values.
SELECT *, CONCAT('$', FORMAT (invoice_amount, 2)) AS amount FROM invoices ORDER BY
Using the FORMAT()
function, as just described, in conjunction with CONCAT()
, you can turn any number into a currency format as you might display it in a Web page or application.
Notice in the figure how the invoice amount is actually returned twice: once as part of all columns (*
) and the second time in its formatted shape.
Round each expense amount to the nearest dollar (Figure 6.10
Figure 6.10. The ROUND() function is useful in situations where decimal values do not matter.
SELECT ROUND(expense_amount), expense_amount FROM expenses;
function, when you do not specify a decimal argument, simply rounds every value to the nearest integer.
Retrieve all of the client names in a random order twice (Figure 6.11
Figure 6.11. Running the same query twice with the ORDER BY RAND() clause returns the same results but in different order.
SELECT client_id, client_name FROM clients ORDER BY RAND();
SELECT client_id, client_name FROM clients ORDER BY RAND();
Although this may not be the most practical use of the ORDER BY RAND()
clause, it does give you an idea of how it works. While the RAND()
function is not absolutely random, it is effective enough for most cases. Notice that you do not specify to which column RAND()
Along with the mathematical functions listed here, a number of trigonometry, exponential, and other types of functions are available. Of course you can also use any of the mathematical operators: +, -, *, and /.
The MOD() function is the same as using the percentage sign:
Once again, remember that functions can be applied to columns or to hard-coded values. The following queries are perfectly acceptable:
SELECT ROUND(34.089, 1)
Date and Time Functions
The date and time column types in MySQL are particularly flexible and useful. But because many database users are not familiar with all of the available date and time functions, these options are frequently underused.
Whether you want to make calculations based upon a date or to return only the month name from a stored value, MySQL has a function for that purpose. Table 6.3 lists most of these functions.
Table 6.3. MySQL uses several different functions for working with dates and times in your databases. In the usage examples, dt could represent a date, a time, or a datetime.
Date and Time Functions
Function and Usage
Returns just the hour value of dt.
Returns just the minute value of dt.
Returns just the second value of dt.
Returns just the date value of dt.
Returns the name of the day of dt.
Returns just the numerical day of dt.
Returns the name of the month of dt.
Returns just the numerical month value of dt.
Returns just the year value of dt.
DATE_ADD(dt, INTERVAL x type)
Returns the value of x units added to dt (see the sidebar).
DATE_SUB(dt, INTERVAL x type)
Returns the value of x units subtracted from dt (see the sidebar).
CONVERT_TZ(dt, from_zone, to_zone)
Converts dt from one time zone to another.
Returns the current date.
Returns the current time.
Returns the current date and time.
Returns the number of seconds since the epoch or since the date specified.
As you can tell, the many date and time functions range from those returning portions of a date column to those that return the current date or time. These are all best taught by example.
To use date and time functions:
Display every invoice billed in April (Figure 6.12
Figure 6.12. Using the MONTH() function, I can narrow down my search results based upon the values in a date column.
SELECT * FROM invoices WHERE
MONTH(invoice_date) = 4\G
Because April is the fourth month, this query will return only those invoices billed then. Another way of writing it would be to use WHERE MONTHNAME(invoice_date) = 'April'
(although it's best to use numbers instead of strings wherever possible).
Show the current date and time, according to MySQL (Figure 6.13
Figure 6.13. The CURDATE() and CURTIME() functions return the current date and time. The same can be accomplished with NOW().
SELECT CURDATE(), CURTIME();
To show what date and time MySQL currently thinks it is, you can select the CURDATE()
functions, which return these values. This is an example of a query that can be run without referring to a particular table name (or without even selecting a database).
Display every expense filed in the past two months (Figure 6.14
Figure 6.14. Here I've used the CURDATE() function again (see Figure 6.13) to set a range of acceptable dates for selecting expenses.
SELECT CONCAT('$', FORMAT (expense_amount, 2)) AS amount,
FROM expenses AS e,
expense_categories AS e_c
WHERE (e.expense_category_id = e_c.expense_category_id)
AND (expense_date BETWEEN
SUBDATE(CURDATE(), INTERVAL 2 MONTH)
This query uses a lot of different techniques covered to this point. For starters, it's a simple join to incorporate the expense category along with the expense information. Second, I've converted the expense amount into a more readable format. Third, there are two conditions set in the WHERE
clause: one for the join and another limiting results to the previous six months. The BETWEEN
term is used to specify a range from two months ago, SUBDATE(CURDATE(), INTERVAL 2 MONTH)
, and today, CURDATE()
, which rules out expenses that may have been prebilled (i.e., are in the future).
Because MySQL is such a fast and powerful application, you should try to let it handle most of the formatting of values. Beginning programmers will commonly retrieve values from MySQL and then use the programming language to format the data, which is less efficient.
The NOW() and CURDATE() functions are often used to set the value for a date or datetime column when inserting records:
INSERT INTO invoices (client_id, invoice_date, invoice_amount, invoice_description) VALUES
(23, NOW(), 3049.39, `blah blah')
The DATE_ADD() and DATE_SUB() functions, which are synonyms for ADDDATE() and SUBDATE(), perform calculations upon date values. The syntax for using them is
FUNCTION(date, INTERVAL x type)
In the example, date can be either an entered date or a value retrieved from a column. The x value varies, depending upon which type you specify. The available types are SECOND, MINUTE, HOUR, DAY, MONTH, and YEAR. There are even combinations of these: MINUTE_SECOND, HOUR_MINUTE, DAY_HOUR, and YEAR_MONTH.
To add two hours to a date, you would write
DATE_ADD(date, INTERVAL 2 HOUR)
To add two weeks from December 31, 2006:
DATE_ADD('2006-12-31', INTERVAL 14 DAY)
To subtract 15 months from a date:
DATE_SUB(date, INTERVAL '1-3' YEAR_MONTH)
This last query tells MySQL that you want to subtract 1 year and 3 months from the value stored in the date column.
Formatting the Date and Time
There are two additional date and time functions that you might find yourself using more than all of the others combined. These are DATE_FORMAT() and TIME_FORMAT(). There is some overlap between the two in that DATE_FORMAT() can be used to format both the date and the time, whereas TIME_FORMAT() can format only a time value. The syntax is
SELECT DATE_FORMAT(date_column, formatting') FROM tablename
The formatting relies upon combinations of key codes and the percentage sign to indicate what values you want returned. Table 6.4 lists the available date and time formatting parameters. You can use these in any combination, along with textual additions such as punctuation to return a date and time in a more presentable form.
Table 6.4. The terms for date and time formatting are not obvious, but this table lists the most important ones.
DATE_FORMAT() and TIME_FORMAT() Parameters
Day of the month
Day of the month, two digit
Day with suffix
Abbreviated weekday name
Month number, two digit
Month name, abbreviated
Hour, two digit
Hour, 24-hour clock
Hour, 24-hour clock, two digit
Time, 24-hour clock
AM or PM
AM or PM
Assuming that a column called dt has the date and time of 2006-04-30 23:07:45 stored in it, common formatting tasks and results would be
To format the date and time:
Return the current date and time as Month DD, YYYY - HH:MM
Figure 6.15. The DATE_FORMAT() function uses combinations of parameters to return a formatted date.
SELECT DATE_FORMAT(NOW(),'%M %e, %Y - %l:%i');
Using the NOW()
function, which returns the current date and time, I can practice my formatting to see what results are returned.
Display the current time, using 24-hour notation (Figure 6.16
Figure 6.16. If you are formatting just the time, you must use the TIME_FORMAT() function.
Although the DATE_FORMAT()
function can be used to format both the date and the time (or just the date), if you want to format just the time, you must use the TIME_FORMAT()
function. This can be applied to a time value (such as CURTIME()
returns) or a date-time value (from NOW()
Select every expense, ordered by date and amount, formatting the date as Weekday (abbreviated) Day Month (abbreviated) Year
Figure 6.17. DATE_FORMAT() will apply only to stored date values; NULL values will not be affected (see the first record).
SELECT DATE_FORMAT(expense_date, '%a %b %e %Y') AS the_date,
CONCAT('$', FORMAT(expense_amount, 2)) AS amount
FROM expenses ORDER BY expense_date ASC,
This is just one more example of how you can use these formatting functions to alter the output of a SQL query.
MySQL has several different encryption and decryption functions built into the software (Table 6.5). You may have already seen one of these, PASSWORD(), since it's used to encrypt the various user passwords for MySQL access. MySQL advises against using this one in your own tables, and it can be a cause of problems (see Appendix A, "Troubleshooting," for more).
Table 6.5. Different encryption functions are available as of new releases of MySQL, so know what version you are using!
Returns a 32-digit hash.
Returns a 40-digit hash.
Encrypts data using AES algorithm.
Decrypts AES_ENCRYPT() data.
Older encryption function.
Decrypts ENCODE() data.
Encrypts data using DES algorithm, requires SSL.
Decrypts DES_ENCRYPT() data.
May not be available on Windows, no decryption possible.
Two functions, MD5() and SHA1(), create what's called a hash: a fixed-length mathematical representation of data. So the MD5() of the word rabbit is a51e47f646375ab6bf5dd2c42d3e6181. If you store the hash of some data in your database, you can never retrieve the actual data back out (because the data itself was never stored). What you can do is later compare the stored value against the calculated hash of a submitted value. For example, you store the hash of a password and then, when a user logs in, calculate the hash of the login password and see if the two values are equal. These two functions are fine for low-security situations, but both are crackable, meaning they don't provide guaranteed security.
If you want true encryption, you'll need to use one of several functions also listed in the table. Each of these takes two arguments: the data to be encrypted or decrypted, and a salt argument. The salt is any random value that will make the encryption more unique and more secure. The same salt must be used to encode and decode data, though, so the salt must be stored or memorized somehow. Also, these encryption functions return binary data, which must be stored in a BLOB column type.
Of the true encryption functions, the most secure at the time of this writing are AES_ENCRYPT() and AES_DECRYPT(). An example of how you would use them in a query is:
INSERT INTO users (username, user_pass) VALUES ('trout', AES_ENCRYPT ('myPass1', 'NaCL#'))
To retrieve that stored data, you could use this query:
SELECT username, AES_DECRYPT(user_pass, 'NaCL#') FROM users
To encrypt and decrypt data:
Create a new logins
table (Figure 6.18
Figure 6.18. The logins table will be used to demonstrate encryption and decryption.
CREATE TABLE logins (
login_id INT UNSIGNED NOT NULL AUTO_INCREMENT,
client_id SMALLINT UNSIGNED NOT NULL,
PRIMARY KEY (login_id),
Because none of the current tables would logically use encryption, I'm creating a new one. The premise behind this table is that each client can have any number of logins in order to access their account information. The table will contain a login_id
, a login_pass
, and a login_name
. To associate each login with a client, the client_id
is a foreign key here (relating to the primary key with the same name in the clients
For extra security, the login_name
will be encrypted and decrypted and must therefore be a BLOB
type (I've chosen TINYBLOB
). The password will be stored using the SHA1()
function, which always returns a string 40 characters long. Hence, that column is defined as a CHAR(40)
The primary key index and an index on the client_id
(since it's a foreign key and will be used in joins and WHERE
clauses) are added as well.
Insert a new login record (Figure 6.19
Figure 6.19. I encrypt values when storing them.
INSERT INTO logins (client_id, login_name, login_pass) VALUES
(4, AES_ENCRYPT('larryeullman', 'w1cKet'), SHA1('larryPASS'));
Here I am adding a new record to the table, using the SHA1()
function to encrypt the password (larrypass
) and AES_ENCRYPT()
with a salt of w1cKet
to encrypt the user name (larryeullman
). If you are not using at least version 4.0.2 of MySQL, you'll need to change this query to use the MD5()
Retrieve the user's information (Figure 6.20
Figure 6.20. and decrypt them for retrieval.
SELECT client_id FROM logins WHERE (login_pass = SHA1('larryPASS') AND AES_DECRYPT
(login_name, 'w1cKet') = 'larryeullman');
Whenever you store a SHA1()
encrypted string, to make a comparison match, you simply use the same function again. If these match, the right password was entered; otherwise, no value will be returned by this query (Figure 6.21
Figure 6.21. These functions are case-sensitive, so entering a password incorrectly will return no results.
Any value stored using an encryption function can be retrieved (and matched) using the corresponding decryption function, as long as the same salt is used (here, w1cKet
The theory of grouping query results is similar to ordering and limiting query results in that it uses a GROUP BY clause. However, this is significantly different from the other clauses because it works by grouping the returned data into similar blocks of information. For example, to group all of the invoices by client, you would use
SELECT * FROM invoices GROUP BY client_id
The returned data is altered in that you've now aggregated the information instead of returning just the specific itemized records. So where the database might have seven invoices for one client, the GROUP BY would return all seven of those records as one. I did not discuss the idea in the preceding chapter because you will normally use one of several grouping (or aggregate) functions in conjunction with GROUP BY. Table 6.6 lists these.
Table 6.6. The grouping, or aggregate, functions are normally used with the GROUP BY clause in a SQL query.
Function and Usage
Returns the smallest value from the column.
Returns the largest value from the column.
Returns the sum of all of the values in the column.
Counts the number of rows.
Returns a concatenation of the grouped values.
You can apply combinations of WHERE, ORDER BY, and LIMIT conditions to a GROUP BY, normally structuring your query like this:
SELECT columns FROM tablename WHERE clause GROUP BY columnname ORDER BY columnname LIMIT x
To group data:
Find the largest invoice amount (Figure 6.22
Figure 6.22. The MAX() function performs a type of grouping, even if the GROUP BY clause is not formally stated.
SELECT MAX(invoice_amount) FROM invoices;
Since the MAX()
function returns the largest value for a column, this is the easiest way to return the desired result. Another option would be to write
SELECT invoice_amount FROM invoices ORDER BY invoice_amount DESC LIMIT 1
Determine how much has been spent under each expense category (Figure 6.23
Figure 6.23. The SUM() function is used here with a GROUP BY clause to total up fields with similar expense_category_ids.
SELECT SUM(expense_amount), expense_category FROM expenses LEFT JOIN expense_categories
USING (expense_category_id) GROUP BY (expenses.expense_category_id);
To accomplish this task, I first use a left join to incorporate the name of the expense category into the results. Then I simply group all of the expenses together by category ID and summarize their respective amounts. MySQL will return a table of two columns: the total and the category.
See how many invoices have been billed to each client (Figure 6.24
Figure 6.24. The COUNT() function returns the number of records that apply to the GROUP BY criteria (here, client_id).
SELECT COUNT(*) AS num, client_name
FROM invoices LEFT JOIN clients
GROUP BY (clients.client_id)
ORDER BY num DESC;
Whenever you need to determine how many records fall into a certain category, a combination of COUNT()
and GROUP BY
will get the desired result. With grouping, you can order the results as you would with any other query.
Alter the query in Step 2 so that it reflects how many invoices are tied into each total amount (Figure 6.25
Figure 6.25. By adding COUNT() to the query inFigure 6.23, I can also show how many expenses are included in each SUM() column.
SELECT COUNT(*), SUM(expense_amount),
expense_category FROM expenses LEFT JOIN expense_categories
GROUP BY (expenses.expense_category_id);
Here I've used the COUNT()
function again, along with an alias, to count each record in each grouping. You can apply multiple aggregate functions within the same query, although that syntax can get tricky.
NULL is a peculiar value, as you've seen, and it's interesting to know that GROUP BY will group NULL values together, since they have the same nonvalue.
When used as COUNT(columnname), only non-null values will be counted. When you use COUNT(*), all rows will be counted.
The GROUP BY clause, and the functions listed here, take some time to figure out, and MySQL will report an error whenever your syntax is inapplicable. Experiment within the mysql client to determine the exact wording of any query you might want to run in an application.
You can optionally use a WITH ROLLUP modifier in a GROUP BY query. It will summarize grouped columns for you (Figure 6.26).
Figure 6.26. If you add WITH ROLLUP to the query in Figure 6.25, MySQL will summarize the total number of expenses and the total amount of expenses. The last listed record reflects these totals. (The word Travel under expense_category is actually meaningless on that line.)
To conclude this chapter, I'll discuss two final functions that do not fit neatly into any of the earlier categories. The first, LAST_INSERT_ID(), is critical for working within a relational database. The second, DISTINCT(), assists you in adjusting what results to return from a table.
LAST_INSERT_ID() is a function that returns the value set by the last INSERT statement for a column that's automatically incremented. For example, in the accounting database example, the expense_categories table uses an expense_category_id column that was defined as a not-null, unsigned integer that was the primary key and would be auto-incremented. This meant that every time a NULL value was entered for that column, MySQL would automatically use the next logical value. LAST_INSERT_ID() will return that value. You can use it like so:
To use LAST_INSERT_ID():
Insert another user into the logins
table (Figure 6.27
Figure 6.27. To test LAST_INSERT_ID() (Figure 6.28), I'll add another record to the logins table.
INSERT INTO logins
(client_id, login_name, login_pass) VALUES
(4, AES_ENCRYPT('homerjsimpson', 'w1cKet'), SHA1('D\'ohReMi'));
Make sure when working with encryption that you use the same encryption function and salt for every record in the table, or else you will have difficulty accurately retrieving data later.
Retrieve that record's login_id
Figure 6.28. LAST_INSERT_ID() is a specific function that returns only the number corresponding to the previous insert.
This command returns just one value in one column, which reflects the primary key inserted in Step 1. When using relational databases, use LAST_INSERT_ID()
to ensure proper primary keytoforeign key integrity.
A common misunderstanding about LAST_INSERT_ID() is that it will return the last inserted value into the table, regardless of where it comes from. This is not true. The function will return only the last inserted ID for a query made by the current connection. Therefore, if ten scripts and three mysql clients are all connected to a database at the same time, and each inserts a value into a table and then recalls this value, each script or client will return only the ID correlating to that session's previous insert.
The DISTINCT() function is used to weed out duplicate values and is normally applied to a column.
SELECT DISTINCT(columnname) FROM tablename
This is frequently used in conjunction with GROUP BY so that the number of unique records, based upon a category, can be retrieved and possibly counted.
To use DISTINCT():
List the different clients that have been billed (Figure 6.29
Figure 6.29. DISTINCT() is somewhat like the aggregate functions in that it helps to group the returned records.
client_name FROM invoices, clients
WHERE invoices.client_id = clients.client_id;
Instead of listing every client for every invoice, this query will list only each unique client. It also rules out any clients that have not yet been billed (the join accomplishes that).
Count how many different clients have been billed (Figure 6.30
Figure 6.30. Using DISTINCT() along with COUNT() returns the number of unique values in a table.
The combination of the COUNT() and DISTINCT() functions returns just the one number, rather than every applicable record.
There are many other miscellaneous MySQL functions, such as DATABASE(). If you were to run the query SELECT DATABASE(), it would tell you which database is currently being used.
The USER() function will tell you which user is currently being used. This is a combination of username@host.
If you're familiar with the concept of castingforcibly changing the type of a piece of data, possibly changing the value of the data in the processrest assured that MySQL has several casting functions available. See the manual for specifics.