SELECT
statement is used to pull information from a table. The general form of the statement is: SELECTwhat_to_select
FROMwhich_table
WHEREconditions_to_satisfy
;
what_to_select
indicates what you want to see. This can be a list of columns, or *
to indicate “all columns.” which_table
indicates the table from which you want to retrieve data. The WHERE
clause is optional. If it is present, conditions_to_satisfy
specifies one or more conditions that rows must satisfy to qualify for retrieval. 1. Selecting All Data
The simplest form ofSELECT
retrieves everything from a table: mysql> SELECT * FROM pet;
+----------+--------+---------+------+------------+------------+
| name | owner | species | sex | birth | death |
+----------+--------+---------+------+------------+------------+
| Fluffy | Harold | cat | f | 1993-02-04 | NULL |
| Claws | Gwen | cat | m | 1994-03-17 | NULL |
| Buffy | Harold | dog | f | 1989-05-13 | NULL |
| Fang | Benny | dog | m | 1990-08-27 | NULL |
| Bowser | Diane | dog | m | 1979-08-31 | 1995-07-29 |
| Chirpy | Gwen | bird | f | 1998-09-11 | NULL |
| Whistler | Gwen | bird | NULL | 1997-12-09 | NULL |
| Slim | Benny | snake | m | 1996-04-29 | NULL |
| Puffball | Diane | hamster | f | 1999-03-30 | NULL |
+----------+--------+---------+------+------------+------------+
This form of SELECT
is useful if you want to review your entire table, for example, after you've just loaded it with your initial data set. For example, you may happen to think that the birth date for Bowser doesn't seem quite right. Consulting your original pedigree papers, you find that the correct birth year should be 1989, not 1979. There are at least two ways to fix this:
- Edit the file
pet.txt
to correct the error, then empty the table and reload it usingDELETE
andLOAD DATA
:
mysql>
However, if you do this, you must also re-enter the record for Puffball.DELETE FROM pet;
mysql>LOAD DATA LOCAL INFILE 'pet.txt' INTO TABLE pet;
- Fix only the erroneous record with an
UPDATE
statement:
mysql>
TheUPDATE pet SET birth = '1989-08-31' WHERE name = 'Bowser';
UPDATE
changes only the record in question and does not require you to reload the table.
Selecting Particular Rows
As shown in the preceding section, it is easy to retrieve an entire table. Just omit theWHERE
clause from theSELECT
statement. But typically you don't want to see the entire table, particularly when it becomes large. Instead, you're usually more interested in answering a particular question, in which case you specify some constraints on the information you want. Let's look at some selection queries in terms of questions about your pets that they answer.
You can select only particular rows from your table. For example, if you want to verify the change that you made to Bowser's birth date, select Bowser's record like this:
mysql>
The output confirms that the year is correctly recorded as 1989, not 1979.SELECT * FROM pet WHERE name = 'Bowser';
+--------+-------+---------+------+------------+------------+ | name | owner | species | sex | birth | death | +--------+-------+---------+------+------------+------------+ | Bowser | Diane | dog | m | 1989-08-31 | 1995-07-29 | +--------+-------+---------+------+------------+------------+
String comparisons normally are case-insensitive, so you can specify the name as'bowser'
,'BOWSER'
, and so forth. The query result is the same.
You can specify conditions on any column, not justname
. For example, if you want to know which animals were born during or after 1998, test thebirth
column:
mysql>
You can combine conditions, for example, to locate female dogs:SELECT * FROM pet WHERE birth >= '1998-1-1';
+----------+-------+---------+------+------------+-------+ | name | owner | species | sex | birth | death | +----------+-------+---------+------+------------+-------+ | Chirpy | Gwen | bird | f | 1998-09-11 | NULL | | Puffball | Diane | hamster | f | 1999-03-30 | NULL | +----------+-------+---------+------+------------+-------+
mysql>
The preceding query uses theSELECT * FROM pet WHERE species = 'dog' AND sex = 'f';
+-------+--------+---------+------+------------+-------+ | name | owner | species | sex | birth | death | +-------+--------+---------+------+------------+-------+ | Buffy | Harold | dog | f | 1989-05-13 | NULL | +-------+--------+---------+------+------------+-------+AND
logical operator. There is also anOR
operator:
mysql>
SELECT * FROM pet WHERE species = 'snake' OR species = 'bird';
+----------+-------+---------+------+------------+-------+ | name | owner | species | sex | birth | death | +----------+-------+---------+------+------------+-------+ | Chirpy | Gwen | bird | f | 1998-09-11 | NULL | | Whistler | Gwen | bird | NULL | 1997-12-09 | NULL | | Slim | Benny | snake | m | 1996-04-29 | NULL | +----------+-------+---------+------+------------+-------+AND
andOR
may be intermixed, althoughAND
has higher precedence thanOR
. If you use both operators, it is a good idea to use parentheses to indicate explicitly how conditions should be grouped:
mysql>
SELECT * FROM pet WHERE (species = 'cat' AND sex = 'm')
->OR (species = 'dog' AND sex = 'f');
+-------+--------+---------+------+------------+-------+ | name | owner | species | sex | birth | death | +-------+--------+---------+------+------------+-------+ | Claws | Gwen | cat | m | 1994-03-17 | NULL | | Buffy | Harold | dog | f | 1989-05-13 | NULL | +-------+--------+---------+------+------------+-------+3. Selecting Particular Columns
If you do not want to see entire rows from your table, just name the columns in which you are interested, separated by commas. For example, if you want to know when your animals were born, select thename
andbirth
columns:
mysql>
To find out who owns pets, use this query:SELECT name, birth FROM pet;
+----------+------------+ | name | birth | +----------+------------+ | Fluffy | 1993-02-04 | | Claws | 1994-03-17 | | Buffy | 1989-05-13 | | Fang | 1990-08-27 | | Bowser | 1989-08-31 | | Chirpy | 1998-09-11 | | Whistler | 1997-12-09 | | Slim | 1996-04-29 | | Puffball | 1999-03-30 | +----------+------------+
mysql>
Notice that the query simply retrieves theSELECT owner FROM pet;
+--------+ | owner | +--------+ | Harold | | Gwen | | Harold | | Benny | | Diane | | Gwen | | Gwen | | Benny | | Diane | +--------+owner
column from each record, and some of them appear more than once. To minimize the output, retrieve each unique output record just once by adding the keywordDISTINCT
:
mysql>
You can use aSELECT DISTINCT owner FROM pet;
+--------+ | owner | +--------+ | Benny | | Diane | | Gwen | | Harold | +--------+WHERE
clause to combine row selection with column selection. For example, to get birth dates for dogs and cats only, use this query:
mysql>
SELECT name, species, birth FROM pet
->WHERE species = 'dog' OR species = 'cat';
+--------+---------+------------+ | name | species | birth | +--------+---------+------------+ | Fluffy | cat | 1993-02-04 | | Claws | cat | 1994-03-17 | | Buffy | dog | 1989-05-13 | | Fang | dog | 1990-08-27 | | Bowser | dog | 1989-08-31 | +--------+---------+------------+
4. Sorting Rows
You may have noticed in the preceding examples that the result rows are displayed in no particular order. It is often easier to examine query output when the rows are sorted in some meaningful way. To sort a result, use anORDER BY
clause.
Here are animal birthdays, sorted by date:
mysql>
On character type columns, sorting — like all other comparison operations — is normally performed in a case-insensitive fashion. This means that the order is undefined for columns that are identical except for their case. You can force a case-sensitive sort for a column by usingSELECT name, birth FROM pet ORDER BY birth;
+----------+------------+ | name | birth | +----------+------------+ | Buffy | 1989-05-13 | | Bowser | 1989-08-31 | | Fang | 1990-08-27 | | Fluffy | 1993-02-04 | | Claws | 1994-03-17 | | Slim | 1996-04-29 | | Whistler | 1997-12-09 | | Chirpy | 1998-09-11 | | Puffball | 1999-03-30 | +----------+------------+BINARY
like so:ORDER BY BINARY
.col_name
The default sort order is ascending, with smallest values first. To sort in reverse (descending) order, add theDESC
keyword to the name of the column you are sorting by:
mysql>
You can sort on multiple columns, and you can sort different columns in different directions. For example, to sort by type of animal in ascending order, then by birth date within animal type in descending order (youngest animals first), use the following query:SELECT name, birth FROM pet ORDER BY birth DESC;
+----------+------------+ | name | birth | +----------+------------+ | Puffball | 1999-03-30 | | Chirpy | 1998-09-11 | | Whistler | 1997-12-09 | | Slim | 1996-04-29 | | Claws | 1994-03-17 | | Fluffy | 1993-02-04 | | Fang | 1990-08-27 | | Bowser | 1989-08-31 | | Buffy | 1989-05-13 | +----------+------------+
mysql>
TheSELECT name, species, birth FROM pet
->ORDER BY species, birth DESC;
+----------+---------+------------+ | name | species | birth | +----------+---------+------------+ | Chirpy | bird | 1998-09-11 | | Whistler | bird | 1997-12-09 | | Claws | cat | 1994-03-17 | | Fluffy | cat | 1993-02-04 | | Fang | dog | 1990-08-27 | | Bowser | dog | 1989-08-31 | | Buffy | dog | 1989-05-13 | | Puffball | hamster | 1999-03-30 | | Slim | snake | 1996-04-29 | +----------+---------+------------+DESC
keyword applies only to the column name immediately preceding it (birth
); it does not affect thespecies
column sort order.
5. Date Calculations
MySQL provides several functions that you can use to perform calculations on dates, for example, to calculate ages or extract parts of dates.
To determine how many years old each of your pets is, compute the difference in the year part of the current date and the birth date, then subtract one if the current date occurs earlier in the calendar year than the birth date. The following query shows, for each pet, the birth date, the current date, and the age in years.
mysql>
Here,SELECT name, birth, CURDATE(),
->(YEAR(CURDATE())-YEAR(birth))
->- (RIGHT(CURDATE(),5)
->AS age
->FROM pet;
+----------+------------+------------+------+ | name | birth | CURDATE() | age | +----------+------------+------------+------+ | Fluffy | 1993-02-04 | 2003-08-19 | 10 | | Claws | 1994-03-17 | 2003-08-19 | 9 | | Buffy | 1989-05-13 | 2003-08-19 | 14 | | Fang | 1990-08-27 | 2003-08-19 | 12 | | Bowser | 1989-08-31 | 2003-08-19 | 13 | | Chirpy | 1998-09-11 | 2003-08-19 | 4 | | Whistler | 1997-12-09 | 2003-08-19 | 5 | | Slim | 1996-04-29 | 2003-08-19 | 7 | | Puffball | 1999-03-30 | 2003-08-19 | 4 | +----------+------------+------------+------+YEAR()
pulls out the year part of a date andRIGHT()
pulls off the rightmost five characters that represent theMM-DD
(calendar year) part of the date. The part of the expression that compares theMM-DD
values evaluates to 1 or 0, which adjusts the year difference down a year ifCURDATE()
occurs earlier in the year thanbirth
. The full expression is somewhat ungainly, so an alias (age
) is used to make the output column label more meaningful.
The query works, but the result could be scanned more easily if the rows were presented in some order. This can be done by adding anORDER BY name
clause to sort the output by name:
mysql>
To sort the output bySELECT name, birth, CURDATE(),
->(YEAR(CURDATE())-YEAR(birth))
->- (RIGHT(CURDATE(),5)
->AS age
->FROM pet ORDER BY name;
+----------+------------+------------+------+ | name | birth | CURDATE() | age | +----------+------------+------------+------+ | Bowser | 1989-08-31 | 2003-08-19 | 13 | | Buffy | 1989-05-13 | 2003-08-19 | 14 | | Chirpy | 1998-09-11 | 2003-08-19 | 4 | | Claws | 1994-03-17 | 2003-08-19 | 9 | | Fang | 1990-08-27 | 2003-08-19 | 12 | | Fluffy | 1993-02-04 | 2003-08-19 | 10 | | Puffball | 1999-03-30 | 2003-08-19 | 4 | | Slim | 1996-04-29 | 2003-08-19 | 7 | | Whistler | 1997-12-09 | 2003-08-19 | 5 | +----------+------------+------------+------+age
rather thanname
, just use a differentORDER BY
clause:
mysql>
A similar query can be used to determine age at death for animals that have died. You determine which animals these are by checking whether theSELECT name, birth, CURDATE(),
->(YEAR(CURDATE())-YEAR(birth))
->- (RIGHT(CURDATE(),5)
->AS age
->FROM pet ORDER BY age;
+----------+------------+------------+------+ | name | birth | CURDATE() | age | +----------+------------+------------+------+ | Chirpy | 1998-09-11 | 2003-08-19 | 4 | | Puffball | 1999-03-30 | 2003-08-19 | 4 | | Whistler | 1997-12-09 | 2003-08-19 | 5 | | Slim | 1996-04-29 | 2003-08-19 | 7 | | Claws | 1994-03-17 | 2003-08-19 | 9 | | Fluffy | 1993-02-04 | 2003-08-19 | 10 | | Fang | 1990-08-27 | 2003-08-19 | 12 | | Bowser | 1989-08-31 | 2003-08-19 | 13 | | Buffy | 1989-05-13 | 2003-08-19 | 14 | +----------+------------+------------+------+death
value isNULL
. Then, for those with non-NULL
values, compute the difference between thedeath
andbirth
values:
mysql>
The query usesSELECT name, birth, death,
->(YEAR(death)-YEAR(birth)) - (RIGHT(death,5)
->AS age
->FROM pet WHERE death IS NOT NULL ORDER BY age;
+--------+------------+------------+------+ | name | birth | death | age | +--------+------------+------------+------+ | Bowser | 1989-08-31 | 1995-07-29 | 5 | +--------+------------+------------+------+death IS NOT NULL
rather thandeath <> NULL
becauseNULL
is a special value that cannot be compared using the usual comparison operators. This is discussed later. See “Working withNULL
Values”.
What if you want to know which animals have birthdays next month? For this type of calculation, year and day are irrelevant; you simply want to extract the month part of thebirth
column. MySQL provides several functions for extracting parts of dates, such asYEAR()
,MONTH()
, andDAYOFMONTH()
.MONTH()
is the appropriate function here. To see how it works, run a simple query that displays the value of bothbirth
andMONTH(birth)
:
mysql>
Finding animals with birthdays in the upcoming month is also simple. Suppose that the current month is April. Then the month value isSELECT name, birth, MONTH(birth) FROM pet;
+----------+------------+--------------+ | name | birth | MONTH(birth) | +----------+------------+--------------+ | Fluffy | 1993-02-04 | 2 | | Claws | 1994-03-17 | 3 | | Buffy | 1989-05-13 | 5 | | Fang | 1990-08-27 | 8 | | Bowser | 1989-08-31 | 8 | | Chirpy | 1998-09-11 | 9 | | Whistler | 1997-12-09 | 12 | | Slim | 1996-04-29 | 4 | | Puffball | 1999-03-30 | 3 | +----------+------------+--------------+4
and you can look for animals born in May (month5
) like this:
mysql>
There is a small complication if the current month is December. You cannot merely add one to the month number (SELECT name, birth FROM pet WHERE MONTH(birth) = 5;
+-------+------------+ | name | birth | +-------+------------+ | Buffy | 1989-05-13 | +-------+------------+12
) and look for animals born in month13
, because there is no such month. Instead, you look for animals born in January (month1
).
You can write the query so that it works no matter what the current month is, so that you do not have to use the number for a particular month.DATE_ADD()
allows you to add a time interval to a given date. If you add a month to the value ofCURDATE()
, then extract the month part withMONTH()
, the result produces the month in which to look for birthdays:
mysql>
A different way to accomplish the same task is to addSELECT name, birth FROM pet
->WHERE MONTH(birth) = MONTH(DATE_ADD(CURDATE(),INTERVAL 1 MONTH));
1
to get the next month after the current one after using the modulo function (MOD
) to wrap the month value to0
if it is currently12
:
mysql>
SELECT name, birth FROM pet
->WHERE MONTH(birth) = MOD(MONTH(CURDATE()), 12) + 1;
MONTH()
returns a number between1
and12
. AndMOD(something,12)
returns a number between0
and11
. So the addition has to be after theMOD()
, otherwise we would go from November (11
) to January (1
).
6. Working with
TheNULL
ValuesNULL
value can be surprising until you get used to it. Conceptually,NULL
means “a missing unknown value” and it is treated somewhat differently from other values. To test forNULL
, you cannot use the arithmetic comparison operators such as=
,<
, or<>
. To demonstrate this for yourself, try the following query:
mysql>
Clearly you get no meaningful results from these comparisons. Use theSELECT 1 = NULL, 1 <> NULL, 1 < NULL, 1 > NULL;
+----------+-----------+----------+----------+ | 1 = NULL | 1 <> NULL | 1 < NULL | 1 > NULL | +----------+-----------+----------+----------+ | NULL | NULL | NULL | NULL | +----------+-----------+----------+----------+IS NULL
andIS NOT NULL
operators instead:
mysql>
In MySQL,SELECT 1 IS NULL, 1 IS NOT NULL;
+-----------+---------------+ | 1 IS NULL | 1 IS NOT NULL | +-----------+---------------+ | 0 | 1 | +-----------+---------------+0
orNULL
means false and anything else means true. The default truth value from a boolean operation is1
.
This special treatment ofNULL
is why, in the previous section, it was necessary to determine which animals are no longer alive usingdeath IS NOT NULL
instead ofdeath <> NULL
.
TwoNULL
values are regarded as equal in aGROUP BY
.
When doing anORDER BY
,NULL
values are presented first if you doORDER BY ... ASC
and last if you doORDER BY ... DESC
.
A common error when working withNULL
is to assume that it is not possible to insert a zero or an empty string into a column defined asNOT NULL
, but this is not the case. These are in fact values, whereasNULL
means “not having a value.” You can test this easily enough by usingIS
[NOT
]NULL
as shown:
mysql>
Thus it is entirely possible to insert a zero or empty string into aSELECT 0 IS NULL, 0 IS NOT NULL, '' IS NULL, '' IS NOT NULL;
+-----------+---------------+------------+----------------+ | 0 IS NULL | 0 IS NOT NULL | '' IS NULL | '' IS NOT NULL | +-----------+---------------+------------+----------------+ | 0 | 1 | 0 | 1 | +-----------+---------------+------------+----------------+NOT NULL
column, as these are in factNOT NULL
.
7. Pattern Matching
MySQL provides standard SQL pattern matching as well as a form of pattern matching based on extended regular expressions similar to those used by Unix utilities such as vi, grep, and sed.
SQL pattern matching allows you to use “_
” to match any single character and “%
” to match an arbitrary number of characters (including zero characters). In MySQL, SQL patterns are case-insensitive by default. Some examples are shown here. You do not use=
or<>
when you use SQL patterns; use theLIKE
orNOT LIKE
comparison operators instead.
To find names beginning with “b
”:
mysql>
To find names ending with “SELECT * FROM pet WHERE name LIKE 'b%';
+--------+--------+---------+------+------------+------------+ | name | owner | species | sex | birth | death | +--------+--------+---------+------+------------+------------+ | Buffy | Harold | dog | f | 1989-05-13 | NULL | | Bowser | Diane | dog | m | 1989-08-31 | 1995-07-29 | +--------+--------+---------+------+------------+------------+fy
”:
mysql>
To find names containing a “SELECT * FROM pet WHERE name LIKE '%fy';
+--------+--------+---------+------+------------+-------+ | name | owner | species | sex | birth | death | +--------+--------+---------+------+------------+-------+ | Fluffy | Harold | cat | f | 1993-02-04 | NULL | | Buffy | Harold | dog | f | 1989-05-13 | NULL | +--------+--------+---------+------+------------+-------+w
”:
mysql>
To find names containing exactly five characters, use five instances of the “SELECT * FROM pet WHERE name LIKE '%w%';
+----------+-------+---------+------+------------+------------+ | name | owner | species | sex | birth | death | +----------+-------+---------+------+------------+------------+ | Claws | Gwen | cat | m | 1994-03-17 | NULL | | Bowser | Diane | dog | m | 1989-08-31 | 1995-07-29 | | Whistler | Gwen | bird | NULL | 1997-12-09 | NULL | +----------+-------+---------+------+------------+------------+_
” pattern character:
mysql>
The other type of pattern matching provided by MySQL uses extended regular expressions. When you test for a match for this type of pattern, use theSELECT * FROM pet WHERE name LIKE '_____';
+-------+--------+---------+------+------------+-------+ | name | owner | species | sex | birth | death | +-------+--------+---------+------+------------+-------+ | Claws | Gwen | cat | m | 1994-03-17 | NULL | | Buffy | Harold | dog | f | 1989-05-13 | NULL | +-------+--------+---------+------+------------+-------+REGEXP
andNOT REGEXP
operators (orRLIKE
andNOT RLIKE
, which are synonyms).
The following list describes some characteristics of extended regular expressions:
To demonstrate how extended regular expressions work, the- “
.
” matches any single character.
- A character class “
[...]
” matches any character within the brackets. For example, “[abc]
” matches “a
”, “b
”, or “c
”. To name a range of characters, use a dash. “[a-z]
” matches any letter, whereas “[0-9]
” matches any digit.
- “
*
” matches zero or more instances of the thing preceding it. For example, “x*
” matches any number of “x
” characters, “[0-9]*
” matches any number of digits, and “.*
” matches any number of anything.
- A
REGEXP
pattern match succeeds if the pattern matches anywhere in the value being tested. (This differs from aLIKE
pattern match, which succeeds only if the pattern matches the entire value.)
- To anchor a pattern so that it must match the beginning or end of the value being tested, use “
^
” at the beginning or “$
” at the end of the pattern.
LIKE
queries shown previously are rewritten here to useREGEXP
.
To find names beginning with “b
”, use “^
” to match the beginning of the name:
mysql>
If you really want to force aSELECT * FROM pet WHERE name REGEXP '^b';
+--------+--------+---------+------+------------+------------+ | name | owner | species | sex | birth | death | +--------+--------+---------+------+------------+------------+ | Buffy | Harold | dog | f | 1989-05-13 | NULL | | Bowser | Diane | dog | m | 1989-08-31 | 1995-07-29 | +--------+--------+---------+------+------------+------------+REGEXP
comparison to be case sensitive, use theBINARY
keyword to make one of the strings a binary string. This query matches only lowercase “b
” at the beginning of a name:
mysql>
To find names ending with “SELECT * FROM pet WHERE name REGEXP BINARY '^b';
fy
”, use “$
” to match the end of the name:
mysql>
To find names containing a “SELECT * FROM pet WHERE name REGEXP 'fy$';
+--------+--------+---------+------+------------+-------+ | name | owner | species | sex | birth | death | +--------+--------+---------+------+------------+-------+ | Fluffy | Harold | cat | f | 1993-02-04 | NULL | | Buffy | Harold | dog | f | 1989-05-13 | NULL | +--------+--------+---------+------+------------+-------+w
”, use this query:
mysql>
Because a regular expression pattern matches if it occurs anywhere in the value, it is not necessary in the previous query to put a wildcard on either side of the pattern to get it to match the entire value like it would be if you used an SQL pattern.SELECT * FROM pet WHERE name REGEXP 'w';
+----------+-------+---------+------+------------+------------+ | name | owner | species | sex | birth | death | +----------+-------+---------+------+------------+------------+ | Claws | Gwen | cat | m | 1994-03-17 | NULL | | Bowser | Diane | dog | m | 1989-08-31 | 1995-07-29 | | Whistler | Gwen | bird | NULL | 1997-12-09 | NULL | +----------+-------+---------+------+------------+------------+
To find names containing exactly five characters, use “^
” and “$
” to match the beginning and end of the name, and five instances of “.
” in between:
mysql>
You could also write the previous query using theSELECT * FROM pet WHERE name REGEXP '^.....$';
+-------+--------+---------+------+------------+-------+ | name | owner | species | sex | birth | death | +-------+--------+---------+------+------------+-------+ | Claws | Gwen | cat | m | 1994-03-17 | NULL | | Buffy | Harold | dog | f | 1989-05-13 | NULL | +-------+--------+---------+------+------------+-------+{
(“repeat-n
}n
-times”) operator:
mysql>
SELECT * FROM pet WHERE name REGEXP '^.{5}$';
+-------+--------+---------+------+------------+-------+ | name | owner | species | sex | birth | death | +-------+--------+---------+------+------------+-------+ | Claws | Gwen | cat | m | 1994-03-17 | NULL | | Buffy | Harold | dog | f | 1989-05-13 | NULL | +-------+--------+---------+------+------------+-------+
8. Counting Rows
Databases are often used to answer the question, “How often does a certain type of data occur in a table?” For example, you might want to know how many pets you have, or how many pets each owner has, or you might want to perform various kinds of census operations on your animals.
Counting the total number of animals you have is the same question as “How many rows are in thepet
table?” because there is one record per pet.COUNT(*)
counts the number of rows, so the query to count your animals looks like this:
mysql>
Earlier, you retrieved the names of the people who owned pets. You can useSELECT COUNT(*) FROM pet;
+----------+ | COUNT(*) | +----------+ | 9 | +----------+COUNT()
if you want to find out how many pets each owner has:
mysql>
The preceding query usesSELECT owner, COUNT(*) FROM pet GROUP BY owner;
+--------+----------+ | owner | COUNT(*) | +--------+----------+ | Benny | 2 | | Diane | 2 | | Gwen | 3 | | Harold | 2 | +--------+----------+GROUP BY
to group all records for eachowner
. The use ofCOUNT()
in conjunction withGROUP BY
is useful for characterizing your data under various groupings. The following examples show different ways to perform animal census operations.
Number of animals per species:
mysql>
Number of animals per sex:SELECT species, COUNT(*) FROM pet GROUP BY species;
+---------+----------+ | species | COUNT(*) | +---------+----------+ | bird | 2 | | cat | 2 | | dog | 3 | | hamster | 1 | | snake | 1 | +---------+----------+
mysql>
(In this output,SELECT sex, COUNT(*) FROM pet GROUP BY sex;
+------+----------+ | sex | COUNT(*) | +------+----------+ | NULL | 1 | | f | 4 | | m | 4 | +------+----------+NULL
indicates that the sex is unknown.)
Number of animals per combination of species and sex:
mysql>
You need not retrieve an entire table when you useSELECT species, sex, COUNT(*) FROM pet GROUP BY species, sex;
+---------+------+----------+ | species | sex | COUNT(*) | +---------+------+----------+ | bird | NULL | 1 | | bird | f | 1 | | cat | f | 1 | | cat | m | 1 | | dog | f | 1 | | dog | m | 2 | | hamster | f | 1 | | snake | m | 1 | +---------+------+----------+COUNT()
. For example, the previous query, when performed just on dogs and cats, looks like this:
mysql>
Or, if you wanted the number of animals per sex only for animals whose sex is known:SELECT species, sex, COUNT(*) FROM pet
->WHERE species = 'dog' OR species = 'cat'
->GROUP BY species, sex;
+---------+------+----------+ | species | sex | COUNT(*) | +---------+------+----------+ | cat | f | 1 | | cat | m | 1 | | dog | f | 1 | | dog | m | 2 | +---------+------+----------+
mysql>
If you name columns to select in addition to theSELECT species, sex, COUNT(*) FROM pet
->WHERE sex IS NOT NULL
->GROUP BY species, sex;
+---------+------+----------+ | species | sex | COUNT(*) | +---------+------+----------+ | bird | f | 1 | | cat | f | 1 | | cat | m | 1 | | dog | f | 1 | | dog | m | 2 | | hamster | f | 1 | | snake | m | 1 | +---------+------+----------+COUNT()
value, aGROUP BY
clause should be present that names those same columns. Otherwise, the following occurs:
- If the
ONLY_FULL_GROUP_BY
SQL mode is enabled, an error occurs:
mysql>
SET sql_mode = 'ONLY_FULL_GROUP_BY';
Query OK, 0 rows affected (0.00 sec) mysql>SELECT owner, COUNT(*) FROM pet;
ERROR 1140 (42000): Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause - If
ONLY_FULL_GROUP_BY
is not enabled, the query is processed by treating all rows as a single group, but the value selected for each named column is indeterminate. The server is free to select the value from any row:
mysql>
SET sql_mode = '';
Query OK, 0 rows affected (0.00 sec) mysql>SELECT owner, COUNT(*) FROM pet;
+--------+----------+ | owner | COUNT(*) | +--------+----------+ | Harold | 8 | +--------+----------+ 1 row in set (0.00 sec)
3.3.4.9. Using More Than one Table
Thepet
table keeps track of which pets you have. If you want to record other information about them, such as events in their lives like visits to the vet or when litters are born, you need another table. What should this table look like? It needs to contain the following information:
Given these considerations, the- The pet name so that you know which animal each event pertains to.
- A date so that you know when the event occurred.
- A field to describe the event.
- An event type field, if you want to be able to categorize events.
CREATE TABLE
statement for theevent
table might look like this:
mysql>
As with theCREATE TABLE event (name VARCHAR(20), date DATE,
->type VARCHAR(15), remark VARCHAR(255));
pet
table, it is easiest to load the initial records by creating a tab-delimited text file containing the following information.
Load the records like this:name
date
type
remark
Fluffy
1995-05-15
litter
4 kittens, 3 female, 1 male
Buffy
1993-06-23
litter
5 puppies, 2 female, 3 male
Buffy
1994-06-19
litter
3 puppies, 3 female
Chirpy
1999-03-21
vet
needed beak straightened
Slim
1997-08-03
vet
broken rib
Bowser
1991-10-12
kennel
Fang
1991-10-12
kennel
Fang
1998-08-28
birthday
Gave him a new chew toy
Claws
1998-03-17
birthday
Gave him a new flea collar
Whistler
1998-12-09
birthday
First birthday
mysql>
Based on what you have learned from the queries that you have run on theLOAD DATA LOCAL INFILE 'event.txt' INTO TABLE event;
pet
table, you should be able to perform retrievals on the records in theevent
table; the principles are the same. But when is theevent
table by itself insufficient to answer questions you might ask?
Suppose that you want to find out the ages at which each pet had its litters. We saw earlier how to calculate ages from two dates. The litter date of the mother is in theevent
table, but to calculate her age on that date you need her birth date, which is stored in thepet
table. This means the query requires both tables:
mysql>
There are several things to note about this query:SELECT pet.name,
->(YEAR(date)-YEAR(birth)) - (RIGHT(date,5)
->remark
->FROM pet INNER JOIN event
->ON pet.name = event.name
->WHERE event.type = 'litter';
+--------+------+-----------------------------+ | name | age | remark | +--------+------+-----------------------------+ | Fluffy | 2 | 4 kittens, 3 female, 1 male | | Buffy | 4 | 5 puppies, 2 female, 3 male | | Buffy | 5 | 3 puppies, 3 female | +--------+------+-----------------------------+
You need not have two different tables to perform a join. Sometimes it is useful to join a table to itself, if you want to compare records in a table to other records in that same table. For example, to find breeding pairs among your pets, you can join the- The
FROM
clause joins two tables because the query needs to pull information from both of them.
- When combining (joining) information from multiple tables, you need to specify how records in one table can be matched to records in the other. This is easy because they both have a
name
column. The query uses anON
clause to match up records in the two tables based on thename
values.
The query uses anINNER JOIN
to combine the tables. AnINNER JOIN
allows for rows from either table to appear in the result if and only if both tables meet the conditions specified in theON
clause. In this example, theON
clause specifies that thename
column in thepet
table must match thename
column in theevent
table. If a name appears in one table but not the other, the row will not appear in the result because the condition in theON
clause fails.
- Because the
name
column occurs in both tables, you must be specific about which table you mean when referring to the column. This is done by prepending the table name to the column name.
pet
table with itself to produce candidate pairs of males and females of like species:
mysql>
In this query, we specify aliases for the table name to refer to the columns and keep straight which instance of the table each column reference is associated with.SELECT p1.name, p1.sex, p2.name, p2.sex, p1.species
->FROM pet AS p1 INNER JOIN pet AS p2
->ON p1.species = p2.species AND p1.sex = 'f' AND p2.sex = 'm';
+--------+------+--------+------+---------+ | name | sex | name | sex | species | +--------+------+--------+------+---------+ | Fluffy | f | Claws | m | cat | | Buffy | f | Fang | m | dog | | Buffy | f | Bowser | m | dog | +--------+------+--------+------+---------+
- “
No comments:
Post a Comment