この章では、 mysql
クライアントプログラムの使用の仕方と
単純なデータベースの作成と使用の仕方を例題としてとり上げます。
mysql
(``terminal monitor'' や ``monitor'' として参照されます)は
MySQL サーバーに接続できる対話モードのプログラムで、
クエリの実行や結果の表示が可能です。
mysql
はバッチモードも可能です: クエリをまずファイルに書いておき、
そしてそのファイルを mysql
に実行させます。
どちらの方法でも mysql
はこの章の説明でカバーされます。
mysql
の提供するオプションを見るには、 --help
オプションを
実行します:
shell> mysql --help
この章では mysql
が既にあなたのマシンにインストールされているものとし、
また MySQL サーバーに接続可能であるとします。 もしそうでないなら、
あなたの MySQL 管理者に連絡を取ってください。
(もしあなた自身が管理者なら、このマニュアルの他のセクションを参照してください。)
本章ではデータベース設定の全体のプロセス、データベースの使用について述べます。 もしあなたが既に存在しているデータベースにアクセスする事だけに興味があるなら、 この章のデータベース、テーブルの作成方法の説明を読み飛ばしてもかまいません。
この章が本質的にはチュートリアルであるので、多くの詳細は、かならず違う章にあります。 ここに書かれている事柄で、 さらに詳しい情報についてはマニュアルの適切なセクションを調べて下さい。
サーバーに mysql
コマンドから接続するためには、通常、
MySQL ユーザ名、パスワードを指定する必要があるでしょう。
もしあなたがログインしている以外のマシンの上でサーバーが走っていれば、
あなたは、ホスト名を明示する必要があるでしょう。
まずあなたの管理者に問い合わせて、あなたが使うべき接続のためのパラメーターを見つけ出して下さい。
(すなわち、どのようなホスト、ユーザー名とパスワードを使うべきなのか。)
適切なパラメーターを知っていれば、あなたは、以下のようにして接続できます:
shell> mysql -h host -u user -p Enter password: ********
********
とかかれている部分にはあなたのパスワードを入れます;
mysql
が Enter password:
プロンプトを出したときに。
きちんと動けば、いくつかの情報と mysql>
プロンプトが現れます:
shell> mysql -h host -u user -p Enter password: ******** Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 459 to server version: 3.22.20a-log Type 'help' for help. mysql>
このプロンプトは mysql
がコマンド入力を待っている状態を示します。
いくつかの MySQL インストレーションでは、ローカルホストで
実行されているサーバーに、ユーザーに``匿名''(名前無し)の接続を許しています。
もしあなたのマシンがこの場合に当てはまるなら、mysql
をオプション無しで
起動すべきです:
shell> mysql
接続が成功した後、 mysql>
プロンプトで、QUIT
とタイプすれば
いつでも接続を切ることができます:
mysql> QUIT Bye
Control-D で切断してもかまいません。
以下のセクションでのほとんどの例では、あなたがサーバーに接続しているものと
しています。その場合は mysql>
プロンプトによって、それを示しています。
前のセクションで述べたように、あなたがサーバーとつながっていることを確かめて下さい。
この状態ではまだどんなデータベースも選択していない状態です。しかしそれはかまいません。
ここでは、テーブルの作成やデータの入力、取り出しよりも、
いかにクエリを発行するかに重点を置きます。
このセクションでは、あなた自身を mysql
の動きに慣れさせるために、
あなたがやってみることができるであろう、いくつかのクエリを使用して、
コマンド入力の基本的な原則を述べます。
サーバーのバージョン、今日の日付をサーバーに問い合わせる、単純なコマンド
がここにあります。 以下のように mysql>
プロンプトの後に入力し
RETURN キーを押してみてください:
mysql> SELECT VERSION(), CURRENT_DATE; +--------------+--------------+ | version() | CURRENT_DATE | +--------------+--------------+ | 3.22.20a-log | 1999-03-19 | +--------------+--------------+ 1 row in set (0.01 sec) mysql>
このクエリは、mysql
のいくつかの事を説明しています:
QUIT
はそのうちの一つです。後で他も出てきます。)
mysql
はそれをサーバーに送り、結果を表示します。
その後、 mysql>
プロンプトを出し、他のコマンドが受け付けれる状態であると
示します。
mysql
はクエリの結果を表にして出します。 最初の行は項目のラベル名です。
その下の行に、クエリの結果が続きます。 通常、項目のラベル名はデータベースのテーブルから
あなたが取り出したフィールド名になります。 テーブルのフィールド名以外の式を
あなたが検索したのなら(ちょうど例のように)、mysql
は与えられた式自身を
項目のラベル名として表示します。
mysql
は何レコード(rows)返ってきたかを示し、実行にどれくらいの時間が
かかったかも示します。 これはおおざっぱなサーバーのパフォーマンスを示します。
これらの値は wall clock time (CPU や マシンタイムではなく) を使用しており、さらに、
サーバーの負荷、およびネットワーク待ち時間といった要因にも影響されるので、
不正確です。
(この章の他の例題においては ``rows in set'' の行は省きます。)
キーワードは、いかなる大文字小文字でも記述できます。 以下に示すクエリは同じです:
mysql> SELECT VERSION(), CURRENT_DATE; mysql> select version(), current_date; mysql> SeLeCt vErSiOn(), current_DATE;
以下のクエリは、mysql
を簡単な計算機として使用した例です:
mysql> SELECT SIN(PI()/4), (4+1)*5; +-------------+---------+ | SIN(PI()/4) | (4+1)*5 | +-------------+---------+ | 0.707107 | 25 | +-------------+---------+
今までは見てきたたコマンドは、比較的短い、一行のの文です。 あなたは一行に複数、文を書くことが出来ます。 それぞれの文の終わりにはセミコロンを付けます:
mysql> SELECT VERSION(); SELECT NOW(); +--------------+ | version() | +--------------+ | 3.22.20a-log | +--------------+ +---------------------+ | NOW() | +---------------------+ | 1999-03-19 00:15:33 | +---------------------+
コマンドは一行にかく必要もありません。 複数行にわたる長いコマンドでも問題ありません。
mysql
はセミコロンで、一文の終わりと見なします。 その行の最後を文の最後とは
見なしません。 (いうなれば、mysql
は自由な入力を許し、セミコロンが現れるまでは
それらの入力された行を実行しません)
複数行にまたがった文の例です:
mysql> SELECT -> USER() -> , -> CURRENT_DATE; +--------------------+--------------+ | USER() | CURRENT_DATE | +--------------------+--------------+ | joesmith@localhost | 1999-03-18 | +--------------------+--------------+
この例では、複数にまたがるクエリにおいて、最初の行の入力のあと、
いかにプロンプトが mysql>
から ->
に変わるかを示しています。
これは mysql
が、まだ文が終わっておらず、残りの部分の入力を待っている
ことを示します。 The prompt is your friend, because it provides
valuable feedback. If you use that feedback, you will always be aware of
what mysql
is waiting for.
入力中にコマンドの実行を取りやめようと思った場合、
\c
でこれをキャンセルできます:
mysql> SELECT -> USER() -> \c mysql>
Here, too, notice the prompt. It switches back to mysql>
after you
type \c
, providing feedback to indicate that mysql
is ready
for a new command.
The following table shows each of the prompts you may see and summarizes what
they mean about the state that mysql
is in:
Prompt | Meaning |
mysql> | 新しいコマンドの待状態 |
-> | 複数行にまたがるコマンドの、次の行の入力を待っている状態。 |
'> | シングルクオート (`'') で始められた文字の、次の行の入力を待っている状態。 |
"> | ダブルクオート (`"') で始められた文字の、次の行の入力を待っている状態。 |
あなたが文をセミコロンで終わらすことを忘れて実行した場合に、
複数行の入力待ちが起こります。 この場合、 mysql
は
次の入力を待ちます:
mysql> SELECT USER() ->
If this happens to you (you think you've entered a statement but the only
response is a ->
prompt), most likely mysql
is waiting for the
semicolon. If you don't notice what the prompt is telling you, you might sit
there for a while before realizing what you need to do. Enter a semicolon to
complete the statement, and mysql
will execute it:
mysql> SELECT USER() -> ; +--------------------+ | USER() | +--------------------+ | joesmith@localhost | +--------------------+
The '>
and ">
prompts occur during string collection.
In MySQL, you can write strings surrounded by either `''
or `"' characters (for example, 'hello'
or "goodbye"
),
and mysql
lets you enter strings that span multiple lines. When you
see a '>
or ">
prompt, it means that you've entered a line
containing a string that begins with a `'' or `"' quote character,
but have not yet entered the matching quote that terminates the string.
That's fine if you really are entering a multiple-line string, but how likely
is that? Not very. More often, the '>
and ">
prompts indicate
that you've inadvertantly left out a quote character. For example:
mysql> SELECT * FROM my_table WHERE name = "Smith AND age < 30; ">
If you enter this SELECT
statement, then hit RETURN and wait for the
result, nothing will happen. Instead of wondering why this
query takes so long, notice the clue provided by the ">
prompt. It
tells you that mysql
expects to see the rest of an unterminated
string. (Do you see the error in the statement? The string "Smith
is
missing the second quote.)
At this point, what do you do? The simplest thing is to cancel the command.
However, you cannot just type \c
in this case, because mysql
interprets it as part of the string that it is collecting! Instead, enter
the closing quote character (so mysql
knows you've finished the
string), then type \c
:
mysql> SELECT * FROM my_table WHERE name = "Smith AND age < 30; "> "\c mysql>
The prompt changes back to mysql>
, indicating that mysql
is ready for a new command.
It's important to know what the '>
and ">
prompts signify,
because if you mistakenly enter an unterminated string, any further lines you
type will appear to be ignored by mysql
-- including a line
containing QUIT
! This can be quite confusing, especially if you
don't know that you need to supply the terminating quote before you can
cancel the current command.
Now that you know how to enter commands, it's time to access a database.
Suppose you have several pets in your home (your menagerie) and you'd like to keep track of various types of information about them. You can do so by creating tables to hold your data and loading them with the desired information. Then you can answer different sorts of questions about your animals by retrieving data from the tables. This section shows you how to:
The menagerie database will be simple (deliberately), but it is not difficult to think of real-world situations in which a similar type of database might be used. For example, a database like this could be used by a farmer to keep track of livestock, or by a veterinarian to keep track of patient records.
Use the SHOW
statement to find out what databases currently exist
on the server:
mysql> SHOW DATABASES; +----------+ | Database | +----------+ | mysql | | test | | tmp | +----------+
The list of databases is probably different on your machine, but the
mysql
and test
databases are likely to be among them. The
mysql
database is required because it describes user access
privileges. The test
database is often provided as a workspace for
users to try things out.
If the test
database exists, try to access it:
mysql> USE test Database changed
Note that USE
, like QUIT
, does not require a semicolon. (You
can terminate such statements with a semicolon if you like; it does no harm.)
The USE
statement is special in another way, too: it must be given on
a single line.
You can use the test
database (if you have access to it) for the
examples that follow, but anything you create in that database can be
removed by anyone else with access to it. For this reason, you should
probably ask your MySQL administrator for permission to use a
database of your own. Suppose you want to call yours menagerie
. The
administrator needs to execute a command like this:
mysql> GRANT ALL ON menagerie.* TO your_mysql_name;
where your_mysql_name
is the MySQL user name assigned to
you.
If the administrator creates your database for you when setting up your permissions, you can begin using it. Otherwise, you need to create it yourself:
mysql> CREATE DATABASE menagerie;
Under Unix, database names are case sensitive (unlike SQL keywords), so you
must always refer to your database as menagerie
, not as
Menagerie
, MENAGERIE
, or some other variant. This is also true
for table names. (Under Windows, this restriction does not apply, although
you must refer to databases and tables using the same lettercase throughout a
given query.)
Creating a database does not select it for use; you must do that explicitly.
To make menagerie
the current database, use this command:
mysql> USE menagerie Database changed
Your database needs to be created only once, but you must select it for use
each time you begin a mysql
session. You can do this by issuing a
USE
statement as shown above. Alternatively, you can select the
database on the command line when you invoke mysql
. Just specify its
name after any connection parameters that you might need to provide. For
example:
shell> mysql -h host -u user -p menagerie Enter password: ********
Note that menagerie
is not your password on the command just shown.
If you want to supply your password on the command line after the -p
option, you must do so with no intervening space (for example, as
-pmypassword
, not as -p mypassword
). However, putting your
password on the command line is not recommended, because doing so exposes it
to snooping by other users logged in on your machine.
Creating the database is the easy part, but at this point it's empty, as
SHOW TABLES
will tell you:
mysql> SHOW TABLES; Empty set (0.00 sec)
The harder part is deciding what the structure of your database should be: what tables you will need and what columns will be in each of them.
You'll want a table that contains a record for each of your pets. This can
be called the pet
table, and it should contain, as a bare minimum,
each animal's name. Because the name by itself is not very interesting, the
table should contain other information. For example, if more than one person
in your family keeps pets, you might want to list each animal's owner. You
might also want to record some basic descriptive information such as species
and sex.
How about age? That might be of interest, but it's not a good thing to store in a database. Age changes as time passes, which means you'd have to update your records often. Instead, it's better to store a fixed value such as date of birth. Then, whenever you need age, you can calculate it as the difference between the current date and the birth date. MySQL provides functions for doing date arithmetic, so this is not difficult. Storing birth date rather than age has other advantages, too:
You can probably think of other types of information that would be useful in
the pet
table, but the ones identified so far are sufficient for now:
name, owner, species, sex, birth, and death.
Use a CREATE TABLE
statement to specify the layout of your table:
mysql> CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20), -> species VARCHAR(20), sex CHAR(1), birth DATE, death DATE);
VARCHAR
is a good choice for the name
, owner
, and
species
columns because the column values will vary in length. The
lengths of those columns need not all be the same, and need not be
20
. You can pick any length from 1
to 255
, whatever
seems most reasonable to you. (If you make a poor choice and it turns
out later that you need a longer field, MySQL provides an
ALTER TABLE
statement.)
Animal sex can be represented in a variety of ways, for example, "m"
and "f"
, or perhaps "male"
and "female"
. It's simplest
to use the single characters "m"
and "f"
.
The use of the DATE
data type for the birth
and death
columns is a fairly obvious choice.
Now that you have created a table, SHOW TABLES
should produce some
output:
mysql> SHOW TABLES; +---------------------+ | Tables in menagerie | +---------------------+ | pet | +---------------------+
To verify that your table was created the way you expected, use
a DESCRIBE
statement:
mysql> DESCRIBE pet; +---------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+-------+ | name | varchar(20) | YES | | NULL | | | owner | varchar(20) | YES | | NULL | | | species | varchar(20) | YES | | NULL | | | sex | char(1) | YES | | NULL | | | birth | date | YES | | NULL | | | death | date | YES | | NULL | | +---------+-------------+------+-----+---------+-------+
You can use DESCRIBE
any time, for example, if you forget the names of
the columns in your table or what types they are.
After creating your table, you need to populate it. The LOAD DATA
and
INSERT
statements are useful for this.
Suppose your pet records can be described as shown below.
(Observe that MySQL expects dates in YYYY-MM-DD
format;
this may be different than what you are used to.)
name | owner | species | sex | birth | death |
Fluffy | Harold | cat | f | 1993-02-04 | |
Claws | Gwen | cat | m | 1994-03-17 | |
Buffy | Harold | dog | f | 1989-05-13 | |
Fang | Benny | dog | m | 1990-08-27 | |
Bowser | Diane | dog | m | 1989-08-31 | 1995-07-29 |
Chirpy | Gwen | bird | f | 1998-09-11 | |
Whistler | Gwen | bird | 1997-12-09 | ||
Slim | Benny | snake | m | 1996-04-29 |
Because you are beginning with an empty table, an easy way to populate it is to create a text file containing a row for each of your animals, then load the contents of the file into the table with a single statement.
You could create a text file `pet.txt' containing one record per line,
with values separated by tabs, and given in the order in which the columns
were listed in the CREATE TABLE
statement. For missing values (such
as unknown sexes or death dates for animals that are still living), you can
use NULL
values. To represent these in your text file, use
\N
. For example, the record for Whistler the bird would look like
this (where the whitespace between values is a single tab character):
Whistler | Gwen | bird | \N | 1997-12-09 | \N
|
To load the text file `pet.txt' into the pet
table, use this
command:
mysql> LOAD DATA LOCAL INFILE "pet.txt" INTO TABLE pet;
You can specify the column value separator and end of line marker explicitly
in the LOAD DATA
statement if you wish, but the defaults are tab and
linefeed. These are sufficient for the statement to read the file
`pet.txt' properly.
When you want to add new records one at a time, the INSERT
statement
is useful. In its simplest form, you supply values for each column, in the
order in which the columns were listed in the CREATE TABLE
statement.
Suppose Diane gets a new hamster named Puffball. You could add a new record
using an INSERT
statement like this:
mysql> INSERT INTO pet -> VALUES ('Puffball','Diane','hamster','f','1999-03-30',NULL);
Note that string and date values are specified as quoted strings here. Also,
with INSERT
, you can insert NULL
directly to represent a
missing value. You do not use \N
like you do with LOAD DATA
.
From this example, you should be able to see that there would be a lot more
typing involved to load
your records initially using several INSERT
statements rather
than a single LOAD DATA
statement.
The SELECT
statement is used to pull information from a table.
The general form of the statement is:
SELECT what_to_select FROM which_table WHERE conditions_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's present, conditions_to_satisfy
specifies
conditions that rows must satisfy to qualify for retrieval.
The simplest form of SELECT
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 | 1998-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 instance, after you've just loaded it with your initial dataset. As it
happens, the output just shown reveals an error in your data file: Bowser
appears to have been born after he died! Consulting your original pedigree
papers, you find that the correct birth year is 1989, not 1998.
There are are least a couple of ways to fix this:
DELETE
and LOAD DATA
:
mysql> SET AUTOCOMMIT=1; # Used for quick re-create of the table mysql> DELETE FROM pet; mysql> LOAD DATA LOCAL INFILE "pet.txt" INTO TABLE pet;However, if you do this, you must also re-enter the record for Puffball.
UPDATE
statement:
mysql> UPDATE pet SET birth = "1989-08-31" WHERE name = "Bowser";
As shown above, it is easy to retrieve an entire table. But typically you don't want to do that, particularly when the table 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> SELECT * FROM pet WHERE name = "Bowser"; +--------+-------+---------+------+------------+------------+ | name | owner | species | sex | birth | death | +--------+-------+---------+------+------------+------------+ | Bowser | Diane | dog | m | 1989-08-31 | 1995-07-29 | +--------+-------+---------+------+------------+------------+
The output confirms that the year is correctly recorded now as 1989, not 1998.
String comparisons are normally case insensitive, so you can specify the
name as "bowser"
, "BOWSER"
, etc. The query result will be
the same.
You can specify conditions on any column, not just name
. For example,
if you want to know which animals were born after 1998, test the birth
column:
mysql> 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 | +----------+-------+---------+------+------------+-------+
You can combine conditions, for example, to locate female dogs:
mysql> SELECT * FROM pet WHERE species = "dog" AND sex = "f"; +-------+--------+---------+------+------------+-------+ | name | owner | species | sex | birth | death | +-------+--------+---------+------+------------+-------+ | Buffy | Harold | dog | f | 1989-05-13 | NULL | +-------+--------+---------+------+------------+-------+
The preceding query uses the AND
logical operator. There is also an
OR
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
and OR
may be intermixed. If you do that, it's a good idea
to use parentheses to indicate 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 | +-------+--------+---------+------+------------+-------+
If you don't want to see entire rows from your table, just name the columns
in which you're interested, separated by commas. For example, if you want to
know when your animals were born, select the name
and birth
columns:
mysql> 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 | +----------+------------+
To find out who owns pets, use this query:
mysql> SELECT owner FROM pet; +--------+ | owner | +--------+ | Harold | | Gwen | | Harold | | Benny | | Diane | | Gwen | | Gwen | | Benny | | Diane | +--------+
However, notice that the query simply retrieves the owner
field 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 keyword
DISTINCT
:
mysql> SELECT DISTINCT owner FROM pet; +--------+ | owner | +--------+ | Benny | | Diane | | Gwen | | Harold | +--------+
You can use a 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 | +--------+---------+------------+
You may have noticed in the preceding examples that the result rows are
displayed in no particular order. However, it's often easier to examine
query output when the rows are sorted in some meaningful way. To sort a
result, use an ORDER BY
clause.
Here are animal birthdays, sorted by date:
mysql> SELECT 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 | +----------+------------+
To sort in reverse order, add the DESC
(descending) keyword to the
name of the column you are sorting by:
mysql> 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 | +----------+------------+
You can sort on multiple columns. For example, to sort by type of animal, then by birth date within animal type with youngest animals first, use the following query:
mysql> SELECT 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 | +----------+---------+------------+
Note that the DESC
keyword applies only to the column name immediately
preceding it (birth
); species
values are still sorted in
ascending order.
MySQL はいくつかの日付の計算を行う関数を提供します。 例えば、年の計算や日付の部分を取り出したり。
ペットの年齢が何才なのかを知るためには、現在の日付と誕生日との差を計算 します。 このためには、二つの日付を日数に直し、差を取り、そして 365 (年の日数) で割ります:
mysql> SELECT name, (TO_DAYS(NOW())-TO_DAYS(birth))/365 FROM pet; +----------+-------------------------------------+ | name | (TO_DAYS(NOW())-TO_DAYS(birth))/365 | +----------+-------------------------------------+ | Fluffy | 6.15 | | Claws | 5.04 | | Buffy | 9.88 | | Fang | 8.59 | | Bowser | 9.58 | | Chirpy | 0.55 | | Whistler | 1.30 | | Slim | 2.92 | | Puffball | 0.00 | +----------+-------------------------------------+
クエリは動きましたが、改良したいくつかのことがあります。 第1に、この結果は、もしレコードにいくつかの順番を与えれば、簡単に並び換えが 出来そうなこと。 第2に、年齢欄の見出しは、このままではなんなのか分かりません。
最初の問題は ORDER BY name
節を加えることで name 順で並び換えが出来ます。
二つ目の問題に対しては、違う見出しを出力に現れるように、欄の見出しを
扱います (これを フィールドのエイリアス と呼びます) :
mysql> SELECT name, (TO_DAYS(NOW())-TO_DAYS(birth))/365 AS age -> FROM pet ORDER BY name; +----------+------+ | name | age | +----------+------+ | Bowser | 9.58 | | Buffy | 9.88 | | Chirpy | 0.55 | | Claws | 5.04 | | Fang | 8.59 | | Fluffy | 6.15 | | Puffball | 0.00 | | Slim | 2.92 | | Whistler | 1.30 | +----------+------+
name
ではなく age
で並び換えるには、 ORDER BY
節を
以下のように変えます:
mysql> SELECT name, (TO_DAYS(NOW())-TO_DAYS(birth))/365 AS age -> FROM pet ORDER BY age; +----------+------+ | name | age | +----------+------+ | Puffball | 0.00 | | Chirpy | 0.55 | | Whistler | 1.30 | | Slim | 2.92 | | Claws | 5.04 | | Fluffy | 6.15 | | Fang | 8.59 | | Bowser | 9.58 | | Buffy | 9.88 | +----------+------+
似たクエリが、既に死んでしまった動物の、死んだ時点の年齢を調べるのにも
使用できます。 どの動物が死んでいるかどうかは、death
値が NULL
か
どうかで判断します。 これらの値が 非NULL
ならば、 death
と birth
の差の計算を行います:
mysql> SELECT name, birth, death, (TO_DAYS(death)-TO_DAYS(birth))/365 AS age -> FROM pet WHERE death IS NOT NULL ORDER BY age; +--------+------------+------------+------+ | name | birth | death | age | +--------+------------+------------+------+ | Bowser | 1989-08-31 | 1995-07-29 | 5.91 | +--------+------------+------------+------+
このクエリでは、 death != NULL
ではなく death IS NOT NULL
を
使用します。 なぜなら、 NULL
は特別な値だからです。 これは後で解説します。
「9.3.4.6 NULL
値の振る舞い」節参照.
もしあなたが翌月誕生日の動物を知りたいとします。 この種の計算では、
年と日が関係ありません。 birth
フィールドの月だけを単に
取り出したくなるでしょう。 MySQL は日付の部分を取り出すいくつかの関数を
提供します。 YEAR()
, MONTH()
, DAYOFMONTH()
など。
ここでは MONTH()
がいいでしょう。 これがどのように動くか確かめるには、
birth
と MONTH(birth)
の両方を表示させるクエリを実行してみます:
mysql> SELECT 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月だとします。 すると、5月生まれの動物を探します:
mysql> SELECT name, birth FROM pet WHERE MONTH(birth) = 5; +-------+------------+ | name | birth | +-------+------------+ | Buffy | 1989-05-13 | +-------+------------+
もし現在が12月なら、ちょっと困るかもしれません。 月の数字 (12
) に
1 を足して 13 月生まれの動物を探すわけにはいきません。 かわりに1月生まれの
動物を探します。
現在が何月なのかを気にすることなく、動作するクエリを書くことが可能です。
この方法では、あなたは月の数字をクエリに使用することがありません。
DATE_ADD()
関数は与えた日付からある時間を足すことが可能です。
もし現時点 NOW()
の値に 1ヶ月 足し、 MONTH()
で月の
部分を取り出し、その結果を、誕生月を調べるために使用します:
mysql> SELECT name, birth FROM pet -> WHERE MONTH(birth) = MONTH(DATE_ADD(NOW(), INTERVAL 1 MONTH));
同じことができる別の方法として、剰余関数 を使用した結果に 1 を足す方法があります。
(もし今月が12月なら、剰余関数 (MOD
) を使用した結果は 0
になります):
mysql> SELECT name, birth FROM pet -> WHERE MONTH(birth) = MOD(MONTH(NOW()), 12) + 1;
Note that MONTH
returns a number between 1 and 12. And
MOD(something,12)
returns a number between 0 and 11. So the
addition has to be after the MOD()
otherwise we would go from
November (11) to January (1).
NULL
値の振る舞い
あなたがそれに慣れるまでは、NULL
値には驚くかもしれません。
概念的に、NULL
値は ``値無し'' か ``未知な値'' を意味し、
他の値よりもいくぶん異なって扱われます。
NULL
の評価の際には、数値の比較演算子 =
, <
, !=
を
使用することは出来ません。 あなた自身これをデモするために、以下のようにしてみてください:
mysql> SELECT 1 = NULL, 1 != NULL, 1 < NULL, 1 > NULL; +----------+-----------+----------+----------+ | 1 = NULL | 1 != NULL | 1 < NULL | 1 > NULL | +----------+-----------+----------+----------+ | NULL | NULL | NULL | NULL | +----------+-----------+----------+----------+
確実にこれらの比較からいかなる意味がある結果も受け取りません。
この代わりに、 IS NULL
, IS NOT NULL
演算子を使用します:
mysql> SELECT 1 IS NULL, 1 IS NOT NULL; +-----------+---------------+ | 1 IS NULL | 1 IS NOT NULL | +-----------+---------------+ | 0 | 1 | +-----------+---------------+
MySQL では, 0 や NULL
は false(偽) で、それ以外全てが true(真)です。
The default trueth value from a boolean operation is 1.
この NULL
の特別な扱いはなぜなのか。 前のセクションを見てください。
どの動物がもはや生きていないことを決定づけるためには、
death != NULL
ではなく death IS NOT NULL
の使用が
必要とされるのです。
MySQL は標準SQLのパターン・マッチを提供します。
また、拡張された正規表現を基にした形式のパターン・マッチ、
これは vi
, grep
, sed
など UNIX のユーティリティに
使用されている物と似ていますが、これも提供します。
SQL パターン・マッチは、 `_' でいかなる1文字とも一致し、
`%' であらゆる0こ以上の文字列に一致します。
MySQL では、デフォルトで SQL パターンはケース非依存です。
いくつか例を以下に示します。 SQLパターンを使用する場合、 =
や
!=
を使用しないように注意してください。 LIKE
か NOT LIKE
を代わりに使用します。
`b' から始まる名前を見つけるには:
mysql> 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> 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> 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 | +----------+-------+---------+------+------------+------------+
5文字の名前を見つけるには, `_' パターン文字を使用して:
mysql> SELECT * 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 | +-------+--------+---------+------+------------+-------+
MySQL では他のパターン・マッチの形も提供されます。正規表現の拡張を使用して。
これらのパターン・マッチをテストする際には、REGEXP
と NOT REGEXP
演算子(あるいはRLIKE
, NOT RLIKE
演算子。これらは同義です)
を使用します。
拡張正規表現のいくつかの特徴:
以下は拡張された正規表現がどう動くかの例です。
上に示した LIKE
のクエリは REGEXP
で以下のように書き換えれます:
`b' で始まる名前をみつけるには、`^' で名前の頭にマッチさせ、 かつ `[bB]' として、大文字小文字にもマッチさせます:
mysql> SELECT * 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 | +--------+--------+---------+------+------------+------------+
Prior to MySQL Version 3.23.4, REGEXP
is case sensitive,
and the previous query will return no rows. To match either lowercase or
uppercase `b', use this query instead:
mysql> SELECT * FROM pet WHERE name REGEXP "^[bB]";
From MySQL 3.23.4 on, to force a REGEXP
comparison to
be case sensitive, use the BINARY
keyword to make one of the
strings a binary string. This query will match only lowercase `b'
at the beginning of a name:
mysql> SELECT * FROM pet WHERE name REGEXP BINARY "^b";
`fy' で終わる名前を見つけるには、 `$' を使用して名前の 最後にマッチさせます:
mysql> 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' を含む名前を見つけるには:
mysql> 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 | +----------+-------+---------+------+------------+------------+
正規表現はもし値中のどこかにでもあればマッチするため、 SQL パターンのように、パターンの前後にワイルドカードを書く必要はありません。
5文字の名前を見つけるには、`^' と `$' を使用して名前の初めと終わりに マッチさせ、その間に `.' を5個入れます:
mysql> SELECT * 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 | +-------+--------+---------+------+------------+-------+
前のクエリは `{n}' ``n
回繰り返し'' オペレーターで
書換え可能です。
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 | +-------+--------+---------+------+------------+-------+
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 censuses on your animals.
Counting the total number of animals you have is the same question as ``How
many rows are in the pet
table?'' because there is one record per pet.
The COUNT()
function counts the number of non-NULL
results, so
the query to count your animals looks like this:
mysql> SELECT COUNT(*) FROM pet; +----------+ | COUNT(*) | +----------+ | 9 | +----------+
Earlier, you retrieved the names of the people who owned pets. You can
use COUNT()
if you want to find out how many pets each owner has:
mysql> SELECT owner, COUNT(*) FROM pet GROUP BY owner; +--------+----------+ | owner | COUNT(*) | +--------+----------+ | Benny | 2 | | Diane | 2 | | Gwen | 3 | | Harold | 2 | +--------+----------+
Note the use of GROUP BY
to group together all records for each
owner
. Without it, all you get is an error message:
mysql> SELECT owner, COUNT(owner) FROM pet; ERROR 1140 at line 1: Mixing of GROUP columns (MIN(),MAX(),COUNT()...) with no GROUP columns is illegal if there is no GROUP BY clause
COUNT()
and GROUP BY
are useful for characterizing your
data in various ways. The following examples show different ways to
perform animal census operations.
Number of animals per species:
mysql> SELECT species, COUNT(*) FROM pet GROUP BY species; +---------+----------+ | species | COUNT(*) | +---------+----------+ | bird | 2 | | cat | 2 | | dog | 3 | | hamster | 1 | | snake | 1 | +---------+----------+
Number of animals per sex:
mysql> SELECT sex, COUNT(*) FROM pet GROUP BY sex; +------+----------+ | sex | COUNT(*) | +------+----------+ | NULL | 1 | | f | 4 | | m | 4 | +------+----------+
(In this output, NULL
indicates sex unknown.)
Number of animals per combination of species and sex:
mysql> SELECT 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 | +---------+------+----------+
You need not retrieve an entire table when you use COUNT()
. For
example, the previous query, when performed just on dogs and cats, looks like
this:
mysql> 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 | +---------+------+----------+
Or, if you wanted the number of animals per sex only for known-sex animals:
mysql> SELECT 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 | +---------+------+----------+
The pet
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:
Given these considerations, the CREATE TABLE
statement for the
event
table might look like this:
mysql> CREATE TABLE event (name VARCHAR(20), date DATE, -> type VARCHAR(15), remark VARCHAR(255));
As with the pet
table, it's easiest to load the initial records
by creating a tab-delimited text file containing the information:
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 |
Load the records like this:
mysql> LOAD DATA LOCAL INFILE "event.txt" INTO TABLE event;
Based on what you've learned from the queries you've run on the pet
table, you should be able to perform retrievals on the records in the
event
table; the principles are the same. But when is the
event
table by itself insufficient to answer questions you might ask?
Suppose you want to find out the ages of each pet when they had their
litters. The event
table indicates when this occurred, but to
calculate the age of the mother, you need her birth date. Because that is
stored in the pet
table, you need both tables for the query:
mysql> SELECT pet.name, (TO_DAYS(date) - TO_DAYS(birth))/365 AS age, remark -> FROM pet, event -> WHERE pet.name = event.name AND type = "litter"; +--------+------+-----------------------------+ | name | age | remark | +--------+------+-----------------------------+ | Fluffy | 2.27 | 4 kittens, 3 female, 1 male | | Buffy | 4.12 | 5 puppies, 2 female, 3 male | | Buffy | 5.10 | 3 puppies, 3 female | +--------+------+-----------------------------+
There are several things to note about this query:
FROM
clause lists two tables because the query needs to pull
information from both of them.
name
column. The query uses
WHERE
clause to match up records in the two tables based on the
name
values.
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.
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 pet
table with itself to pair up
males and females of like species:
mysql> SELECT p1.name, p1.sex, p2.name, p2.sex, p1.species -> FROM pet AS p1, pet AS p2 -> WHERE 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 | +--------+------+--------+------+---------+
In this query, we specify aliases for the table name in order to refer to the columns and keep straight which instance of the table each column reference is associated with.
データベースやテーブルの名前が何であったか、あるいはテーブルの構造はどうであったか、 (なんというフィールドがあったのか)忘れてしまった場合。 MySQL ではいくつかのデータベースとテーブルの情報を提供する文を通じて この問題を処理します。
あなたは既に SHOW DATABASES
を知っているはずで, これはサーバーが
管理するデーターベースの一覧を表示します。 現在選択しているデータベースが
何であるかを知るには、DATABASE()
関数を使用します:
mysql> SELECT DATABASE(); +------------+ | DATABASE() | +------------+ | menagerie | +------------+
もしデータベースをまだ何も選択していなければ、結果はブランクです。
データベースに含まれるテーブルを見つけるには(たとえば、あなたがテーブル名を 知らない場合)、このコマンドを使用します:
mysql> SHOW TABLES; +---------------------+ | Tables in menagerie | +---------------------+ | event | | pet | +---------------------+
もしテーブルの構造を知りたいなら、DESCRIBE
コマンドが便利です;
これはテーブルのそれぞれのフィールドについての情報を表示します。
mysql> DESCRIBE pet; +---------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+-------+ | name | varchar(20) | YES | | NULL | | | owner | varchar(20) | YES | | NULL | | | species | varchar(20) | YES | | NULL | | | sex | char(1) | YES | | NULL | | | birth | date | YES | | NULL | | | death | date | YES | | NULL | | +---------+-------------+------+-----+---------+-------+
Field
はフィールド名を示し、Type
はフィールドのデータ型、
Null
はこのフィールドが Null
値を含むかを示し、
Key
はこのフィールドがインデックス化されているかを示し、
Default
はフィールドのデフォルトの値を示します。
もしテーブルにインデックスを持っているなら、
SHOW INDEX FROM tbl_name
がそれらの情報を示します。
Here are examples of how to solve some common problems with MySQL.
Some of the examples use the table shop
to hold the price of each
article (item number) for certain traders (dealers). Supposing that each
trader has a single fixed price per article, then (item
,
trader
) is a primary key for the records.
Start the command line tool mysql
and select a database:
mysql your-database-name
(In most MySQL installations, you can use the database-name 'test').
You can create the example table as:
CREATE TABLE shop ( article INT(4) UNSIGNED ZEROFILL DEFAULT '0000' NOT NULL, dealer CHAR(20) DEFAULT '' NOT NULL, price DOUBLE(16,2) DEFAULT '0.00' NOT NULL, PRIMARY KEY(article, dealer)); INSERT INTO shop VALUES (1,'A',3.45),(1,'B',3.99),(2,'A',10.99),(3,'B',1.45),(3,'C',1.69), (3,'D',1.25),(4,'D',19.95);
Okay, so the example data is:
mysql> SELECT * FROM shop; +---------+--------+-------+ | article | dealer | price | +---------+--------+-------+ | 0001 | A | 3.45 | | 0001 | B | 3.99 | | 0002 | A | 10.99 | | 0003 | B | 1.45 | | 0003 | C | 1.69 | | 0003 | D | 1.25 | | 0004 | D | 19.95 | +---------+--------+-------+
``What's the highest item number?''
SELECT MAX(article) AS article FROM shop +---------+ | article | +---------+ | 4 | +---------+
``Find number, dealer, and price of the most expensive article.''
In ANSI SQL this is easily done with a sub-query:
SELECT article, dealer, price FROM shop WHERE price=(SELECT MAX(price) FROM shop)
In MySQL (which does not yet have sub-selects), just do it in two steps:
SELECT
statement.
SELECT article, dealer, price FROM shop WHERE price=19.95
Another solution is to sort all rows descending by price and only
get the first row using the MySQL specific LIMIT
clause:
SELECT article, dealer, price FROM shop ORDER BY price DESC LIMIT 1
NOTE: If there are several most expensive articles (for example, each 19.95)
the LIMIT
solution shows only one of them!
``What's the highest price per article?''
SELECT article, MAX(price) AS price FROM shop GROUP BY article +---------+-------+ | article | price | +---------+-------+ | 0001 | 3.99 | | 0002 | 10.99 | | 0003 | 1.69 | | 0004 | 19.95 | +---------+-------+
``For each article, find the dealer(s) with the most expensive price.''
In ANSI SQL, I'd do it with a sub-query like this:
SELECT article, dealer, price FROM shop s1 WHERE price=(SELECT MAX(s2.price) FROM shop s2 WHERE s1.article = s2.article);
In MySQL it's best do it in several steps:
This can easily be done with a temporary table:
CREATE TEMPORARY TABLE tmp ( article INT(4) UNSIGNED ZEROFILL DEFAULT '0000' NOT NULL, price DOUBLE(16,2) DEFAULT '0.00' NOT NULL); LOCK TABLES shop read; INSERT INTO tmp SELECT article, MAX(price) FROM shop GROUP BY article; SELECT shop.article, dealer, shop.price FROM shop, tmp WHERE shop.article=tmp.article AND shop.price=tmp.price; UNLOCK TABLES; DROP TABLE tmp;
If you don't use a TEMPORARY
table, you must also lock the 'tmp' table.
``Can it be done with a single query?''
Yes, but only by using a quite inefficient trick that I call the ``MAX-CONCAT trick'':
SELECT article, SUBSTRING( MAX( CONCAT(LPAD(price,6,'0'),dealer) ), 7) AS dealer, 0.00+LEFT( MAX( CONCAT(LPAD(price,6,'0'),dealer) ), 6) AS price FROM shop GROUP BY article; +---------+--------+-------+ | article | dealer | price | +---------+--------+-------+ | 0001 | B | 3.99 | | 0002 | A | 10.99 | | 0003 | C | 1.69 | | 0004 | D | 19.95 | +---------+--------+-------+
The last example can, of course, be made a bit more efficient by doing the splitting of the concatenated column in the client.
You can use MySQL user variables to remember results without having to store them in a temporary variables in the client. 「7.2 ユーザー変数」節参照.
For example, to find the articles with the highest and lowest price you can do:
select @min_price:=min(price),@max_price:=max(price) from shop; select * from shop where price=@min_price or price=@max_price; +---------+--------+-------+ | article | dealer | price | +---------+--------+-------+ | 0003 | D | 1.25 | | 0004 | D | 19.95 | +---------+--------+-------+
You don't need foreign keys to join 2 tables.
The only thing MySQL doesn't do is CHECK
to make sure that
the keys you use really exist in the table(s) you're referencing and it
doesn't automatically delete rows from table with a foreign key
definition. If you use your keys like normal, it'll work just fine:
CREATE TABLE persons ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, name CHAR(60) NOT NULL, PRIMARY KEY (id) ); CREATE TABLE shirts ( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, style ENUM('t-shirt', 'polo', 'dress') NOT NULL, color ENUM('red', 'blue', 'orange', 'white', 'black') NOT NULL, owner SMALLINT UNSIGNED NOT NULL REFERENCES persons, PRIMARY KEY (id) ); INSERT INTO persons VALUES (NULL, 'Antonio Paz'); INSERT INTO shirts VALUES (NULL, 'polo', 'blue', LAST_INSERT_ID()), (NULL, 'dress', 'white', LAST_INSERT_ID()), (NULL, 't-shirt', 'blue', LAST_INSERT_ID()); INSERT INTO persons VALUES (NULL, 'Lilliana Angelovska'); INSERT INTO shirts VALUES (NULL, 'dress', 'orange', LAST_INSERT_ID()), (NULL, 'polo', 'red', LAST_INSERT_ID()), (NULL, 'dress', 'blue', LAST_INSERT_ID()), (NULL, 't-shirt', 'white', LAST_INSERT_ID()); SELECT * FROM persons; +----+---------------------+ | id | name | +----+---------------------+ | 1 | Antonio Paz | | 2 | Lilliana Angelovska | +----+---------------------+ SELECT * FROM shirts; +----+---------+--------+-------+ | id | style | color | owner | +----+---------+--------+-------+ | 1 | polo | blue | 1 | | 2 | dress | white | 1 | | 3 | t-shirt | blue | 1 | | 4 | dress | orange | 2 | | 5 | polo | red | 2 | | 6 | dress | blue | 2 | | 7 | t-shirt | white | 2 | +----+---------+--------+-------+ SELECT s.* FROM persons p, shirts s WHERE p.name LIKE 'Lilliana%' AND s.owner = p.id AND s.color <> 'white'; +----+-------+--------+-------+ | id | style | color | owner | +----+-------+--------+-------+ | 4 | dress | orange | 2 | | 5 | polo | red | 2 | | 6 | dress | blue | 2 | +----+-------+--------+-------+
MySQL doesn't yet optimize when you search on two different
keys combined with OR
(Searching on one key with different OR
parts is optimized quite good):
SELECT field1_index, field2_index FROM test_table WHERE field1_index = '1' OR field2_index = '1'
The reason is that we haven't yet had time to come up with an efficient
way to handle this in the general case. (The AND
handling is,
in comparison, now completely general and works very well).
For the moment you can solve this very efficiently by using a
TEMPORARY
table. This type of optimization is also very good if
you are using very complicated queries where the SQL server does the
optimizations in the wrong order.
CREATE TEMPORARY TABLE tmp SELECT field1_index, field2_index FROM test_table WHERE field1_index = '1'; INSERT INTO tmp SELECT field1_index, field2_index FROM test_table WHERE field2_index = '1'; SELECT * from tmp; DROP TABLE tmp;
The above way to solve this query is in effect an UNION
of two queries.
mysql
のバッチモードでの使用
前のセクションでは、mysql
を対話モードで使用し、クエリの入力と
結果を表示しました。 mysql
はバッチモードでも実行可能です。
これを行うには、あなたが実行したいコマンドをファイルに書いておき、
そのファイルから入力を読み込むようにして mysql
を実行します:
shell> mysql < batch-file
もし接続パラメターを指定する必要がある場合は、以下のようになるでしょう:
shell> mysql -h host -u user -p < batch-file Enter password: ********
この方法で mysql
を使用するとき、あなたはスクリプトファイルを
作成し、それを実行することになるでしょう。
なぜスクリプトを使用するのか? それには以下の理由があります:
mysql
に実行させます。
shell> mysql < batch-file | more
shell> mysql < batch-file > mysql.out
cron
ジョブとして実行する場合。 この場合、バッチモードでなければなりません。
mysql
コマンドに渡す場合。
たとえば、EUC-JP, Shift_JIS の漢字文字などは、端末や環境によっては直接入力できない場合もあるでしょう。
mysql
をバッチモードで使用するか、対話モードで使用するかで、
デフォルトの出力形式が変わります。 例えば、 SELECT DISTINCT species FROM pet
の
出力は対話モードでは次のようになります:
+---------+ | species | +---------+ | bird | | cat | | dog | | hamster | | snake | +---------+
しかし、バッチモードでは:
species bird cat dog hamster snake
もしバッチモードでも対話モードのような出力を得たい場合には、mysql -t
とします。
出力中に、実行したコマンドを表示させるならば、mysql -vvv
とします。
At Analytikerna and Lentus, we have been doing the systems and field work for a big research project. This project is a collaboration between the Institute of Environmental Medicine at Karolinska Institutet Stockholm and the Section on Clinical Research in Aging and Psychology at the University of Southern California.
The project involves a screening part where all twins in Sweden older than 65 years are interviewed by telephone. Twins who meet certain criteria are passed on to the next stage. In this latter stage, twins who want to participate are visited by a doctor/nurse team. Some of the examinations include physical and neuropsychological examination, laboratory testing, neuroimaging, psychological status assessment, and family history collection. In addition, data are collected on medical and environmental risk factors.
More information about Twin studies can be found at:
http://www.imm.ki.se/TWIN/TWINUKW.HTM
The latter part of the project is administered with a Web interface written using Perl and MySQL.
Each night all data from the interviews are moved into a MySQL database.
The following query is used to determine who goes into the second part of the project:
select concat(p1.id, p1.tvab) + 0 as tvid, concat(p1.christian_name, " ", p1.surname) as Name, p1.postal_code as Code, p1.city as City, pg.abrev as Area, if(td.participation = "Aborted", "A", " ") as A, p1.dead as dead1, l.event as event1, td.suspect as tsuspect1, id.suspect as isuspect1, td.severe as tsevere1, id.severe as isevere1, p2.dead as dead2, l2.event as event2, h2.nurse as nurse2, h2.doctor as doctor2, td2.suspect as tsuspect2, id2.suspect as isuspect2, td2.severe as tsevere2, id2.severe as isevere2, l.finish_date from twin_project as tp /* For Twin 1 */ left join twin_data as td on tp.id = td.id and tp.tvab = td.tvab left join informant_data as id on tp.id = id.id and tp.tvab = id.tvab left join harmony as h on tp.id = h.id and tp.tvab = h.tvab left join lentus as l on tp.id = l.id and tp.tvab = l.tvab /* For Twin 2 */ left join twin_data as td2 on p2.id = td2.id and p2.tvab = td2.tvab left join informant_data as id2 on p2.id = id2.id and p2.tvab = id2.tvab left join harmony as h2 on p2.id = h2.id and p2.tvab = h2.tvab left join lentus as l2 on p2.id = l2.id and p2.tvab = l2.tvab, person_data as p1, person_data as p2, postal_groups as pg where /* p1 gets main twin and p2 gets his/her twin. */ /* ptvab is a field inverted from tvab */ p1.id = tp.id and p1.tvab = tp.tvab and p2.id = p1.id and p2.ptvab = p1.tvab and /* Just the sceening survey */ tp.survey_no = 5 and /* Skip if partner died before 65 but allow emigration (dead=9) */ (p2.dead = 0 or p2.dead = 9 or (p2.dead = 1 and (p2.death_date = 0 or (((to_days(p2.death_date) - to_days(p2.birthday)) / 365) >= 65)))) and ( /* Twin is suspect */ (td.future_contact = 'Yes' and td.suspect = 2) or /* Twin is suspect - Informant is Blessed */ (td.future_contact = 'Yes' and td.suspect = 1 and id.suspect = 1) or /* No twin - Informant is Blessed */ (ISNULL(td.suspect) and id.suspect = 1 and id.future_contact = 'Yes') or /* Twin broken off - Informant is Blessed */ (td.participation = 'Aborted' and id.suspect = 1 and id.future_contact = 'Yes') or /* Twin broken off - No inform - Have partner */ (td.participation = 'Aborted' and ISNULL(id.suspect) and p2.dead = 0)) and l.event = 'Finished' /* Get at area code */ and substring(p1.postal_code, 1, 2) = pg.code /* Not already distributed */ and (h.nurse is NULL or h.nurse=00 or h.doctor=00) /* Has not refused or been aborted */ and not (h.status = 'Refused' or h.status = 'Aborted' or h.status = 'Died' or h.status = 'Other') order by tvid;
Some explanations:
concat(p1.id, p1.tvab) + 0 as tvid
id
and tvab
in
numerical order. Adding 0
to the result causes MySQL to
treat the result as a number.
id
tvab
1
or 2
.
ptvab
tvab
. When tvab
is 1
this is
2
, and vice versa. It exists to save typing and to make it easier for
MySQL to optimize the query.
This query demonstrates, among other things, how to do lookups on a
table from the same table with a join (p1
and p2
). In the example, this
is used to check whether a twin's partner died before the age of 65. If so,
the row is not returned.
All of the above exist in all tables with twin-related information. We
have a key on both id,tvab
(all tables), and id,ptvab
(person_data
) to make queries faster.
On our production machine (A 200MHz UltraSPARC), this query returns about 150-200 rows and takes less than one second.
The current number of records in the tables used above:
Table | Rows |
person_data | 71074 |
lentus | 5291 |
twin_project | 5286 |
twin_data | 2012 |
informant_data | 663 |
harmony | 381 |
postal_groups | 100 |
Each interview ends with a status code called event
. The query
shown below is used to display a table over all twin pairs combined by
event. This indicates in how many pairs both twins are finished, in how many
pairs one twin is finished and the other refused, and so on.
select t1.event, t2.event, count(*) from lentus as t1, lentus as t2, twin_project as tp where /* We are looking at one pair at a time */ t1.id = tp.id and t1.tvab=tp.tvab and t1.id = t2.id /* Just the sceening survey */ and tp.survey_no = 5 /* This makes each pair only appear once */ and t1.tvab='1' and t2.tvab='2' group by t1.event, t2.event;
Go to the first, previous, next, last section, table of contents.