【ORACLE】正規表現
正規表現でのチェックは、正規表現自体を理解していれば 非常に有用且つ開発が楽になる。
ただ、基本的にはサーバーサイドで使用することは あんまり良くない。
その理由として、
バックトラッキング(文字のチェック等が構文によって最初からやり直されること)の影響が計りしれないため、
サーバーサイドのボトルネックを増やすことになるのと、原因がつかみにくくなる。
そこを考えた上で、簡単なものであれば使ってもよさそうだ。
というわけでPL/SQLでの例
0から9の整数であれば、数値とみなすチェック。
SET SERVEROUTPUT ON DECLARE TYPE STR_TYPE IS TABLE OF VARCHAR(2000); STR_ARRY STR_TYPE; BEGIN STR_ARRY :=STR_TYPE(); STR_ARRY.EXTEND; STR_ARRY(STR_ARRY.COUNT) := 'AAAAA'; STR_ARRY.EXTEND; STR_ARRY(STR_ARRY.COUNT) := 'BBBBB'; STR_ARRY.EXTEND; STR_ARRY(STR_ARRY.COUNT) := '1234'; STR_ARRY.EXTEND; STR_ARRY(STR_ARRY.COUNT) := '5679'; FOR I IN 1..STR_ARRY.COUNT LOOP /*数字かチェックする*/ IF REGEXP_LIKE(STR_ARRY(I),'^[0-9]+$') THEN DBMS_OUTPUT.PUT_LINE(TO_CHAR(I) || '番目は数値だよ'); END IF; END LOOP RETURN; END;
日付チェックとかは、日付型を利用したほうが早いので
フォーマットが確実に決まっているのであれば、
それに対して限定的に使うというのが一番良いかもしれない。
【ORACLE】エラーコードの割り当て
RAISE_APPLICATION_ERRORという関数を使えば、
エラーコードの割り当てとメッセージを指定することができる。
ただし、ユーザー定義のエラーとして使用が許可されている番号は -20000〜-20999 までの 1000 コード分らしい。
トリガーで例外エラーとして処理する場合とか役に立ちそうな気がする。
CREATE OR REPLACE TRIGGER SAMPLE_TRG BEFORE INSERT ON TABLE1 FOR EACH ROW BEGIN RAISE_APPLICATION_ERROR(-20001, 'TABLE1のデータ追加は認めていません。' ); END ; /
フロントエンド開発とかでよく定義するイベントハンドラをイメージすると
個人的にはかなりいろいろなことができるような気がしている。
サーバーサービスの機能をしっかり使っていかないと、
宝の持ち腐れになるので、もったいない感じがする。
これ以外にもきっとまだ知られていないものがあるのだろう。
【Oracle】複合トリガー
Oracle11gからの新しい機能。
基本的に行トリガーは自分自身のレコードを更新することはできない。
でも、複合トリガーを使うとそんなことができてしまったりする。
これは便利ではあるが、開発では非機能要件をしっかり考慮しておかないと
運用でかなり危ない状況に陥る可能性があるので、慎重に使わなければならないもの。
とりあえず、簡単なものを作ってみる。
毎度のことだが、テーブルの構成は以下の記事にある。
fubukin.hatenablog.com
サンプルで作ってるテーブルの主キーの設定はここの記事の最後くらいにある。
fubukin.hatenablog.com
そして、事前準備として
シーケンスを作っておく
CREATE SEQUENCE "TEST_SEQ" MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 6060 CACHE 20 NOORDER NOCYCLE;
複合トリガーの作成。
トリガーの起動順序は
1.BEFORE行トリガー
2.BEFORE文トリガー
3.AFTER行トリガー
4.AFTER文トリガー
CREATE OR REPLACE TRIGGER TEST_INSERT_COMPOUND FOR INSERT ON TABLE1 /*****************************************************************/ -- 共通の宣言のセクション /*****************************************************************/ COMPOUND TRIGGER /*共通の宣言部(共通の変数などを宣言する)*/ /*テーブル配列定義*/ TYPE TABLE1_TYPE IS TABLE OF TABLE1%ROWTYPE; TABLE1_REC TABLE1_TYPE :=TABLE1_TYPE(); /*****************************************************************/ -- BEFOREの行のセクション /*****************************************************************/ BEFORE EACH ROW IS BEGIN /*配列拡張*/ TABLE1_REC.EXTEND; /*シーケンス番号を自動で入れる*/ :NEW.A1 := TEST_SEQ.NEXTVAL; TABLE1_REC(TABLE1_REC.COUNT).A1 := :NEW.A1; END BEFORE EACH ROW; /*****************************************************************/ -- BEFOREの文のセクション /*****************************************************************/ /* BEFORE STATEMENT IS BEGIN END BEFORE STATEMENT; */ /*****************************************************************/ -- AFTERの行のセクション /*****************************************************************/ /* AFTER EACH ROW IS BEGIN END AFTER EACH ROW; */ /*****************************************************************/ -- AFTERの文のセクション /*****************************************************************/ AFTER STATEMENT IS BEGIN /*値の更新*/ FOR I IN 1..TABLE1_REC.COUNT LOOP UPDATE TABLE1 SET A2 = TO_CHAR(I) ,A3 = TO_CHAR(SYSDATE,'YYYYMMDD') ,A4 = TO_CHAR(SYSDATE,'HH24MISS') ,A5 = ' ' ,A6 = ' ' ,A7 = ' ' ,A8 = ' ' ,A9 = ' ' ,A10 = ' ' ,A11 = ' ' ,A12 = ' ' WHERE A1 = TABLE1_REC(I).A1; END LOOP; TABLE1_REC.DELETE; END AFTER STATEMENT; /*****************************************************************/ -- 終わりのEND; /*****************************************************************/ END TEST_INSERT_COMPOUND;
何も入れない全てNULLになるINSERT文を実行
そして結果を見ると、対象に値が入っている。
非機能要件制約さえ決めておけば、この使い道は結構あると思うんだけどな。
【ASP.NET】サーバーコントロールをHTMLとしてレスポンスで返す
AndroidとiOSの開発を行っていた時に、
どんな端末でも使えるようにというコンセプトで
WEBブラウザを使用してアプリケーション開発を行ったことがあった。
スマートフォンがまだ普及していなくて
ちょうど、iPadが話題になってたときだった。
そのときに開発言語がバラバラになるのを恐れて、
調査して実装したのがWEBサービスでAJAXを使用して、
フロントエンド開発もバックエンド開発もやれるように考えて
出てきたのがこれ。
サーバーコントロールをうまく使えば
ポストバックを回避できるので、どんな端末でもほとんど対応できる。
Google系のブラウザやSafariを扱うときは、オープンソースのJqueryを使っていくと
Javascriptの構文の違いに悩まされることは、ほぼ無いのではないかと思う。
とりあえず、Visual StudioでWEBアプリケーションの新規プロジェクトを起こして
以下のソースをコピーしてやってみると、何か見えるかもしれない。
Default.aspx.vb
Imports System.IO Imports System.Web.UI Partial Public Class _Default Inherits System.Web.UI.Page 'ロードイベントは必ず通ります。 Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Select Case Request.Form("load") Case "Button1" Grid_View() Case Else End Select End Sub Public Sub Grid_View() 'オブジェクトの定義はWEB画面で配置するより、中で定義して貼り付けたほうが環境に依存することがないです。 'ここではGridViewコントロールをHTMLとして出力します。 Dim dgv As New GridView 'HTMLを吐き出すために以下のクラスを使います。 Dim strw As New StringWriter() Dim wt As New System.Web.UI.HtmlTextWriter(strw) 'サンプルでデータテーブルを作り、適当にデータを入れています。 Dim dt As New DataTable Dim dr As DataRow dt.Columns.Add("項目1") dt.Columns.Add("項目2") dt.Columns.Add("項目3") dt.Columns.Add("項目4") For i As Integer = 0 To 50 dr = dt.NewRow dr(0) = "データA" & CStr(i) dr(1) = "データB" & CStr(i) dr(2) = "データC" & CStr(i) dr(3) = "データD" & CStr(i) dt.Rows.Add(dr) Next 'GridViewのデータバインドメソッドです。 dgv.DataSource = dt 'このメソッドを実行しないと、 'データソースにデータ定義を関連付けしても反映されません。 dgv.DataBind() 'クライアントに返すレスポンスを初期化します。 Response.Clear() 'GridViewのレンダー(実際にWEBで記述されるHTMLの内容)を 'ここでTextWriterに出力します dgv.RenderControl(wt) 'レスポンスに出力します Response.Write(strw.ToString) '以下、リソースの開放です。必ずレスポンスを終わらせる場合は開放してから、 'Response.Endしてください。 wt.Close() strw.Close() wt.Dispose() strw.Dispose() dt.Clear() dt.Dispose() dgv.Dispose() 'レスポンス終了 Response.End() End Sub End Class
Default.aspx
<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Default.aspx.vb" Inherits="WebApplication1._Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <script src="js_XMLHTTPRequest.js" type="text/javascript" ></script> <script language="JavaScript"> <!-- //GridViewの表示 function button_clk(){ document.getElementById('test').innerHTML = HTTPXMLREQ('Default.aspx','load=Button1&test=12'); } //--> </script> <title></title> </head> <body> <form id="form1" runat="server"> <div id="test"> </div> <p> <input id="Button1" type="button" value="button" onclick='button_clk();' /></p> </form> </body> </html>
js_XMLHTTPRequest.js
var xhr var Restxt // ------------------------------------------------------------ // XMLHttpRequest オブジェクトを作成する関数 // ------------------------------------------------------------ function XMLHttpRequestCreate() { try { return new XMLHttpRequest(); } catch (e) { } try { return new ActiveXObject('MSXML2.XMLHTTP.6.0'); } catch (e) { } try { return new ActiveXObject('MSXML2.XMLHTTP.3.0'); } catch (e) { } try { return new ActiveXObject('MSXML2.XMLHTTP'); } catch (e) { } return null; } function HTTPXMLREQ(url, send_data) { Restxt = ""; // ------------------------------------------------------------ // XMLHttpRequest オブジェクトを作成 // ------------------------------------------------------------ xhr = XMLHttpRequestCreate(); // ------------------------------------------------------------ // XHR 通信の状態が変化するたびに実行されるイベント // ------------------------------------------------------------ xhr.onreadystatechange = ResponseData // ------------------------------------------------------------ // 「POST メソッド」「接続先 URL」を指定 // ------------------------------------------------------------ xhr.open("POST",url,false); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); // ------------------------------------------------------------ // 「送信データ」を指定、XHR 通信を開始する // ------------------------------------------------------------ xhr.send(send_data); return Restxt } function ResponseData() { switch (xhr.readyState) { case 4: // ------------------------------------------------------------ // XHR 通信失敗 // ------------------------------------------------------------ if (xhr.status == 0) { alert("XHR 通信に失敗しました。"); // ------------------------------------------------------------ // XHR 通信成功 // ------------------------------------------------------------ } else { // ------------------------------------------------------------ // リクエスト成功 // ------------------------------------------------------------ if ((200 <= xhr.status && xhr.status < 300) || (xhr.status == 304)) { Restxt = xhr.responseText; // ------------------------------------------------------------ // リクエスト失敗 // ------------------------------------------------------------ } else { alert(xhr.status); } } break; } };
【Oracle】ネイティブコンパイル
サーバーのメモリを消費する分、
30%くらい処理速度が向上するらしい。
サーバーのスケールアップを考えた上で、
考慮したいところ。
/*常にネイティブコンパイルを行うように設定を変更する*/ ALTER SYSTEM SET PLSQL_CODE_TYPE = NATIVE; / /*以下、限定的にネイティブコンパイルを行う*/ ALTER PACKAGE SYS.UTL_FILE COMPILE PLSQL_CODE_TYPE = NATIVE; / ALTER PACKAGE SYS.DBMS_STATS COMPILE PLSQL_CODE_TYPE = NATIVE; / ALTER PACKAGE SYS.DBMS_SQL COMPILE PLSQL_CODE_TYPE = NATIVE;
SYSにあるパッケージはネイティブで動かした方が得がありそうな気がする。
【Oracle】ユーザー範囲の統計更新
一個ずつやるよりは良いのではないかと思う。
BEGIN DBMS_STATS.GATHER_SCHEMA_STATS ( OWNNAME => 'スキーマ名(ユーザー)' ,OPTIONS => 'GATHER' ) ; END;
【Oracle】DBセッション確認
接続セッションの状態を確認するSQL
ACTIVEセッションが一つ増えるけど、実行している自分になるので気にしないでおこう
SELECT TO_CHAR(SYSDATE,'yyyy/mm/dd HH24:MI:SS'), S.STATUS, S.USERNAME, NVL(S.SQL_EXEC_START,S.PREV_EXEC_START), S.LAST_CALL_ET, ROUND( CASE WHEN Q.EXECUTIONS > 0 THEN Q.CPU_TIME / Q.EXECUTIONS / 1000 ELSE 0 END,2) FROM V$SESSION S, V$SQLSTATS Q WHERE S.USER# <> 0 AND Q.SQL_ID (+) = NVL(S.SQL_ID,S.PREV_SQL_ID) ORDER BY CASE STATUS WHEN 'ACTIVE' THEN 1 WHEN 'INACTIVE' THEN 2 ELSE 9 END, CASE WHEN Q.EXECUTIONS > 0 THEN Q.CPU_TIME / Q.EXECUTIONS ELSE 0 END DESC;