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

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

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

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

Q&A

解決済

2回答

1155閲覧

一つのテーブルに対してWhere句のパラメータを変えたもの同士で計算したい

shintaro1001

総合スコア7

SQL

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

0グッド

0クリップ

投稿2021/09/26 07:32

前提・実現したいこと

顧客ID,購入日,購入回数,商品コード,枝番,商品名という項目で構成された購入回数テーブルがあります。
この購入回数というのは、同じ顧客IDで複数回購入した場合のN回目という値が入っています。

|行数|顧客ID|購入日|購入回数|商品名|
|:--|:--:|--:|
|1|001|2020/01/01|1|AAA
|2|001|2020/01/10|2|CCC
|3|002|2020/01/15|1|BBB
|4|002|2020/01/18|2|CCC
|5|003|2020/01/20|1|DDD
|6|003|2020/01/25|2|EEE

このテーブルから同じ顧客IDで購入回数2回目と1回目の差の平均を求めたいのですが、
自己結合してサブクエリを作るなど模索してみたものの
どのようにSQLを書いてよいのかがわかりません。
テーブルのレコード数としては118万行ほどです。

上記のデータを元にした場合、得たい結果としては下記のようになります。

顧客ID001の2レコード目と1レコード目の差 select 購入日 from 購入回数テーブル where 顧客ID='001' and 購入回数=2 の購入日から select 購入日 from 購入回数テーブル where 顧客ID='001' and 購入回数=1 の購入日の日数の差を出す。上記の例では9日 2回商品を購入している顧客すべてに対して上記の差を求めて平均を出す ID002の1回目と2回目の差→3日 ID003の1回目と2回目の差→5日 3人の顧客の1回目と2回目の差の日数の平均→(9+3+5)/3→5.667

こういった場合、どのような書き方をすればよいのでしょうか。
よろしくお願いします。

###環境
Macローカルpostgresql(pgadmin)

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

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

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

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

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

m.ts10806

2021/09/26 09:30

PostgreSQLのバージョンと テーブル定義(CREATE TABLE文)をご提示ください。
shintaro1001

2021/09/26 12:12

ポスグレのバージョンはPostgreSQL 13.4 テーブル情報は必要なカラムに限定して、カラム名は企業情報などを特定できないように一般名称化しています。 CREATE TABLE IF NOT EXISTS public.購入回数テーブル ( 顧客ID character varying(10) COLLATE pg_catalog."default", "購入日" date, "購入回数" integer, "商品コード" character varying(4) COLLATE pg_catalog."default", "枝番" character varying(2) COLLATE pg_catalog."default", "商品名" character varying(20) COLLATE pg_catalog."default", ) よろしくお願いします。
mayu-

2021/09/27 09:40 編集

> 顧客ID,購入日,購入回数,商品コード,枝番,商品名 というフィールド構成の表において 構成に合致しないデータ例を掲載された理由は何でしょう。 SQLの記述に重要な要素である「 列 」を "ご自身の判断で" 省略して掲載なさったりすると ミスリードを誘発する原因になりかねないと思います。 また、"購入回数テーブル" という名前からすると 読み手は、[ 顧客ID + 購入回数 ]で行がユニークになる と考えるのが自然ではないでしょうか。少なくとも私はそう感じます。 しかし、それならなぜ[ 枝番 ]という列がテーブルに存在するのか、という疑問が出てきます。 [ 顧客ID + 購入回数 ]で行がユニークになるのでしたら、どう考えても不要なカラムですよね。 [ 顧客ID + 購入回数 ]    では行がユニークにならず [ 顧客ID + 購入回数 + 枝番 ] だと行の特定( 顧客ごとの比較 )が可能 ということなのでしたら 回答してくださったお二方のSQLに少し記述を加えることでご希望の結果になるでしょう。 ■ gentaroさんのSQL [現在]: WHERE 購入回数=2 WHERE 購入回数=1 [変更後]: WHERE 購入回数=2 and 枝番='1' WHERE 購入回数=1 and 枝番='1' ■ takanaweb5さんのSQL [現在]: HAVING COUNT(*) = 2 [変更後]: HAVING COUNT( distinct 購入日 ) = 2
shintaro1001

2021/09/27 13:36

> SQLの記述に重要な要素である「 列 」を > "ご自身の判断で" 省略して掲載なさったりすると > ミスリードを誘発する原因になりかねないと思います。 ご指摘ありがとうございます。まさに自分の判断で省略してしまっておりました。 結論を申し上げると[ 顧客ID + 購入回数 ]でユニークになります。 枝番は商品をグループ分けするために付与されたものとなります。 回答いただいた方々、それぞれの方法で、求めたい値が得られることを確認しました。
mayu-

2021/09/27 16:15 編集

そうでしたか。杞憂に過ぎなかったようなので本当に良かったです。 回答がついた後、日を跨いでも未解決だったため データ構造についてはけっこう疑念を抱いていました。ご容赦下さい。
m.ts10806

2021/09/27 19:34

質問本文に記載してください。
guest

回答2

0

ベストアンサー

サンプルデータ

SQL

1INSERT INTO 購入回数テーブル 2 (顧客ID, 購入日, 購入回数) 3VALUES 4 (001, '2020-01-01', 1), 5 (001, '2020-01-10', 2), 6 (001, '2020-01-11', 3), -- 購入回数>2のため計算の対象外となるレコード 7 (002, '2020-01-15', 1), 8 (002, '2020-01-18', 2), 9 (003, '2020-01-20', 1), 10 (003, '2020-01-25', 2), 11 (004, '2020-01-20', 1), 12 (004, '2020-01-20', 2), -- 同一日に2回購入したケース 13 (005, '2020-01-20', 1) -- 2回目の購入がないため対象外となるレコード 14;

以下のSQLを実行することで、顧客IDごとの1回目と2回目の差の日数を一覧化出来ます。

SQL

1SELECT 2 顧客ID 3, MAX(購入日) - MIN(購入日) AS 経過日 4--, MIN(購入日) AS 一回目購入日 -- (購入回数が1と2のレコードのうち小さいほう) 5--, MAX(購入日) AS 二回目購入日 -- (購入回数が1と2のレコードのうち大きいほう) 6FROM 購入回数テーブル 7WHERE 購入回数 IN(1,2) -- 対象のレコードを絞り込む 8GROUP BY 顧客ID 9HAVING 10-- HAVING句に条件を指定することで、1回しか購入していない顧客を対象外にする 11 COUNT(*) = 2 -- あるいは MAX(購入回数) = 2 でも可 12ORDER BY 顧客ID

結果

顧客id経過日
19
23
35
40

求めたいのは上記結果の平均であるため、全体のSQLは以下となります。

SQL

1SELECT AVG(経過日) 2FROM 3 (SELECT MAX(購入日) - MIN(購入日) AS 経過日 4 FROM 購入回数テーブル 5 WHERE 購入回数 IN(1,2) 6 GROUP BY 顧客ID 7 HAVING COUNT(*) = 2 8 ) AS T

投稿2021/09/26 13:49

編集2021/09/26 14:02
takanaweb5

総合スコア359

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

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

shintaro1001

2021/09/28 03:41

ありがとうございます。 一旦ユーザーごとの平均を出した上で全体の平均も求められるので、応用が効きました。
guest

0

SQL

1SELECT AVG(A.購入日 - B.購入日) 2FROM ( 3 select 顧客ID, 購入日 from 購入回数テーブル WHERE 購入回数=2 4) AS A 5INNER JOIN ( 6 select 顧客ID, 購入日 from 購入回数テーブル WHERE 購入回数=1 7) AS B 8ON A.顧客ID = B.顧客ID;

投稿2021/09/26 07:45

gentaro

総合スコア8947

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

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

gentaro

2021/09/26 07:55

「購入日」がdate型を使っているという前提の回答。
shintaro1001

2021/09/28 03:41

ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問