カテゴリー: DB/SQL

PostgreSQL覚書 Triggerあれこれ

10年前にOracleで作成して以来、久しぶりにトリガーを作成してみた。
しかも、PostgreSQLで。

いろいろできなかったことがあったので調べてみたが、結局わからないところは代替案(要するに適当)で作った。
トリガー作成なんて、今後もあまりやらないだろうから、覚書は細かく書いておくことに。

まず、Functionを作ってからTriggerを作成。

--------------------------------------------------------
-- 走らせる処理はこっちに書く(言語はplpgsql)
--------------------------------------------------------
CREATE OR REPLACE FUNCTION SET_XX_FUNC()
  RETURNS trigger AS $$
    DECLARE
        -- 変数定義 --
    BEGIN
        -- 処理 --
    END;
$$ LANGUAGE ’plpgsql’;

--------------------------------------------------------
-- {table}に設定したテーブルに行が挿入、または行更新された後に処理を走らせる
--------------------------------------------------------
CREATE OR REPLACE TRIGGER SET_XX
 AFTER INSERT OR UPDATE ON {table} FOR EACH ROW
 EXECUTE PROCEDURE SET_XX_FUNC();

さて、ここまでは良い。
こっからだ。

--------------------------------------------------------
--★ 変数の設定
--------------------------------------------------------
DECLARE item_a int;
DECLARE item_b int :=999 ;
item_a := 1;

>>1つ目は変数のみ定義。
>>2つ目は変数定義と同時に初期値を設定する方法。
>>3つ目は定義した変数に値を入れる方法。

--------------------------------------------------------
--★ 情報の取得方法その1(普通のSELECT文)
--------------------------------------------------------
DECLARE item_a int;
DECLARE item_b int;
DECLARE item_c int;
-- (中略) --
SELECT a,b,c INTO item_a,item_b,item_c 
FROM {table} WHERE key1=NEW.key1 ;

>>SELECT文で取得したデータを変数に入れる場合の書き方はこんな感じ。
  SELECT a INTO item_a, b INTO item_b … ってな書き方はNG。
>>トリガーで更新・挿入したデータの情報を取得する場合は、NEW.{カラム名}で。

--------------------------------------------------------
--★ 情報の取得方法その2(Executeを利用したSELECT文)
--------------------------------------------------------
DECLARE add_months text = ’2months’;
DECLARE next_kigen text = ’’;
-- (中略) --
EXECUTE ’SELECT TO_CHAR(
           DATE_TRUNC(’’month’’
                , CURRENT_TIMESTAMP 
	               || ’ + interval ’ || quote_literal(add_months) || ’)’ 
	               || ’ + interval ’’-1days’’,’’YYYYMMDD’’)’
 INTO next_kigen; 

>>普通のSELECT文ではどうしても無理なものは、文字列のSELECT文を実行することになる。
  上の例は、現在日時から翌月末の日付(+2ヶ月-1日)を取得する方法。
  add_monthsがころころ変わる場合(3か月後、4か月後と)、普通のSELECT文ではどうしてもできなかった。
  あとは、サブクエリ―を使う場合もSELECT文では書けないと思っておいたほうがよい。
  (テーブル別名=エイリアスが使えないので。)
  なので、文字列を生成して
       EXECUTE 文字列 INTO 変数名;
  で検索結果をINTOの後ろの変数に格納させる。
  文字列内のシングルクォーテーションは’’と、2連続にすること。
>>quote_literal関数はよくわからないが、変数の型にあったquateを設定すると思う。
  文字列の間で変数を使いたいときは、必ずこの関数を使う。(使わんかったら動かんかったし)
  ちなみに、DATE_TRUNC(‘month’,{date_type})は月初を求めるときの関数。

--------------------------------------------------------
--★ 更新後の{item1}が0以下なら処理をやめる(条件文の例)
--------------------------------------------------------
IF NEW.{item1} <= 0 THEN
  RETURN NEW;
END IF;

>>途中で処理を抜ける処理の書き方がイマイチわからなかったので、とりあえずRETURN NEWしてる。
  (正しい方法を教えて!!)

--------------------------------------------------------
--★ 検索結果がないときの判定
--------------------------------------------------------
IF NOT FOUND THEN
  RETURN NEW;
END IF;

>>なのだが、これってカーソル処理やFOR文でしか作動しないように思う。

——————————————————–
–★ OLDとNEW
——————————————————–
OLDは更新前のレコードで、NEWは更新後のレコード。
NEW.{カラム名} という感じで使う。
なので、DELETEのトリガーの場合はOLDしかないし、INSERTの場合はNEWしかない。
今回は、AFTER INSERT OR UPDATE ~つまり更新・挿入後に行う~の処理なので、NEWしか使っていない。

衰えた脳みそをフル活用させて1日がかりで作ったのだが、うまく動かなかった ・・・・

で、デバッグ。
本当はログ出力して経過をみたかったのだけれど、ログ出力の仕方がよくわからないし、テスト環境と本番環境で異なる設定をしてもなぁ・・・と思って、動作途中で強制エラーを発生させることにした。

--------------------------------------------------------
--★ RAISE EXCEPTION(強制的に例外を発生させる)
--------------------------------------------------------
DECLARE add_months text = ’2months’;
-- (中略) 
RAISE EXCEPTION '% ここまで終わったぜ', add_months;

>>エラーメッセージに「2months ここまで終わったぜ」と表示される。
  (%は可変値で、2つ目以降の引数で設定する。Cとかの%dとかと同じ)
  なので、とりあえず、ここまでは走ってるよなぁ・・・ということで、
  次はこの例外発生処理を後ろに移動させてから実行する。
  これを何度か繰り返して、なんとか思い通りに動作するところまで行った。

とりあえず、直近で必要な情報はこんなところ。
今回はカーソルとかは使わなかったので、使うときはまた追記なり投稿なりしようと思う。
EXECUTEとかの使い方が難しいし、面倒だった。

まぁ、こんだけ書いておけば、次回はさほどオロオロすることもないだろう。

PostgreSQL覚書 日付8ケタ数値に変換

SQLServerとPostgreSQLを行ったり来たりしてると、どうもこんがらがって困る。

<現在日付を8桁にする>
to_char(current_timestamp,’YYYYMMDD’)::numeric

SQLServerだとCONVERTを使うんで、CONVERT・・・・と書きそうになった。

SQLServer覚書 日付8桁に、文字を数値に、数値を文字に

前の投稿から結構日数が経ってしまった。
というか、1月以上あいてしまった。

最近、SQLServerに接続してうんたらかんたら・・・というような開発をしている。
面倒だ・・・

DBによって、いろいろ関数がちがうのが困る。
例えば、OracleとかPostgreSQLだと、Now()で取ってこれる現在日時も、SQLServerだとGetDate()。
PostgreSQLだと、カラム名::intとかすればキャストできてしまうわけだが、SQLServerだと、Cast関数か、Convert関数を使う。

どっちが良いかはともかく、揃えてくれ・・・と思う。

さて、日付を8桁のYYYYMMDD形式にする方法。
現在時刻を8桁の文字列にする方法(CONVERT関数)
convert(varchar(8), getdate(), 112)

引数の最後の112ってのが「yyyymmdd」形式という意味。
これが、111だと「yyyy/mm/dd」、110だと「yyyy-mm-dd」になる。
(始めの引数をvarchar(10)にしておかねば・・・)

文字を数値に(CONVERT関数)
convert(int, ‘1234’)

数値を文字に(STR関数)
str(1234)

VBAでADOを利用しているが、数値のままだと文字連結でエラーになってしまった。
なので文字列型にしてから連結するためにSTR関数を使う。

PostgreSQL覚書 カンマ区切り→縦

つづけて、1つのカラムの中にカンマ区切りでデータが入っていた場合に、
これを縦にする方法。


SELECT regexp_split_to_table(item_cd,’,’) item_cd FROM table_name

item_cdカラムに「a,b,c」と入力されているとすると、出力は

a
b
c
となる。

PostgreSQL覚書 横→縦

財布を落とした。
警察へ行った。
紛失届を作成するのにあれやこれや聞かれて、答えれば答えるほど(なくしたものの大きさに)堪える・・・
って、こんな時にもダジャレが出るぐらいに凹んでる。

さて、仕事しよう。

data1,data2,data3 というカラムがあるとして、それを

data1
data2
data3
と縦に並べたいときのやり方。

SELECT unnest(array[data1, data2, data3]) as data_col
FROM table_name;

PostgreSQL覚書 現在の接続数

PostgreSQLで現在の接続数をみたい場合のSQL文

詳細
SELECT * FROM pg_stat_activity
ORDER BY query_start;

サマリー
SELECT datname, usename, COUNT(*)
FROM pg_stat_activity
GROUP BY datname, usename;

PostgreSQL覚書 型変換して並び替え

文字型のフィールドを数値化して並び替えする場合はキャストするらしい。

ORDER BY cast( [文字型フィールド] as int)

って感じ。
([文字型フィールド] をフィールド名に変える。)

このブログのPostgreSQL情報もどんどん増えてきていい感じだ。

PostgreSQL覚書 日付と曜日

PostgreSQLは配列を使えるので、簡単な書き方が何かあるはずだと思っていたら、やっぱりあった。


SELECT
TO_CHAR(current_timestamp,’YYYY年MM月DD日’) ||
(ARRAY[‘(日)’,'(月)’,'(火)’,'(水)’,'(木)’,'(金)’,'(土)’])
[extract(‘dow’ FROM current_timestamp) + 1] as now_date

“2013年04月18日(木)”

ってな感じで表示される。
便利だなぁ~~~

PostgreSQL 8桁日付の日付加算

8桁で日付を表しているときの日付計算について

下の例では、8桁日付のところを文字列にしているが
 COALESCE(TO_CHAR(20120921,’99999999′),’99991231′)
とか置きかえてやれば、数値でも転用できる。

——————————————

SELECT to_char(to_timestamp(‘20120921′,’YYYYMMDD’) + ’20 days’,’YYYYMMDD’) as add_date;

とすると、結果は “20121011” と返ってくる。

SELECT to_char(to_timestamp(‘20120921′,’YYYYMMDD’) + ’20 days’,’YYYYMM01′) as add_date;

とすると、結果は “20121001” と返ってくるので月初めの日付を取得することができる。

要は、TO_TIMESTAMPメソッドでタイムスタンプ型にしてやることのようだ。
TO_DATEでは計算はできなかった。

[2013/05/09追記]
select to_date(20120921::text,’YYYYMMDD’)
::で型変換(キャスト)してしまえば、もっと楽ってわかった。

PostgreSQL覚書 RETURNING句

私はPostgreSQLが好きだ。
無料だし、機能も結構充実してる。
MySQLのGUI管理画面はいかにも重そうなのだけど、PostgreSQLのはそんなに重くない。
(よく強制終了しているけど)

この間、他の人の書いたソースコードにRETURNING句があった。
一目見ただけで、何のためのものかは分かったのだが、
「こんなことできるんだ・・・」
と衝撃的だった。
INSERT INTO ….. RETURNING A,B
とかにすると、挿入したデータのカラム値を配列で返してくれるそうな。

便利だ!
Nextval()とか、CurVal()とかしなくてもいいのね!
なんて素敵なんだ!

PHP覚書 Oracleに接続するときの注意

PHPのOracle接続について、なんとなくわかったことを書いておく。

WindowsServer2008にApacheとPHPを入れたところ、OCI8のライブラリ情報がphpinfo()で表示されなかった。

なんでじゃぁ~~~~!!

と、迷っていたのだが、詰まるところPHPが32bit対応の場合は、Oracle自体も32bitでないと駄目なようで、
Oracleが64bitの場合は、32bitのOracle Clientを入れるとか、32bitで仲介するツール群をインストールするとかしなければならないようである。

もう1つの手といえば、おそらくPHP自体を64bit用にコンパイルすることだろう。
コンパイルしたものも実際にあるようだが、商用で使うのであれば、自身でコンパイルしておいたほうがいいと思う。

PHP覚書 MDB2 oci8_11gで接続(苦戦)

MDB2Oracle11gに接続しているのだが、なんだか実行速度が遅い。
これはひょっとすると、Oracle10gのクライアントをちゃんとアンインストールしなかったからなのでは?
と思い、Oracle関係のファイルをすべてアンインストール後、再度11gを入れなおしたのだが、これが災いして、11gに接続できなくなった。

php_oci8_11g.dllはモジュールが見つからないとかアラームが出て、Apacheもまともに起動できなくなった。

ウゲェ~

と思い、2,3日を悶々と過ごし、ようやく再接続に成功した。

原因がよくわからないのだが、
結局は、アンインストールしても環境変数にゴミが残ってしまっててダメだったようだ。
環境変数のPathやTNS_ADMINが正しく設定されているか確認後、Windowsを再起動してApacheは起動できた。
phpinfoで確認して、oci8が動作していることも確認。

で、実行速度は・・・と言えば、変わらなかった。
タイムアウトでシステムエラーになるので、php.iniのmax_execution_timeの値を変えてみて、とりあえずエラーにはならないようにしたのだが。

結局SQL文が悪いのか?

Oracle覚書 CREATE USER

ようやくOracle。
Silver Fellowという資格は持っているが、とったのが10年ほど前なのでもう忘れてしまった。

仕事で使わないと忘れてしまうのですよ。

もうDBはできているという前提で。
(DBは管理ツールで作成できるんで、まぁいいとしても、SYSTEM権限のPWって、どうやって決めるのだ・・・と思いつつ、後日に持越し)

CREATE USER [USER_NAME] IDENTIFIED BY [PASSWORD];
GRANT CREATE SESSION TO [USER_NAME];

で、とりあえずユーザをつくって権限を与えるところまで完了。
テーブルとかは、Object Browzerで作るからまぁいいや。

PostgreSQL覚書 日時の差分を日数で取得する方法

日付の差分を日数で取得する方法
first_date,last_dateがTimestamp型の場合、差分は1day 10:20:30 みたいに、日数と時間で表示される。

だけど、日数だけで取得したいときは、

SELECT first_date,last_date, cast(last_date as DATE) – cast(first_date as DATE) term FROM [table_name]

と、キャストして実行するそうだ。
勉強になった。

SQLServer覚書:PostgreSQLを参照する(WinXP/SQLServer2008)

ネットでいろいろ調べてようやく参照できるようになったので、メモ。
PostgreSQLのODBC Driverはすでにインストール済みとして書いてるよ。

まずは、コンパネ→管理ツール→Microsoft ODBC アドミニストレータで、システムDNSにODBCの接続設定を追加する。
(ユーザーDNSじゃないところがポイント)

次に、SQLServerを開いて、新しいリンクサーバーを作成する。
「全般」ページで以下を設定。
 サーバの種類はその他のデータベース。
 プロバイダは Microsoft OLE DB Provider ODBC Drivers
 製品名はPostgreSQL
 データソースは、最初に作成したODBC接続設定の名称

「セキュリティ」ページで以下を設定。
 このセキュリティコンテキストを使用するを選択して、
 リモートログインとパスワードにPostgreSQLのDBのユーザー名とパスワードを入力。

これで、OKボタンを押せば、リンクサーバが出来上がり。

あとの注意点は、Select文は、OPENQUERYを使うというところ。

SELECT * FROM OPENQUERY([リンクサーバ名],’SELECT * FROM [スキーマ名(public)].[テーブル名]’)

といった感じ。

丸一日かかって、OracleとPostgreSQL両方のDBを参照するやり方がわかった。
やっぱり、勉強が足りないな。

[追記:64bit版OS利用時(WindowsServerとか)]
64bit版OSの場合は、PostgreSQLのODBC Driver(64bit版)をインストールすれば、あとは同様に処理ができるようである。

他のサイトでもこの手の記事は多いんだけど、設定のテキストボックスに何を入れていいかまで書いてないのだ。
ここまで書いてもらえないと、応用力がないのでわからんのよ、私は。

PostgreSQL覚書 Tableの内容をファイル保存

psqlでテーブルの内容をドカーンとファイルに落とす。

psql
DB名 #= COPY [テーブル名] TO [ファイル名] (FORMAT csv);

ファイル名のところは、
E’D¥:¥¥xxx.csv’

というように、エスケープ処理が必要になることを忘れない。

逆に、CSVファイルのデータをテーブルに保存する場合は、

psql
DB名 #= COPY [テーブル名] TO [ファイル名] with csv;

PostgreSQL覚書 コマンド文字化け対応(Win)

psqlのコマンドのエラーが文字化け・・・

エンコードがUTF8になっているのが原因なので、都度エンコードをWindows用にSJISにしてあげる。

psql
DB名#= ¥encoding;

で現在の文字コードが確認できる。

psql
DB名#= ¥encoding SJIS;

で文字コードをSJISにしてくれる。

PostgreSQL サービス開始できなくなる

そもそもの発端は、私がPostgreSQLのpostgreユーザのパスワードを忘れてしまったことにある。

忘れてしまったので、コマンドプロンプトを起動させて再設定をしたその後、pgAdminでローカルのDBにアクセスできなくなってしまった。

あぁ~、またパスワード忘れたかなぁ・・・
あぁ~、再インストールかなぁ・・・

などと思ってコマンドプロンプトからpsqlを起動させてみた。
「つながらないよ」「ホントにDBあんの?」みたいな返答が返ってきたので、「あるよ?」と思ったが、「サービス開始されてないんじゃない?」って思って、Windowsの管理画面を見る。

案の定、サービス起動されてませんがな・・・

パスワードを変えちゃったせいで、起動できなくなっていたようで、さっそく新しいパスワードを設定してサービスを開始。
pgAdminで接続したら、ちゃんと見えるようになった。

なんか、自分で原因作って、自分で悩んで、自分で解決って・・・
一人で遊んでしまった。

PostgreSQL覚書 PSQLを使ってSQLファイルを実行

普段はpgAminを使ってSQLを実行しているのだけど、SQLファイルがデカすぎて、ファイルを開けない場合は、PSQLをつかうか、ファイルの中身を分散してSQLを流していくしかない。

ということで、PSQLを使ったSQLファイル実行。

コマンドプロンプトを開いて、cd等でpostgreSQLのpsql.exeのあるフォルダ(bin)まで遷移し、以下のコマンドを実行

psql -U yyy -h localhost xxx
ユーザ yyyのパスワード:(ここにパスワードを入力してEnter)

xxx => ¥i dump.sql

(xxx:DB名、yyy:ユーザID、dump.sql:SQLファイル名)

DBがローカルにない場合は、localhostの箇所をDBサーバーのIPアドレスに変えればよい。

この例の場合はdump.sqlをpsql.exeと同じフォルダに入れている。

終了するときは、

xxx => ¥q

でpsqlを終了する。

PostgreSQL覚書 配列,縦→横,カンマ区切り

以下のようなテーブルがあったとして
【t_sample】
id,element,rank,…
1,’A’,1,…
1,’B’,2,…
1,’C’,3,…
2,’D’,1,…

これを
1,’A,B,C’
2,’D’
ってな感じで取得する方法

SELECT id
 , ARRAY_TO_STRING(ARRAY(SELECT element
                         FROM t_sample a
                         WHERE a.id = b.id ORDER BY a.rank),',') elements
 FROM (SELECT id FROM t_sample GROUP BY id ORDER BY id) b

ARRAY_TO_STRING:取得した配列をARRAY_TO_STRINGでカンマ区切りにする。

SQL文が少々複雑で面倒だけど、使い勝手はよさげ。

[編集メモ] 2017/3/29 タイトルと見栄えを修正