LoginSignup
26
21

More than 1 year has passed since last update.

PRESTOでのSQL作業用メモ

Last updated at Posted at 2020-03-04

PRESTOでのSQLの使い方について、作業で使ったコードを備忘として残します。
マニアックなものから、普段使いのものまで。並びは適当です。ネタ増えてきたら整理したりします。

細かな説明は一切入れないです。コード忘れた時に、ここからコピーして内容をちょっと変えれば使える。
という感じに特化した感じです。

【形式変換】

json形式のデータから項目を抽出する


  SELECT
      json_info
      -- 以下コードでJSON形式の項目から対象項目を抽出する
      , JSON_EXTRACT_SCALAR(json_info, '$.shohin_id') AS shohin_id
    FROM
      table
  LIMIT 3
;

出力結果例

json_info shohin_id
{"id":"1","shohin_id":"12345","date_ms":"1583295890000"} 12345
{"id":"2","shohin_id":"67890","date_ms":"1583295890000"} 67890
{"id":"3","shohin_id":"23456","date_ms":"1583295890000"} 23456

【サンプリング】

テーブルから、データをランダムサンプリングする(割合)


-- 30%のデータをランダムで取得する方法
select 
  *
from
  table TABLESAMPLE BERNOULLI(30)

テーブルから、データをランダムサンプリングする(割合&件数)


-- 30%のデータをランダムで取得して、さらに件数を3件に絞る
select 
  *
from
  table TABLESAMPLE BERNOULLI(30) LIMIT 3

【window関数】

指定した項目ごとに連番を取得する


-- 以下ではid毎に連番をつける、その際にid, typeで並び替えてから連番をつける
SELECT 
  id
  , type
  , ROW_NUMBER() OVER( PARTITION BY id ORDER BY id, type) AS rownum
FROM 
  table1

累積和を算出する


SELECT 
  target_date
  , shohin_id
  -- 日別の金額を表示 
  , price
  -- 日別の金額に関して累積和を算出する
  , SUM(price) OVER (PARTITION BY shohin_id ORDER BY target_date ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as price_sum  
FROM
(
  SELECT
    target_date
    , shohin_id
    , price
  FROM
    shohin_table
)
ORDER BY 
  target_date
  , shohin_id

group byを使って抽出したレコード内の別項目を抽出する方法


-- MIN_BYの利用例
SELECT
  商品ID
  , MIN(購入日時) AS '一番最初に購入された日時'
  , MIN_BY(商品名, 購入日時) AS '一番最初に購入された日時の商品名' 
FROM
  商品テーブル
GROUP BY
  商品ID


-- MAX_BYの利用例
SELECT
  商品ID
  , MAX(購入日時) AS '一番最後に購入された日時'
  , MAX_BY(商品名, 購入日時)) AS '一番最後に購入された日時の商品名' 
FROM 
  商品テーブル
GROUP BY
  商品ID

この機能はwindow関数らしいのですが、PRESTO独自のようで、初めて知った時にはえらく感動しました。
bigqueryとかにも標準で実装してほしい。

移動平均値を算出する


SELECT
  日付
  , 商品CD
  , 価格
  -- ここで移動平均値を取得する
  , AVG(価格) OVER (PARTITION BY CD ORDER BY 日付 ROWS BETWEEN 7 PRECEDING AND CURRENT ROW) AS avg_price_7days
FROM
  商品テーブル
WHERE
  商品CD = 10001
ORDER BY
  日付
  , 商品CD

【集合関数】

テーブルの差分情報を取得する


-- 差集合の取得  idを基準にして、 table1に存在して、table2に存在しないレコードを取得
SELECT
  id
FROM
  table1

EXCEPT

SELECT
  id
FROM
  table2

【日付操作】

曜日を指定してデータ取得する


SELECT  
  *
FROM
  table1
WHERE
  -- # 毎週日曜日のデータを取得 # 1:月/ 2:火 / 3:水 / 4;木 / 5:金 / 6:土 / 7:日
  DAY_OF_WEEK(DATE(target_date)) = 7  --日曜日のデータ取得

月末や月初など特定日付のデータを取得


SELECT 
  *
FROM 
  table1
WHERE 
  -- # 月初1日のデータのみ取得 # 1:1日 / 2:2日  / 〜 / 30:30日 / 31:31日
  DAY_OF_MONTH(DATE(target_date)) = 1  -- 月初1日目のデータ取得 

月末や月初など特定日付のデータを取得(PRESTOのtimeから抽出する場合)


SELECT
  *
FROM
  table1
WHERE
  TD_TIME_FORMAT(time,'dd','JST') = '01'

明示的に日付指定してデータ取得(PRESTOのtimeから抽出する場合)


SELECT
  *
FROM
  table1
WHERE
  -- prestoでは月の指定は MM を大文字にする必要あり。他SQLだと mm でいけたりするので間違えやすいポイント
  TD_TIME_FORMAT(time,'yyyy-MM-dd','JST') = '2020-03-10'

前日までのデータを取得


SELECT
  *
FROM
  table1
WHERE
  target_date <= TD_TIME_FORMAT(TD_TIME_ADD(TD_SCHEDULED_TIME(), '-1d'),'yyyy-MM-dd','JST') 

日付差分情報(経過日数とか)を取得する


-- DATE型かTIMESTAMP型しかDATE_DIFF関数使えないのでCASTでの出方変換
SELECT
 *
FROM
 TABLE1
WHERE
 DATE_DIFF('day', CAST(created_at AS TIMESTAMP), CAST(updated_at AS TIMESTAMP))

日付項目を一度JST変換して抽出する


-- 日付(UTC)を日付(JST)に基準統一してからデータ取得する
SELECT
  *
FROM
  TABLE1
WHERE
  TD_TIME_FORMAT(TD_TIME_PARSE(created_time), 'yyyy-MM-dd', 'JST') = '2020-04-01'

日付項目を週次(月曜日)に変換する


SELECT
  TD_TIME_FORMAT(TD_DATE_TRUNC('week', TD_TIME_PARSE(created_time, 'jst'), 'jst'),'yyyy-MM-dd', 'jst') as contracted_week
FROM
  TABLE1

VARCHARの日付項目を日付型に変換して、日付差分を取得する方法


SELECT
  -- VARCHAR→日付型へ
  DATE_PARSE(start_date,'%Y/%m/%d') AS start_date
  , DATE_PARSE(end_date,'%Y/%m/%d') AS end_Date

  -- 日付型に変換した項目で日付差分を算出
  , DATE_DIFF('month', CAST(DATE_PARSE(start_date,'%Y/%m/%d') AS TIMESTAMP), CAST(DATE_PARSE(end_date,'%Y/%m/%d') AS TIMESTAMP))
FROM
  TABLE1

スプレッドシートなどからデータをDBへ取込んだ場合、日付項目がVARCHARになってる場合があったりしますが、その場合は上記のようにDATE_PARSEで変換可能。

基準日をベースにして日付操作を行う


SELECT 
  target_date
  , CAST(target_date AS timestamp)  -- timestamp型に変換
  , DATE_ADD('hour' , 9 , CAST(target_date AS timestamp)) -- target_dateの9時間後のデータを取得(UTC→JST変換などで利用)
  , DATE_ADD('day' , 3 , CAST(target_date AS timestamp))  -- target_dateの3日後のデータを取得
  , DATE_ADD('month', 1, CAST(target_date AS timestamp)) -- target_dateの1ヶ月後のデータを取得
  , DATE_ADD('year', 1, CAST(target_date AS timestamp)) -- target_dateの1年後のデータを取得
FROM
  TABLE1

日付を年や年月などに変換する


SELECT 
  target_date
  , SUBSTR(target_date, 1, 4) AS y  -- target_dateを年に変換
  , SUBSTR(target_date, 1, 7) AS ym  -- target_dateを年月に変換
  , SUBSTR(target_date, 1, 10) AS ymd  -- target_dateを年月日に変換
FROM
  TABLE1

実行結果
スクリーンショット 2021-01-21 13.21.48.png

【ステートメント構文】

テーブルの項目や属性情報を参照する


-- テーブルの項目の属性とコメントを確認する
SHOW COLUMNS FROM table1

【関数】

対象項目にnullが入っていた場合、別値を取得する


-- 2つのテーブルからflg情報を取得し、値が存在したものを設定する。
SELECT
  ID
  -- # 左から参照し、値にnull以外が入っていたものを設定する
  , COALESCE(t1.flg, t2.flg, 0) AS status 
FROM 
  table1 AS t1
FULL OUTER JOIN
  table2 AS t2
ON
  t1.id = t2.id

文字列の中から複数の値をワイルドカード検索する 


-- 普通のLIKE指定だと文字列が含むもののみだが、この指定だと複数範囲の値も抽出可能
SELECT
  *
FROM
  table1
WHERE
  REGEXP_LIKE(ID, '[2-5]' )  -- # IDに2,3,4,5が含まれるもののみ抽出する

条件分岐して集計を行う


-- IFを利用して一つの項目から比率を求める
-- 以下では、購入区分 に1(購入者)が入っている顧客とそれ以外の顧客を判定し、日別で購入率を算出している
select 
  日付
  , COUNT(IF(購入区分=1,1,NULL)) AS 購入人数
  , COUNT(購入区分) AS 顧客数
  , COUNT(IF(購入区分=1,1,NULL)) * 1.0 / COUNT(購入区分) AS 購入率
from 
  table1
group by 
  日付

抽出結果を並び替える(order by の省略系)


-- 出力結果の並び替えを行う対象が複雑なコードの時は、 order by 1, 2, 3, 4と数字指定でも同じ結果が得られる
select 
  日付
  , CASE WHEN 購入月 = '2020-01' THEN 1 ELSE 0 END) AS status1
  , CASE WHEN 購入月 = '2020-02' THEN 1 ELSE 0 END) AS status2
  , CASE WHEN 購入日 = '2020-03' THEN 1 ELSE 0 END) AS status3
  , COALESCE(flg, flg1, flg2, 0) AS flg1
  , COALESCE(flg3, flg4, flg5, 0) AS flg2
  , COALESCE(flg6, flg7, flg8, 0) AS flg3
from 
  table1
order by
  1, 2, 3, 4  -- 項目の並び順は数字に変更しても大丈夫

上記の order by句の部分は、本来は以下のような指定が必要だが、省略可能です。


order by  
  日付
  , CASE WHEN 購入月 = '2020-01' THEN 1 ELSE 0 END)
  , CASE WHEN 購入月 = '2020-02' THEN 1 ELSE 0 END)
  , CASE WHEN 購入日 = '2020-03' THEN 1 ELSE 0 END)

ただし、一時的なデータ抽出時のみに利用する方が良くて、本番に実装して動かす処理の場合、省略しすぎるとわかりづらくなる場合があるので注意。

項目内の前後に空白があった場合に取り除く


SELECT
  TRIM('  あああ  ')

これで前後のスペースを削る事が可能

VARCHARの数値項目(区切り文字付き)があった場合に除外して合算する方法


select 
  shohin_cd
  , SUM(CAST(replace(sales, ',', '') AS INTEGER)) AS sales
from
  table1
group by
  shohin_cd

salesという項目には、1,000,000という値がVARCHARで保存されていたとすると、
1)replace関数でカンマをスペースに変換 : 1,000,0001000000
2)CAST関数でVARCHARをINTEGERに変換
3)SUM関数で合計値を算出する
→1),2)を行わないでSUMすると以下のようなエラーが発生します。


Unexpected parameters (varchar) for function sum. Expected: sum(double) , sum(real) , sum(bigint) , sum(interval day to second) , sum(interval year to month) , sum(decimal(p,s)) 

【テーブル操作】

一時的にテーブルを保存しておく


DROP TABLE IF EXISTS
  temp_DB.table_bkup20200814;

CREATE TABLE
  temp_DB.table_bkup20200814 AS
  SELECT * FROM DB.table

データの切り替えなどを行う場合などに、切り替え前のデータをバックアップしておく。
データマートなどを作る仕事をしてる場合、結構使用頻度高めになると思います。

テーブルのカラム名を一覧取得する


SELECT
    column_name
FROM
    information_schema.columns
WHERE
    table_schema = '{スキーム名}'  -- DBスキーム名を入れる
AND
    table_name = '{table名}'  -- テーブル名を入れる

トレジャーデータとかツールを使っている場合SQLの実行結果をそのままスプレッドシートなどにコピペしたい時がありますが、カラム名だけコピーできなかったのですが、その場合このクエリで取得可能になります。縦横変換だけ必要ですが、スプレッドシートはその辺りの操作簡単にできるので問題なし。

26
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
21