質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

MariaDB

MariaDBは、MySQL派生のオープンソースなリレーショナルデータベースシステムです。 また、MySQLとほぼ同じデータベースエンジンに対応しています。

Q&A

解決済

6回答

8251閲覧

MySQLを使って年齢で世代ごとの人数を表示するプログラム

banbook18

総合スコア13

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

MariaDB

MariaDBは、MySQL派生のオープンソースなリレーショナルデータベースシステムです。 また、MySQLとほぼ同じデータベースエンジンに対応しています。

0グッド

0クリップ

投稿2016/10/18 01:46

まだ勉強し始めて三日ほどしか経っていないMySQL初心者です。
現在の日付と生年月日から年齢を求める式を用いて、世代ごとの人数を表示するプログラムを書いています。
case文を使って範囲を指定し、条件に当てはまるものを表示するようにしたいのですが、エラーがでてしまい、想像している実行結果とは異なるものが表示されてしまいます。

以下プログラムと実行結果です。見やすいように改行しています。
因みに year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )はXXXX-YY-ZZという年-月-日の日付から年齢を表示する式です。

select case birthday when (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) >= 10 and (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) < 20 then '10代' when (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) >= 20 and (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) < 30 then '20代' when (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) >= 30 and (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) < 40 then '30代' when (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) >= 40 and (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) < 50 then '40代' when (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) >= 50 and (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) < 60 then '50代' else '定年' end as 世代, count( (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) ) as 人数 from emp3 group by case birthday when (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) >= 10 and (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) < 20 then '10代' when (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) >= 20 and (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) < 30 then '20代' when (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) >= 30 and (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) < 40 then '30代' when (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) >= 40 and (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) < 50 then '40代' when (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) >= 50 and (( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )) < 60 then '50代' else '定年' end; +--------+--------+ | 世代 | 人数 | +--------+--------+ | 定年 | 1500 | +--------+--------+ 1 row in set, 1218 warnings (0.01 sec) Warning (Code 1292): Incorrect datetime value: '1'

エラー文は一行ではなく複数表示されています。
emp3の中身は1500行分このようになっています。
お願いします。
+------+--------------+--------------+------------+---------+
| id | name | address | birthday | dept_id |
+------+--------------+--------------+------------+---------+
| 1 | 安斎 | 神奈川県 | 1969-10-13 | 3 |
| 2 | 神崎 | 愛媛県 | 1972-01-10 | 5 |
| 3 | 若山 | 兵庫県 | 1961-12-09 | 8 |
| 4 | 西野 | 神奈川県 | 1993-05-27 | 7 |
| 5 | 曽我 | 福岡県 | 1981-04-15 | 3 |
| 6 | 岡 | 滋賀県 | 1997-02-15 | 6 |
| 7 | 立石 | 岡山県 | 1950-08-16 | 1 |

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

kantomiさんの、ユーザ変数を使った方法ですが

MySQLのマニュアルと
OracleACEのAketiJyuuzouさんとyoku0825さんと
日本オラクルの木村明治さんによると
1ステートメントでユーザ変数を複数回使用した場合の
ユーザ変数の評価順序は未定義です。
http://qiita.com/AketiJyuuzou/items/cced9b70cc714b382d98

具体的には、Select句の1列目のユーザ変数が、評価されてから、
Select句の2列目以降が評価sれることが保証されません。
https://bugs.mysql.com/bug.php?id=79753

結果が保証されないといえば、
OrderBy句がないと出力順が保証されないのは有名ですね。

投稿2016/10/18 03:14

tamako

総合スコア120

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KiyoshiMotoki

2016/10/18 03:28

回答ではありません。 コメント欄を使用してください。
kantomi

2016/10/18 03:37

複数回代入する場合と、一度代入したものを参照するのは意味が違います。 参照しても値は変わりません。
kantomi

2016/10/18 03:37

間違いだから。
KiyoshiMotoki

2016/10/18 03:59 編集

kantomi様 tamako様が仰っていること自体はkantomi様のケースには当てはまりませんが、 kantomi様が提示されているユーザー変数の使用方法も、動作が保証されていません。 MySQLのマニュアルに、以下の通り説明されています。 https://dev.mysql.com/doc/refman/5.6/ja/user-variables.html > 一般的なルールとして、SET ステートメント以外では、同じステートメント内で、ユーザー変数に値を割り当ててその値を読み取ることは決してしないでください。 > (中略) > SELECT などのほかのステートメント(*)では、予想した結果が得られることもありますが、これは保証されません。 * SETステートメントのこと
tamako

2016/10/18 07:39

リンク先は 「Setステートメントでユーザ変数を複数回使った時の評価順序は保証しないよ」 という、MySQLの公式回答でしたね。 「Select文でユーザ変数を複数回使った時の評価順序は保証しないよ」 という、MySQLの公式回答は、こちらをどうぞ http://bugs.mysql.com/bug.php?id=41741
tamako

2016/10/19 01:28 編集

>複数回代入する場合と、一度代入したものを参照するのは意味が違います。 >参照しても値は変わりません。 意味がよく分からないのですが、 emp3テーブルが2行以上あったら、@ageというユーザ変数に複数回代入しませんか?
guest

0

ベストアンサー

year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) )
ではなく
year(curdate()) - year(birthday) - ( right(curdate(),5) < right(birthday,5) )
ではないですか?括弧が多い気がします。

以下追記
見やすくしてみました。

※以下修正しました11:30
もう一度修正しました11:36

select case when age between 10 and 19 then 10 when age between 20 and 29 then 20 when age between 30 and 39 then 30 when age between 40 and 49 then 40 when age between 50 and 59 then 50 else '定年' end as `世代`, count(*) as `人数` from (select year(curdate()) - year(birthday) - (right(curdate(),5) < right(birthday,5)) as age from emp3) t1 group by `世代`;

投稿2016/10/18 02:08

編集2016/10/18 02:37
moonphase

総合スコア6621

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

banbook18

2016/10/18 02:09

ありがとうございます、変えてみます!
banbook18

2016/10/18 02:15

(( year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5) ))から ( year(curdate()) - year(birthday) - ( right(curdate(),5) < right(birthday,5) ))に変更したのですが先ほどと全く同じエラーが出てきてます。
moonphase

2016/10/18 02:22

回答にSQLを追記しましたが、、、何故か全員40代になりますね。はて。
banbook18

2016/10/18 02:25

追記ありがとうございます。 エラーはでなくなたのですが、実行結果が +--------+--------+ | 世代 | 人数 | +--------+--------+ | 40 | 1500 | +--------+--------+ となって出てきます。 |10 | 数 | |20 | 数 | |30 | 数 | と各年齢ごとに対応できますでしょうか?
moonphase

2016/10/18 02:32

SQLを間違ってました。 ■修正後 group by `世代` ■修正前 group by '世代' ※カラム名なのでシングルクォートで囲んじゃだめですね・・・
banbook18

2016/10/18 02:45

正しい結果が表示されました! ありがとうございます!
banbook18

2016/10/18 02:49

因みに最後の一文のt1は何の役割をしてるのでしょうか? from (select year(curdate()) - year(birthday) - (right(curdate(),5) < right(birthday,5)) as age from emp3) t1(←ここ) group by `世代`;
moonphase

2016/10/18 02:57

(select year(curdate()) - year(birthday) - (right(curdate(),5) < right(birthday,5)) as age from emp3) の結果をt1というテーブル名(エイリアス)を付けてあげてます。 そのt1テーブルに対してselest case...している感じとなります。
banbook18

2016/10/18 03:01

つまり言い換えれば from t1 group by `世代` ということですね! 何度も教えていただきありがとうございます!!
guest

0

時間かかり既に解決済みになってましたが、ちょっと形が違うのでご参考と言う事で

sql

1select case when age>5 then '定年' else concat(age,'0代') end as 世代, sum(cnt) as 人数 FROM ( 2select truncate(((YEAR(CURDATE())-YEAR(birthday)) - (RIGHT(CURDATE(),5)<RIGHT(birthday,5))) / 10, 0) AS age, 31 AS cnt from emp3 4union all select 1,0 5union all select 2,0 6union all select 3,0 7union all select 4,0 8union all select 5,0 9union all select 6,0 10) t1 group by 1 order by 1 ;

投稿2016/10/18 03:56

A.Ichi

総合スコア4070

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

banbook18

2016/10/18 04:01

わざわざありがとうございます!!
A.Ichi

2016/10/18 04:09

どういたしまして、mysqlは、不慣れですが色々出来て楽しいです。
guest

0

こんな感じにすればよいのでは?

SQL

1select * 2,case 3when birthday between (@c:=curdate() + interval 1 day) - interval 10 year and @c then 'g00' 4when birthday between @c - interval 20 year and @c then 'g10' 5when birthday between @c - interval 30 year and @c then 'g20' 6when birthday between @c - interval 40 year and @c then 'g30' 7when birthday between @c - interval 50 year and @c then 'g40' 8when birthday between @c - interval 60 year and @c then 'g50' 9when birthday between @c - interval 70 year and @c then 'g60' 10when birthday between @c - interval 80 year and @c then 'g70' 11else 'error' 12end as gene 13from tbl;

集計するとこう

SQL

1select 2case 3when birthday between (@c:=curdate() + interval 1 day) - interval 10 year and @c then 'g00' 4when birthday between @c - interval 20 year and @c then 'g10' 5when birthday between @c - interval 30 year and @c then 'g20' 6when birthday between @c - interval 40 year and @c then 'g30' 7when birthday between @c - interval 50 year and @c then 'g40' 8when birthday between @c - interval 60 year and @c then 'g50' 9when birthday between @c - interval 70 year and @c then 'g60' 10when birthday between @c - interval 80 year and @c then 'g70' 11else 'error' 12end as gene 13,count(*) 14from tbl 15group by gene; 16

投稿2016/10/18 02:39

yambejp

総合スコア114843

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/09/17 22:24

幸運にも期待通りの結果を得られるかもしれませんが、それが確約されないSQLで、 業務では問題外の品質なので、マイナス投票します。 http://download.nust.na/pub6/mysql/doc/refman/5.1/ja/user-variables.html >基本的なルールは、ステートメントの一部でユーザ変数値を割り当てないこと >および同一ステートメント内の他部分で同じ変数を使用しないことです。 >期待通りの結果を得られるかもしれませんが、これは確約されていません。
mysql_help

2018/11/17 07:09

退会済ユーザ様 減点するなら下記の2点がより適切です。 https://dev.mysql.com/doc/refman/5.6/ja/user-variables.html >SET ステートメント以外では、同じステートメント内で、 >ユーザー変数に値を割り当ててその値を読み取ることは決してしないでください。 という記載に反して、割り当てた値を、読み取っていることが1点目。 select文の結果が10行なら、同じユーザ変数への10回以上の読取と割当を行ってます。 select文でのユーザ変数への値の代入は5.7で非推奨になり、8.0以降で削除予定の機能であることが2点目。 http://www.mysql.gr.jp/mysqlml/mysql/msg/16488
guest

0

MySQL限定ですが、変数を使った方がすっきりするでしょう。
まずは、サブクエリだけ実行して確認してみましょう。

SQL

1SET @age := 0; 2SELECT generation AS `世代`, COUNT(*) AS `人数` 3FROM 4 (SELECT 5 (@age := (year(curdate()) - year(birthday) ) - ( right(curdate(),5) < right(birthday,5))) AS age 6 , CASE WHEN @age >= 10 AND @age < 20 then '10代' 7 WHEN @age >= 20 AND @age < 30 then '20代' 8 WHEN @age >= 30 AND @age < 40 then '30代' 9 WHEN @age >= 40 AND @age < 50 then '40代' 10 WHEN @age >= 50 AND @age < 60 then '50代' 11 ELSE '定年' END AS generation 12 FROM emp3) a 13GROUP BY generation

投稿2016/10/18 02:39

kantomi

総合スコア295

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kantomi

2016/10/18 02:39

最後 ; が抜けているねw
banbook18

2016/10/18 02:43

まだ変数やサブクエリなどわからないことが多いので参考にさせていただきます! ありがとうございます!
tamako

2016/10/18 07:31

kantomi様 あなたの書いたインラインビューの Select句の1列目は、 @age := (year(curdate()) - year(birthday) ) 長いので省略 AS age Select句の2列目は、 CASE WHEN @age >= 10 長いので省略 as generation そしてMySQLの仕様では、 Select句の2列目を評価してから、Select句の1列目を評価するかもしれないし、 Select句の1列目を評価してから、Select句の2列目を評価するかもしれないのです。 どっちの動作をするかはMySQLの仕様として定義されてません。 (マルチコアCPUが当たり前のこの時代、1列目と2列目がパラレルで処理されるかも) さらには、全部の行のAgeという仮想列を評価してから 全部の行のgenerationという仮想列を評価する可能性もあります。 なので結果が保証されないのです。
kantomi

2016/10/20 05:42

確かにそのようですね。
tamako

2016/10/21 09:15

プロフィールを拝見したら、SQLの達人の生島さんでしたか QiitaとSoftware Designの生島さんの連載で、いつも勉強させていただいております。 SQLの達人の生島さんの SQLの間違いを指摘できるとは、凄く嬉しいです。
退会済みユーザー

退会済みユーザー

2018/09/17 22:24

幸運にも期待通りの結果を得られるかもしれませんが、それが確約されないSQLで、 業務では問題外の品質なので、マイナス投票します。 http://download.nust.na/pub6/mysql/doc/refman/5.1/ja/user-variables.html >基本的なルールは、ステートメントの一部でユーザ変数値を割り当てないこと >および同一ステートメント内の他部分で同じ変数を使用しないことです。 >期待通りの結果を得られるかもしれませんが、これは確約されていません。
guest

0

CURRENT_DATE関数とDATEDIFF関数を使い年齢を出してからCASE式で振り分けた方が良さそうです。
関数を使ってはいけないなら話は別ですが。

投稿2016/10/18 02:22

yona

総合スコア18155

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

banbook18

2016/10/18 02:34

関数の使い方自体まだ勉強しておらずわからないので、とりあえずはこの状態からできるようにしたいです…。使ってはいけないということはありませんが、研修で習ってないので微妙なところです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問