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

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

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

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

phpMyAdmin

phpMyAdminはオープンソースで、PHPで書かれたウェブベースのMySQL管理ツールのことです。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

Q&A

解決済

2回答

846閲覧

SQLで3つのカラムをあわせてソートしたい

ikatako

総合スコア270

MySQL

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

phpMyAdmin

phpMyAdminはオープンソースで、PHPで書かれたウェブベースのMySQL管理ツールのことです。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

0グッド

0クリップ

投稿2020/03/01 09:58

編集2020/03/01 11:17

###前提・実現したいこと
以下2つの条件で「threadのリスト」を取得しています。

➀彼が投稿したthread
➁または彼がfollow|favoriteしたthread

こうして取得された「threadのリスト」には
his_post_date(彼が投稿した日)
follow_date(彼がfollowした日)
favorite_date(彼がfavoriteした日)
と3つの日付があるのですが、これらをあわせて昇順でソートしたいと思っています。

###CREATE TABLE と INSERT
対象のテーブルです。3つあります。

SQL

1/* 2■テーブルの構造 3掲示板サイトのテーブルで、以下3つがあります。 4・actions_table : アクション 5・contents_table : 日本ユーザー以外の全て 6・users _table : 日本ユーザー 7*/ 8 9# actions_table 10# アクション 11CREATE TABLE actions_table 12 (action_id int, action_name text, action_date text, actor_id int, target_id int, target_type text, target_country text) 13; 14INSERT INTO actions_table 15 (action_id, action_name, action_date, actor_id, target_id, target_type, target_country) 16VALUES 17 (1, 'follow', '2020-01-00 00:00:00', 1, 2, 'user', 'jp'), 18 (2, 'follow', '2020-02-00 00:00:00', 1, 2, 'thread', 'jp'), 19 (3, 'favorite', '2020-03-00 00:00:00', 1, 3, 'thread', 'jp'), 20 (4, 'favorite', '2020-04-00 00:00:00', 2, 3, 'thread', 'jp'), 21 (5, 'block', '2020-05-00 00:00:00', 1, 3, 'user', 'jp') 22; 23 24# contents_table 25# 日本ユーザー以外の全て 26CREATE TABLE contents_table 27 (content_id int, author_id int, path text, content_date text, parent_thread_id int, content_country text, parent_content_id int, content_type text, content_text text, attach_id int, attach_type text, attach_country text) 28; 29INSERT INTO contents_table 30 (content_id, author_id, content_date, content_country, content_type, content_text ) 31VALUES 32 (1, 0, '2020-01-00 00:00:00', 'en', 'drama', '米国ドラマ' ), 33 (2, 1, '2020-02-00 00:00:00', 'jp', 'thread', '日本スレッドA' ), 34 (3, 2, '2020-03-00 00:00:00', 'jp', 'thread', '日本スレッドB' ), 35 (4, 0, '2020-04-00 00:00:00', 'en', 'user', '米国ユーザー' ) 36; 37 38# users_table 39# 日本ユーザー 40CREATE TABLE users_table 41 (user_id int, nick_name text) 42; 43INSERT INTO users_table 44 (user_id, nick_name) 45VALUES 46 (1, '田中一郎'), 47 (2, '田中二郎') 48; 49

###該当のコード
「threadのリスト」を取得するコードです。

下記のコードを実行する(SQL fiddle)

SQL

1/* 2■当SQLの説明 3彼(user_id=1)として以下を取得している 4➀彼が投稿したthread 5➁または彼がfollow|favoriteしたthread 6*/ 7 8select 9 c.content_id 10 ,c.content_text 11 ,c.author_id 12 ,case 13 when c.author_id=1 14 then c.content_date 15 end as his_post_date 16 ,max( 17 case when a.action_name='follow' 18 then a.action_date end 19 ) as follow_date 20 ,max( 21 case when a.action_name='favorite' 22 then a.action_date end 23 ) as favorite_date 24from actions_table a 25 26# join 27left join contents_table c 28on a.target_id=c.content_id 29left join users_table u 30on a.target_id=u.user_id 31 32# ➀彼が投稿したthread 33where c.content_type='thread' 34and ( c.author_id=1 ) 35# ➁または彼がfollow|favoriteしたthread 36or ( 37 a.actor_id=1 and a.target_type='thread' 38 and a.action_name in ('favorite', 'follow') 39 and exists( 40 select 0 from actions_table 41 where target_id=a.target_id 42 and ( action_name='follow' or action_name='favorite' ) 43 ) 44) 45 46group by a.target_id

###取得のロジック
「threadのリスト」を取得するロジックです。

➀彼が投稿したthread

を取得するために、contents_tableを見て、author_id=1かつcontent_type='thread'のものを取得します。

続いて

➁または彼がfollow|favoriteしたthread

を取得するために、actions_tableを見て、actor_id=1かつaction_name='follow'かつtarget_type='thread'のものを取得します。

以上のように「threadのリスト」はたぶん取得できたのではないかと思っています。

###試したこと
本題です。

こうして取得された「threadのリスト」には
his_post_date(彼が投稿した日)
follow_date(彼がfollowした日)
favorite_date(彼がfavoriteした日)
と3つの日付があるのですが、これらをあわせて昇順でソートしたいと思っています。

そこでgreatestを使ってみたのが次のコードです。

先頭のselectの中で3つを併せてsort_dateとし、最後の行でorder by sort_date descを追加したという2点の変更によって実現できる心づもりでおりました。

しかし実現できず、質問させて頂きました。

SQL

1select 2 c.content_id 3 ,c.content_text 4 ,c.author_id 5 ,case 6 when c.author_id=1 7 then c.content_date 8 end as his_post_date 9 ,max( 10 case when a.action_name='follow' 11 then a.action_date end 12 ) as follow_date 13 ,max( 14 case when a.action_name='favorite' 15 then a.action_date end 16 ) as favorite_date 17 ,greatest( his_post_date, follow_date, favorite_date ) as sort_date 18from actions_table a 19from actions_table a 20 21# join 22left join contents_table c 23on a.target_id=c.content_id 24left join users_table u 25on a.target_id=u.user_id 26 27# ➀彼が投稿したthread 28where c.content_type='thread' 29and ( c.author_id=1 ) 30# ➁または彼がfollow|favoriteしたthread 31or ( 32 a.actor_id=1 and a.target_type='thread' 33 and a.action_name in ('favorite', 'follow') 34 and exists( 35 select 0 from actions_table 36 where target_id=a.target_id 37 and ( action_name='follow' or action_name='favorite' ) 38 ) 39) 40 41group by a.target_id 42order by sort_date desc 43

###補足情報(FW/ツールのバージョンなど)
phpMyAdmin(4.8.5)で、MySQL(10.0.33-MariaDB)を使っています。

もしかしたらそもそも取得のロジックの考え方が間違っているのかもしれませんが、実現したいことは**『➀➁の条件で「threadのリスト」を取得し、3つの日付でソートする』**ということで変わりありません。

実現のためのお力添え頂けましたら幸甚に存じます。
宜しくお願い致します。

###ソート方法について追記
3つの日付の中で、最大値を基準としたソートを考えています。

例えば現状の「threadのリスト」(該当のコードのSQL fiddleで取得されるもの)では
「日本スレッドA、日本スレッドB」の順で取得されていますが、これを
「日本スレッドB、日本スレッドA」の順にしたいということです。

「日本スレッドA」は最大値が「2020-02-00 00:00:00」であるのに対して、
「日本スレッドB」は最大値が「2020-03-00 00:00:00」だからです。

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

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

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

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

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

guest

回答2

0

ベストアンサー

先ず、MySQLの**greatest()**はその対象にNullが含まれるとNullを返却する仕様です。
ですので先ず、Nullとならないようにします。

それから、同じselectのレベルで他のエイリアス名(follow_date等)では参照できませんのでネストして下さい。
※order by やgroup by はselect項目を参照可能ですからエラーにはなりませんが。

SQL

1select * 2from ( 3 select 4 c.content_id 5 ,c.content_text 6 ,c.author_id 7 ,case 8 when c.author_id=1 9 then c.content_date else '1900-01-01 00:00:00' 10 end as his_post_date 11 ,max( 12 case when a.action_name='follow' 13 then a.action_date else '1900-01-01 00:00:00' end 14 ) as follow_date 15 ,max( 16 case when a.action_name='favorite' 17 then a.action_date else '1900-01-01 00:00:00' end 18 ) as favorite_date 19 from actions_table a 20 # join 21 left join contents_table c 22 on a.target_id=c.content_id 23 left join users_table u 24 on a.target_id=u.user_id 25 26 # ➀彼が投稿したthread 27 where c.content_type='thread' 28 and ( c.author_id=1 ) 29 # ➁または彼がfollow|favoriteしたthread 30 or ( 31 a.actor_id=1 and a.target_type='thread' 32 and a.action_name in ('favorite', 'follow') 33 and exists( 34 select 0 from actions_table 35 where target_id=a.target_id 36 and ( action_name='follow' or action_name='favorite' ) 37 ) 38 ) 39 group by a.target_id 40) step1 41order by greatest(his_post_date, follow_date, favorite_date)

投稿2020/03/01 13:34

編集2020/03/01 13:36
sazi

総合スコア25327

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

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

ikatako

2020/03/02 02:18 編集

こんばんは。今回もどうもありがとうございます。 sazi様の日頃のご指導もあり、なんとか自力でできたコードがこちらです http://sqlfiddle.com/#!9/0b9b1f/75 (2020/03/02 10:57に少し修正しました) ご回答のコードはactions_tableを基準としていますよね。それは私の質問に則ってのことかと思いますし、おかげ様で理解も捗りました。 他方で自力でできたコードはcontents_tableを基準としてみました。 この違いですが、使いわけはどのように考えればよろしいのでしょうか。何を根拠にして、基準となるselectを決めればいいのか知りたいです。 ご回答のコードも自力でできたコードも目的の形にはなっていると思うのですが、もしよろしければ後学のために教えて頂けましたら幸いです。
sazi

2020/03/01 13:49

「threadのリスト」で同じことをやってみれば分かります。
sazi

2020/03/02 01:31 編集

それから、日付書式のデータをtextで定義しているのですからNull値の場合に0で置き換えるのはよろしくありません。 日付型に変換するような場合にはエラーになります。 空文字('')にするか、取りうる日付の最小値にする必要があります。 ※空文字を日付型に変換するような場合にはエラー考慮が必要ですが。
ikatako

2020/03/02 02:01 編集

>「threadのリスト」で同じことをやってみれば分かります。 すみませんがこちらどういう意味でしょうか? 0で埋めている部分を除けばご回答と同様に「threadのリスト」が取得されていると思うのですが。 せいぜい考え付いたことといえば、レコードが多いものを基準にした方が良いという理由でしょうか?つまりcontents_tableよりもactions_tableの方がレコードが多いので、actions_tableを基準にしたご回答のコードの方が良いということですか?
ikatako

2020/03/02 02:00

>日付書式のデータをtextで定義しているのですからNull値の場合に0で置き換えるのはよろしくありません。 なるほど。ありがとうございます。ざっと調べただけで、ひとまず0を入れておけばいいのかという安直な発想でした。 因みに質問ではTEXTで定義しつつ、実装はDATETIMEとなっておりますが、同じことですよね。(SQLfiddleがDATETIMEだと不具合が多いので質問ではTEXTで定義しました。)
sazi

2020/03/02 02:00

SQL FILDERに揚げられている内容に、ネストさせずに同じ内容を記述するとエラー(group byがあるから)になります。 それを試してみて下さい。
ikatako

2020/03/02 02:16 編集

select * from ( /* ココ */ ) step1 order by greatest(his_post_date, follow_date, favorite_date) ご回答頂きましたコードのうち、上記の「ココ」を質問のSQLfiddleに書きました(http://sqlfiddle.com/#!9/0b9b1f/78)が、特にエラーはないようでした。 確認ですが、いまアドバイス頂いているのは22:43の「基準とすべきテーブル についてactions_tableとcontent_tableとでは何が違い、どう使い分けるべきか」についてですよね?
ikatako

2020/03/02 02:24

いえやはりそれは別の質問とすべきかもしれませんね。質問自体は解決して頂いておりますし、追加でずけずけと大変失礼致しました。
sazi

2020/03/02 02:25

ソートは追加されていないようですが?
sazi

2020/03/02 03:19 編集

実装したいのは、 >こうして取得された「threadのリスト」には >・his_post_date(彼が投稿した日) >・follow_date(彼がfollowした日) >・favorite_date(彼がfavoriteした日) >と3つの日付があるのですが、これらをあわせて昇順でソートしたいと思っています。 なのではないのですか?
ikatako

2020/03/02 03:17 編集

仰る通りでした。確かに11:15のSQLfiddleの最後の行に「order by greatest(his_post_date, follow_date, favorite_date)」を追加するとエラーとなりました。 とはいえ、このエラーが22:43の「基準とすべきテーブル についてactions_tableとcontent_tableとでは何が違い、どう使い分けるべきか」という疑問について、どのような意味をもつのかが読み取れないでおります。
sazi

2020/03/02 03:25 編集

エラーの有無は、group byがあるかないかです。(group byがエラーという事では無く、group byではエラーになるSQLになってしまっているから) group byが必要かどうかは、ikatakoさんの求める仕様次第です。 ネストしておけば、最後にgroup byが無い限りエラーにはなりません。
ikatako

2020/03/02 05:09

やはりgroup byが無いエラーと、先の疑問との関係が読み取れません。ちょっと難しいですね。
sazi

2020/03/02 05:22 編集

> contents_tableよりもactions_tableの方がレコードが多いので、actions_tableを基準にしたご回答のコードの方が良いということですか? 上記が疑問だと思いますが、質問には一意キーなどが含まれておらず、どちらを基準にするのが良いかというのは私には分かりません。 あくまで質問にあるgroup byによるエラーを回避するための回答です。 group byせずに同じ結果が得られるなら、group byしないで済む方が良いとは思いますが。
ikatako

2020/03/02 10:36

なるほど一意キーにもよるのですね。これまでとの兼ね合いもあるので同じ流れでいきたいですし、何にしても頂戴したコードの方が安心ですのでgroup byの方でいきたいと思います。たびたびのご返信をどうもありがとうございました。
guest

0

「これらをあわせて昇順でソートしたいと思っています。」ですが、どのような基準で解釈するのでしょうか。

  • 「ある列が同じなら次な列の比較で判断する」というのであれば、ORDER BY column1, column2のように列挙すれば可能です。
  • 「3つの列のうち、最大値を基準にする」ような場合は、ORDER BY MAX(column1, column2, column3)のようにできます。

とりあえず、ソート順を他の人にも伝わるように定義してください。

投稿2020/03/01 11:04

maisumakun

総合スコア146018

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

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

ikatako

2020/03/01 11:18

失礼致しました。「最大値を基準にする」でした。質問にその点追記させて頂きました。
sazi

2020/03/01 13:17

order by でMax()は使用できません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問