SELECTの実行結果を、条件に応じて別のテーブルに保存する方法を解説します。SELECTの結果を他のテーブルに保存する場合は、下記のようなINSERT分とSELECT文を合体すれば実現できます。
insert
INSERT INTO "保存先テーブル名"
(SELECT * FROM hoge );
今回は、保存先のテーブルに該当の結果が無い場合だけ、実行するようなSQLを考えてみます。主キーの設定等で別の実現方法もあるかと思いますが、ワーニングを出したくない場合には今回の方法が有効かもしれません。
実行環境
テーブル
今回は集計対象のテーブル(base)と、集計結果を保存するテーブル(ans)の2つがあると考えて下さい。
集計対象テーブル:base
CREATE_TABLE_BASE
/**
* ダミーデータ1000行を作成しbaseテーブルで保存
**/
CREATE TABLE base AS
SELECT
(RANDOM()*100)::smallint AS "年齢"
, CASE WHEN (RANDOM()*100)::smallint %2 = 1 THEN '男性' ELSE '女性' END AS "性別"
FROM generate_series( 1, 1000);
年齢と性別の2列の構成のテーブルです。
行数は12行が表示されていますが、数万行あっても構いません。
集計結果保存テーブル:ans
ansテーブルには、年代、男性合計、女性合計の3列があります。年代には、集計した年齢の階級を、男女それぞれの合計人数を投入します。
今回は、年代のカラムを主キーとし、同じ年代の集計結果が投入されないようになっています。
CREATE_TABLE_ANS
CREATE TABLE ans
(
"年代" smallint NOT NULL,
"男性合計" bigint,
"女性合計" bigint,
CONSTRAINT ans_pkey PRIMARY KEY ("年代")
)
WITH (
OIDS=FALSE
);
検証環境
PostgreSQL 9.3の環境で開発・検証をしました。
SQL
filename
INSERT INTO ans
SELECT
TRUNC("年齢", -1) AS "年代" ,
COUNT( CASE WHEN "性別"='男性' THEN 1 ELSE null END ) AS "男性合計" ,
COUNT( CASE WHEN "性別"='女性' THEN 1 ELSE null END ) AS "女性合計"
FROM base
WHERE
TRUNC("年齢", -1) = 30 --30代の
GROUP BY 1
LIMIT 1 - (SELECT COUNT(*) FROM ans WHERE "年代" = 30 );
SELECT * FROM ans WHERE "年代" = 30 ;
SQLのポイント
ポイントはLIMIT句にあります。
LIMIT句の内部のサブクエリで、挿入先のansテーブルのデータの有無を確認し、レコードが存在する場合はINSERT文がLIMIT 0 の何も返却しないSQLになります。
挿入する対象のテーブルに条件に合致するレコードが存在しない時にだけ、集計が実行されます。