🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Oracle

Oracleは、米オラクルが取り扱うリレーショナルデータベース管理システムです。メインフレームからPCまで、多様なプラットフォームに対応しています。

SQL

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

Q&A

解決済

3回答

1306閲覧

SQL: 各ID毎に過去1年のイベント回数を、イベントの日付ごとに求めたい

mochiwo

総合スコア8

Oracle

Oracleは、米オラクルが取り扱うリレーショナルデータベース管理システムです。メインフレームからPCまで、多様なプラットフォームに対応しています。

SQL

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

0グッド

0クリップ

投稿2020/11/28 13:41

編集2020/11/29 14:14

ご質問です。
各IDごとに過去1年間に起きたイベント回数を、イベントの日付ごとに求める」という作業を考えています。
具体的には、以下のテーブルのfreq_past_1yearの列を求めるSQLクエリを、是非ご教示いただければ幸甚です。
イメージ説明

なお上の例ですと、ID=Aは、
2015年1月1日…初回のイベントなので、過去のイベント0回
2015年2月3日…1年振り返ると、2015年1月1日の1回
2015年8月3日…1年振り返ると、2015年1月1日、2015年2月3日の2回
2016年2月1日…1年振り返ると、2015年2月3日、2015年8月3日の2回
となっています。
(うるう年の処理が困難な場合は、365日と定義しても差し支えないと考えています)

<2020/11/29追記>
サンプルデータは以下で作成いたしました。

SQL

1CREATE TABLE TBL 2 ("ID" varchar2(1), "EVENT_DATE" timestamp) 3; 4 5INSERT ALL 6 INTO TBL ("ID", "EVENT_DATE") 7 VALUES ('A', '01-Jan-2015 12:00:00 AM') 8 INTO TBL ("ID", "EVENT_DATE") 9 VALUES ('A', '03-Feb-2015 12:00:00 AM') 10 INTO TBL ("ID", "EVENT_DATE") 11 VALUES ('A', '03-Aug-2015 12:00:00 AM') 12 INTO TBL ("ID", "EVENT_DATE") 13 VALUES ('A', '01-Feb-2016 12:00:00 AM') 14 INTO TBL ("ID", "EVENT_DATE") 15 VALUES ('B', '01-Apr-2013 12:00:00 AM') 16 INTO TBL ("ID", "EVENT_DATE") 17 VALUES ('B', '01-May-2014 12:00:00 AM') 18 INTO TBL ("ID", "EVENT_DATE") 19 VALUES ('B', '31-Mar-2015 12:00:00 AM') 20SELECT * FROM dual 21;

<11/29 追記2>
@gpsoft様のクエリを参考にさせていただきました。

SQL

1SELECT A.ID 2 , A.EVENT_DATE 3 , COUNT(B.ID) AS FREQ_PAST_1YEAR 4FROM Tbl A 5LEFT JOIN Tbl B 6ON A.ID = B.ID 7 AND B.EVENT_DATE < A.EVENT_DATE 8 AND B.EVENT_DATE > ADD_MONTHS(A.EVENT_DATE, -12) 9GROUP BY A.ID, A.EVENT_DATE 10ORDER BY A.ID, A.EVENT_DATE 11;

@gpsoft様、@sazi様、@Orlofsky様
初心者にもかかわらずご丁寧に教えていただき、大変勉強になりました。
今後質問させていただく時は、Markdownやタグ付けの徹底など留意させていただきます。
どうぞよろしくお願い申し上げます。

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

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

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

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

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

sazi

2020/11/29 01:53

freq_past_1yearの定義はどうなっていますか? 過去1年に限定しているなら、年と年度の何れでしょうか? ※サンプルが合っていない それから、event_dateの型は何ですか?
mochiwo

2020/11/29 07:48

ご指摘ありがとうございます。 「freq_past_1yearの定義はどうなっていますか? 過去1年に限定しているなら、年と年度の何れでしょうか?」 こちら、365日前以内に含まれるならOKとしており、年度ではなく年で考えております。 event_dateの型はSQL Fiddle上でintで作成いたしました。
Orlofsky

2020/11/29 13:17 編集

日付関係は日付型に変更しましょう。日付型を使わずに日付ではないデータが入って来て破綻して大改造を強いられたシステムが絶えません。 https://docs.microsoft.com/ja-jp/sql/t-sql/functions/date-and-time-data-types-and-functions-transact-sql?view=sql-server-ver15 あと、この掲示板ではMarkdownを使ってください。 CREATE TABLE, INSERTは https://teratail.com/help/question-tips#questionTips3-7 の [コード] に修正してください。
mochiwo

2020/11/29 13:14

@Orlofsky様 日付に変更するようにいたします。 Markdown形式に修正いたしました。
sazi

2020/11/29 13:25 編集

@Orlofskyさん > 日付関係は日付型に変更しましょう。 あくまで要件次第だと思います。有効な日付で縛らない方が良い時もありますので。 この質問の用途だけなら、日付型の方が良いと思いますけど。
mochiwo

2020/11/29 14:04 編集

@Orlofsky様 @sazi様 申し訳ございません。 SQL serverとOracleを間違えてタグ付けしておりましたので、タグを修正いたしました。 お手数をおかけしてしまい誠に申し訳ございません。
guest

回答3

0

回答は既に出ているので、DATE型データに使える関数の紹介。

SQL

1SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY/MM/DD HH24:MI:SS' ; 2 3セッションが変更されました。 4 5SQL> SELECT 6 2 A.TODAYS_DATE 7 3 , A.TRUNCED_DATE 8 4 , ADD_MONTHS(A.TRUNCED_DATE, -12) AS ONE_YEAR_AGO 9 5 , ADD_MONTHS(A.TRUNCED_DATE, 12) AS ONE_YEAR_LATER 10 6 FROM( 11 7 SELECT 12 8 SYSDATE AS TODAYS_DATE 13 9 , TRUNC(SYSDATE) AS TRUNCED_DATE 14 10 FROM DUAL 15 11 ) A ; 16 17TODAYS_DATE TRUNCED_DATE ONE_YEAR_AGO ONE_YEAR_LATER 18------------------- ------------------- ------------------- ------------------- 192020/11/30 08:50:07 2020/11/30 00:00:00 2019/11/30 00:00:00 2021/11/30 00:00:00

頑張ってください。

投稿2020/11/29 23:54

Orlofsky

総合スコア16417

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

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

mochiwo

2020/11/30 00:36

@Orlofsky様 日付型データの処理方法をご教示いただき誠にありがとうございます。 ご指導いただいた方法を実解析へ応用していきたいと存じます。
guest

0

質問の意図を読み違えていたので、訂正しました。
相関サブクエリーを用いています。

SQL

1select * 2 , ( 3 select count(*) from tbl 4 where id = t1.id 5 and event_date >= t1.event_date -10000 6 and event_date < t1.event_date 7 ) as freq_past_1year 8from tbl t1 9order by ID, event_date

※1年前の算出は単に単に年から-1しているだけですので、閏年などは考慮していません。
(1日分の誤差が生じる)
閏年の考慮が必要なら、日付型に変更して演算して下さい。

追記

質問に追記された内容を相関サブクエリーに変更してみました。

SQL

1SELECT A.ID 2 , A.EVENT_DATE 3 , (select COUNT(ID) from tbl 4 where ID = A.ID 5 AND EVENT_DATE < A.EVENT_DATE 6 AND EVENT_DATE > ADD_MONTHS(A.EVENT_DATE, -12) 7 ) AS FREQ_PAST_1YEAR 8FROM Tbl A 9;

投稿2020/11/29 06:50

編集2020/11/29 14:41
sazi

総合スコア25327

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

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

mochiwo

2020/11/29 07:49

大変詳細にご教示いただき誠にありがとうございます。 後日、実際の解析環境で試してみます。
sazi

2020/11/29 08:50 編集

質問のサンプル画像は間違っているという事でいいですか。 あ、event_dateの年の一年前って事ですね。 修正します。
mochiwo

2020/11/29 13:15

修正いただき誠にありがとうございます。 後日、確認いたします。
mochiwo

2020/11/29 13:48

@sazi様 誠に申し訳ございません。 SQL serverとOracleを間違えてタグ付けしておりました。 Oracleでも相関サブクエリを使うことは可能でしょうか? ご面倒をおかけしてしまい大変恐縮です。
mochiwo

2020/11/29 14:06

自分でもOracleへの変換を考えてみます。 ご迷惑をおかけしてしまい誠に申し訳ございませんでした。
sazi

2020/11/29 14:36 編集

相関サブクエリーは使えます。
mochiwo

2020/11/29 14:47

@sazi様 相関サブクエリの使い方まで教えていただき誠にありがとうございます。 無事sazi様の方法でも、結果を得ることができました。 大変勉強になりました。
guest

0

ベストアンサー

すみません、動かしてないので自信ないですが、ダメ元で…。

sql

1SELECT EV.ID, EV.event_date, COUNT(EV2.ID) AS freq_past_1year 2FROM event EV 3 LEFT JOIN event EV2 4 ON EV2.ID=EV.ID 5 AND EV2.event_date < EV.event_date 6 AND EV2.event_date > DATEADD(year, -1, EV.event_date) 7GROUP BY EV.ID, EV.event_date 8ORDER BY EV.ID, EV.event_date

※テーブル名はeventと仮定してます。

※「過去1年間」の意味次第では、>>=に変えるべきかも。

event_dateの型次第では、DATEADD()がエラーになるかなぁ。

[追記]
event_dateの型がintとのことなので、クエリーの手直しが必要ですね。

sql

1CREATE TABLE event(ID varchar(1), event_date int); 2 3INSERT INTO event(ID, event_date) VALUES 4('A', 20150101) 5,('A', 20150203) 6,('A', 20150803) 7,('A', 20160201) 8,('B', 20130401) 9,('B', 20140501) 10,('B', 20150331); 11 12SELECT EV.ID, EV.event_date, COUNT(EV2.ID) AS freq_past_1year 13FROM event EV 14 LEFT JOIN event EV2 15 ON EV2.ID=EV.ID 16 AND EV2.event_date < EV.event_date 17 AND EV2.event_date > FORMAT(DATEADD(year, -1, DATEFROMPARTS(EV.event_date/10000, EV.event_date%10000/100, EV.event_date%100)), 'yyyyMMdd') 18GROUP BY EV.ID, EV.event_date 19ORDER BY EV.ID, EV.event_date;

以下、SQL Server 2012での実行結果です。

イメージ説明

投稿2020/11/28 13:55

編集2020/11/29 13:43
gpsoft

総合スコア1323

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

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

mochiwo

2020/11/28 14:39 編集

早速のご連絡誠にありがとうございます。 SQL Fiddleというサイトで、ご指導いただいたクエリを試してみたところ、 "EV"."EVENT_DATE": invalid identifier というエラーが生じてしまいました。 原因おわかりでしょうか…?
Orlofsky

2020/11/28 14:50

質問にCREATE TABLE文を載せないから適切なコメントが付きにくいことに気が付けるとちょっとした進歩なんだが。
sazi

2020/11/29 01:32

@mochiwoさん gpsoftさんは > event_dateの型次第では、DATEADD()がエラーになる と言われていますよ。
mochiwo

2020/11/29 07:49

@Orlofsky様 @sazi様 ご指摘誠にありがとうございます。 わかりづらく申し訳ございません。 サンプルデータは、下記のように作成いたしました。 CREATE TABLE event ("ID" varchar2(1), "event_date" int) ; INSERT ALL INTO event ("ID", "event_date") VALUES ('A', 20150101) INTO event ("ID", "event_date") VALUES ('A', 20150203) INTO event ("ID", "event_date") VALUES ('A', 20150803) INTO event ("ID", "event_date") VALUES ('A', 20160201) INTO event ("ID", "event_date") VALUES ('B', 20130401) INTO event ("ID", "event_date") VALUES ('B', 20140501) INTO event ("ID", "event_date") VALUES ('B', 20150331) SELECT * FROM dual ; ただ、私のサンプルデータの作り方や、ブラウザ上(SQL fiddle)で動かしているのが悪いためか、うまくいかないため、実際の解析環境で後日試してみます。 結果分かり次第、ご連絡いたします。
Orlofsky

2020/11/29 08:17

CREATE TABLEとINSERTは質問に追加してください。
mochiwo

2020/11/29 08:27

@ Orlofsky様 質問に追加いたしました。 どうぞご確認くだされば幸甚に存じます。
gpsoft

2020/11/29 13:47

遅くなりましたが、最新情報を踏まえて、回答を編集しました。 コメントして頂いた皆さま、ありがとうございます。
mochiwo

2020/11/29 13:52

@gpsoft様 大変丁寧にご教示いただき誠にありがとうございます。 実は私のミスで、SQL serverとOracleのタグ付を間違えておりました。 教えていただいた方法で、Oracleの書き方へ変更して試してみます。
mochiwo

2020/11/29 14:08

@gpsoft様 望みの結果を得ることができました! 誠にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問