Showing posts with label Oracle. Show all posts
Showing posts with label Oracle. Show all posts

2021-11-07

ORACLE_HOMEを取得するPowerShellワンライナー

(Get-ChildItem -Path HKLM:\SOFTWARE\ORACLE | Where-Object {$_.PSChildName -like "KEY_*"}).GetValue("ORACLE_HOME")

エイリアスを使うと、

(gci HKLM:\SOFTWARE\ORACLE | ? {$_.PSChildName -like "KEY_*"}).GetValue("ORACLE_HOME")

Windows 64-bit に Oracle Client 32-bit をインストールしている場合
(gci HKLM:\SOFTWARE\WOW6432Node\ORACLE | ? {$_.PSChildName -like "KEY_*"}).GetValue("ORACLE_HOME")

レジストリ・パラメータについて

データベース管理者リファレンス for Microsoft Windows によると

レジストリ・パラメータの概要
HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\KEY_HOMENAMEについて
コンピュータの新規OracleホームにOracle製品をインストールするたびに、HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE\KEY_HOMENAMEが作成されます。
このサブキーにはほとんどのOracle製品に対するパラメータ値が含まれます
(略)
 ORACLE_HOME
 Oracle製品がインストールされるOracleホーム・ディレクトリを指定します。

とあります。
このレジストリのサブキーの情報は %ORACLE_HOME%\BIN\oracle.key に記載されています。したがって、以下のようにPATH を探索してレジストリのサブキーを取得することもできます。

Get-Content (Join-Path ($Env:Path.Split(';') | ? {($_ -ne "") -and (Test-Path (Join-Path $_  oracle.key))}) oracle.key)

上述のワンライナーでは 簡便なORACLE_HOME というプロパティがあるキーを取得するという方法を採用しています。


`oracle.key` のあるフォルダの一つ上のフォルダが `ORACLE_HOME` ですので、以下のワンライナーでも`ORACLE_HOME` が取得できます。
Split-Path ($Env:Path.Split(';') | ? {($_ -ne "") -and (Test-Path (Join-Path $_  oracle.key))})

2020-07-22

PL/SQLから日本語(JIS)でメール送信する

ここでは、

にあるサンプルコード
DECLARE
  c UTL_SMTP.CONNECTION;
 
  PROCEDURE send_header(name IN VARCHAR2, header IN VARCHAR2) AS
  BEGIN
    UTL_SMTP.WRITE_DATA(c, name || ': ' || header || UTL_TCP.CRLF);
  END;
 
BEGIN
  c := UTL_SMTP.OPEN_CONNECTION('smtp-server.acme.com');
  UTL_SMTP.HELO(c, 'foo.com');
  UTL_SMTP.MAIL(c, 'sender@foo.com');
  UTL_SMTP.RCPT(c, 'recipient@foo.com');
  UTL_SMTP.OPEN_DATA(c);
  send_header('From',    '"Sender" <sender@foo.com>');
  send_header('To',      '"Recipient" <recipient@foo.com>');
  send_header('Subject', 'Hello');
  UTL_SMTP.WRITE_DATA(c, UTL_TCP.CRLF || 'Hello, world!');
  UTL_SMTP.CLOSE_DATA(c);
  UTL_SMTP.QUIT(c);
EXCEPTION
  WHEN utl_smtp.transient_error OR utl_smtp.permanent_error THEN
    BEGIN
      UTL_SMTP.QUIT(c);
    EXCEPTION
      WHEN UTL_SMTP.TRANSIENT_ERROR OR UTL_SMTP.PERMANENT_ERROR THEN
        NULL; -- When the SMTP server is down or unavailable, we don't have
              -- a connection to the server. The QUIT call raises an
              -- exception that we can ignore.
    END;
    raise_application_error(-20000,
      'Failed to send mail due to the following error: ' || sqlerrm);
END;

を、日本語JISコード(ISO-2022-JP)で送れるように書き換えてみます。
ついでに、SMTP認証あり、ポート番号を587に変更し、プロシージャ化しています。

CREATE OR REPLACE PROCEDURE SEND_MAIL(
    IN_FROM_NAME        VARCHAR2,
    IN_FROM_ADDRESS     VARCHAR2,
    IN_TO_ADDRESS       VARCHAR2,
    IN_SUBJECT          VARCHAR2,
    IN_MESSAGE          VARCHAR2,
    IN_AUTH_USER        VARCHAR2,
    IN_AUTH_PASSWORD    VARCHAR2
) AS
    c   UTL_SMTP.CONNECTION;
    host    CONSTANT VARCHAR2(64) := 'foo.com';
    port    CONSTANT NUMBER := 587;
    -- ヘッダー部送信
    -- UTL_ENCODE.MIMEHEADER_ENCODE(buf, 'ISO2022-JP')を使うと=?iso-2022-jp?B?が=?ISO2022-JP?B?になるため環境により文字化けする。
    -- Each line of characters MUST be no more than 998 characters, and SHOULD be no more than 78 characters, excluding the CRLF.(RFC 2822 2.1.1)
    PROCEDURE SEND_HEADER (
         IN_NAME IN VARCHAR2
        ,IN_HEADER IN VARCHAR2
        ,IN_ADDRESS IN VARCHAR2 := NULL
    ) AS
    BEGIN
        UTL_SMTP.WRITE_DATA(c, IN_NAME || ': ');
        UTL_SMTP.WRITE_DATA(c, '=?iso-2022-jp?B?');
        UTL_SMTP.WRITE_RAW_DATA(c, UTL_ENCODE.BASE64_ENCODE(UTL_RAW.CAST_TO_RAW(CONVERT(IN_HEADER, 'ISO2022-JP'))));
        UTL_SMTP.WRITE_DATA(c, '?=');
        IF IN_ADDRESS IS NOT NULL THEN
            UTL_SMTP.WRITE_DATA(c, '<' || IN_ADDRESS || '>');
        END IF;
        UTL_SMTP.WRITE_DATA(c, UTL_TCP.CRLF);
    END;

BEGIN
    c := UTL_SMTP.OPEN_CONNECTION(host, port);
    UTL_SMTP.EHLO(c, host);

    -- SMTP認証(PLAIN)
    -- 11.2.0.1以前
    --UTL_SMTP.COMMAND(c, 'AUTH', 'PLAIN ' || UTL_ENCODE.TEXT_ENCODE(CHR(0)|| IN_AUTH_USER || CHR(0)|| IN_AUTH_PASSWORD, NULL, UTL_ENCODE.BASE64));
    -- 11.2.0.2以上
    UTL_SMTP.AUTH(c => c, username => IN_AUTH_USER, password => IN_AUTH_PASSWORD, schemes => UTL_SMTP.all_schemes);

    UTL_SMTP.MAIL(c, IN_FROM_ADDRESS);
    UTL_SMTP.RCPT(c, IN_TO_ADDRESS);

    UTL_SMTP.OPEN_DATA(c);

    --ヘッダ
    SEND_HEADER('From', IN_FROM_NAME, IN_FROM_ADDRESS);
    UTL_SMTP.WRITE_DATA(c, 'To: ' || IN_TO_ADDRESS || UTL_TCP.CRLF);
    SEND_HEADER('Subject', IN_SUBJECT);

    UTL_SMTP.WRITE_DATA(c, 'MIME-Version: 1.0' || UTL_TCP.CRLF);
    UTL_SMTP.WRITE_DATA(c, 'Content-Type: text/plain; charset=iso-2022-jp' || UTL_TCP.CRLF);
    UTL_SMTP.WRITE_DATA(c, 'Content-Transfer-Encoding: 7bit' || UTL_TCP.CRLF);

    UTL_SMTP.WRITE_DATA(c, UTL_TCP.CRLF);
    -- 本文
    UTL_SMTP.WRITE_RAW_DATA(c, UTL_RAW.CAST_TO_RAW(CONVERT(IN_MESSAGE, 'ISO2022-JP')));
    UTL_SMTP.CLOSE_DATA(c);
    UTL_SMTP.QUIT(c);

EXCEPTION
    WHEN UTL_SMTP.transient_error OR UTL_SMTP.permanent_error THEN
        BEGIN
            UTL_SMTP.QUIT(c);
        EXCEPTION
            WHEN UTL_SMTP.TRANSIENT_ERROR OR UTL_SMTP.PERMANENT_ERROR THEN
                NULL; -- When the SMTP server is down or unavailable, we don't have
                            -- a connection to the server. The QUIT call will raise an
                            -- exception that we can ignore.
        END;
        raise_application_error(-20000,
            'Failed to send mail due to the following error: ' || sqlerrm);
END;
/


  • ヘッダのエンコードにはUTL_ENCODE.MIMEHEADER_ENCODEを使いたいところですが、キャラクタ・セットが iso-2022-jp ではなく ISO2022-JP と出力されてしまい、環境によって文字化けするため、使わないようにしています。
  • RFC2822では1行の文字数について、CRLFを除く998未満(MUST)、78文字未満(SHOULD)と規定されており、UTL_ENCODE.MIMEHEADER_ENCODEを使えば1行当たり78文字未満に分割(folding)してくれるのですが、上記のコードでは考慮していませんのでご注意ください。

2019-01-13

Oracleで有効数字を取得する。

基本的な考え方

ある数値の桁数を取得するには常用対数を用います。
FLOOR(LOG(10, n)) は次のとおり0以外の数値が最初に出現する桁を表します。

SQL> WITH DATA AS (
  2  SELECT 1234 N FROM DUAL
  3  UNION ALL SELECT 234 N FROM DUAL
  4  UNION ALL SELECT 34 N FROM DUAL
  5  UNION ALL SELECT 4 N FROM DUAL
  6  --UNION ALL SELECT 0 N FROM DUAL
  7  UNION ALL SELECT 0.123 N FROM DUAL
  8  UNION ALL SELECT 0.0234 N FROM DUAL
  9  UNION ALL SELECT 0.00345 N FROM DUAL
 10  )
 11  SELECT N, FLOOR(LOG(10, N)) FROM DATA;

         N FLOOR(LOG(10,N))
---------- ----------------
      1234                3
       234                2
        34                1
         4                0
      .123               -1
     .0234               -2
    .00345               -3

注: LOG(10, 0) は次のエラーを発生させます。: ORA-01428: 引数'0'が有効範囲外です

したがって、ROUND(n, - FLOOR(LOG(10, n))) で最初の1桁で丸めを行った値を取得することができます。

SQL> WITH DATA AS (
  2  SELECT 1234 N FROM DUAL
  3  UNION ALL SELECT 234 N FROM DUAL
  4  UNION ALL SELECT 34 N FROM DUAL
  5  UNION ALL SELECT 4 N FROM DUAL
  6  --UNION ALL SELECT 0 N FROM DUAL
  7  UNION ALL SELECT 0.123 N FROM DUAL
  8  UNION ALL SELECT 0.0234 N FROM DUAL
  9  UNION ALL SELECT 0.00345 N FROM DUAL
 10  )
 11  SELECT N, ROUND(n, - FLOOR(LOG(10, N))) FROM DATA;

         N ROUND(N,-FLOOR(LOG(10,N)))
---------- --------------------------
      1234                       1000
       234                        200
        34                         30
         4                          4
      .123                         .1
     .0234                        .02
    .00345                       .003

最初の d 桁で丸めるには、ROUND(n, d - 1- FLOOR(LOG(10, n)))とします。

関数

以上を関数にまとめると次のようになります。

CREATE OR REPLACE FUNCTION SIGNIFICANT_FIGURES(
    n   NUMBER,
    d   NUMBER
) RETURN NUMBER
IS
BEGIN
    IF n = 0 THEN
        RETURN 0;
    ELSE
        ROUND(n, d - 1 - FLOOR(LOG(10, n)));
    END IF;
END;

偶数丸めを行うには、ROUND 関数の代わりに ROUND_HALF_EVEN関数を使います。

注: Oracle Database 18c からROUND_TIES_TO_EVEN 関数が使えます。

2019-01-12

Oracleで偶数丸め(JIS丸め)

Oracle標準のROUND 関数は四捨五入です。
偶数丸め(JIS丸め、銀行丸め)を行うには関数を自作する必要があります。

基本的な考え方

単純な整数への丸めを行うケースを考えます。

  • 端数がちょうど0.5かどうかを判定するため、絶対値を1で割ります。
    余りが0.5のとき偶数丸めを行う必要がありますが、その他の場合は四捨五入と同じですので、ROUND関数が使えます。
  • 実は、偶数丸めを行うためには、絶対値を1でなく2で割った方が良いです。
    2で割った場合、余りが0.5または1.5のときに偶数丸めが必要となります。
    余りが0.5のときは切り捨て(TRUNCATE)を行い、 余りが1.5のときは切り上げ(四捨五入ですのでROUND関数が使えます)を行うことで偶数丸めが実現できます。

関数

以上を関数にまとめると、以下のようになります。

CREATE OR REPLACE FUNCTION ROUND_HALF_EVEN(
    n       NUMBER,
    integer NUMBER DEFAULT 0
) RETURN NUMBER
IS
BEGIN
    IF MOD(ABS(n) * POWER(10, integer), 2) = 0.5 THEN
        RETURN TRUNC(n, integer);
    ELSE
        RETURN ROUND(n, integer);
    END IF;
END;

Oracle Database 18c から ROUND_TIES_TO_EVEN 関数が実装されました。

2018-11-28

Get Significant Figures in Oracle

Basic Idea

You can use common logarithm (i.e. log base 10) to evaluate the number of digits.
FLOOR(LOG(10, n)) indicates the decimal places of the first non-zero digit as follows.

SQL> WITH DATA AS (
  2  SELECT 1234 N FROM DUAL
  3  UNION ALL SELECT 234 N FROM DUAL
  4  UNION ALL SELECT 34 N FROM DUAL
  5  UNION ALL SELECT 4 N FROM DUAL
  6  --UNION ALL SELECT 0 N FROM DUAL
  7  UNION ALL SELECT 0.123 N FROM DUAL
  8  UNION ALL SELECT 0.0234 N FROM DUAL
  9  UNION ALL SELECT 0.00345 N FROM DUAL
 10  )
 11  SELECT N, FLOOR(LOG(10, N)) FROM DATA;

         N FLOOR(LOG(10,N))
---------- ----------------
      1234                3
       234                2
        34                1
         4                0
      .123               -1
     .0234               -2
    .00345               -3

Note: LOG(10, 0) causes an error: ORA-01428: argument '0' is out of range

Therefore, you can get the value rounded to the first digit of a given number n by ROUND(n, - FLOOR(LOG(10, n))).

SQL> WITH DATA AS (
  2  SELECT 1234 N FROM DUAL
  3  UNION ALL SELECT 234 N FROM DUAL
  4  UNION ALL SELECT 34 N FROM DUAL
  5  UNION ALL SELECT 4 N FROM DUAL
  6  --UNION ALL SELECT 0 N FROM DUAL
  7  UNION ALL SELECT 0.123 N FROM DUAL
  8  UNION ALL SELECT 0.0234 N FROM DUAL
  9  UNION ALL SELECT 0.00345 N FROM DUAL
 10  )
 11  SELECT N, ROUND(n, - FLOOR(LOG(10, N))) FROM DATA;

         N ROUND(N,-FLOOR(LOG(10,N)))
---------- --------------------------
      1234                       1000
       234                        200
        34                         30
         4                          4
      .123                         .1
     .0234                        .02
    .00345                       .003

In order to round to first d digits, you can modify as ROUND(n, d - FLOOR(LOG(10, n)) - 1).

Function

These can be summarized as the following function.

CREATE OR REPLACE FUNCTION SIGNIFICANT_FIGURES(
    n   NUMBER,
    d   NUMBER
) RETURN NUMBER
IS
BEGIN
    IF n = 0 THEN
        RETURN 0;
    ELSE
        RETURN ROUND(n, d - FLOOR(LOG(10, n)) - 1);
    END IF;
END;

If you want to round half to even, use ROUND_HALF_EVEN function instead of the standard ROUND function.
Note: From Oracle Database 18c, you can use ROUND_TIES_TO_EVEN function.

2018-11-27

Round Half to Even in Oracle

Oracle standard ROUND function is rounding away from 0.
In order to round half to even (aka bankers’ rounding) in Oracle, you have to create a custom function.

Basic idea

Consider the case of rounding to an integer.

  • To judge whether the number is half-way between two integers, you have to divide the absolute value by 1 and see the remainder.
    If the remainder is 0.5, you need to round to even. Otherwise, you can use the standard ROUND function
  • Actually, you had better divide the absolute value by 2 rather than 1 in order to round half to even.
    In this case, you need to round to even when the remainder is 0.5 or 1.5.
    When the remainder is 0.5, you have to round towards 0 (i.e. truncate).
    When the remainder is 1.5, you have to round away from 0. (You can use the standard ROUND function.)

Function

These ideas can be summarized as the following function.

CREATE OR REPLACE FUNCTION ROUND_HALF_EVEN(
    n       NUMBER,
    integer NUMBER DEFAULT 0
) RETURN NUMBER
IS
BEGIN
    IF MOD(ABS(n) * POWER(10, integer), 2) = 0.5 THEN
        RETURN TRUNC(n, integer);
    ELSE
        RETURN ROUND(n, integer);
    END IF;
END;

From Oracle Database 18c, you can use ROUND_TIES_TO_EVEN function.

4

2018-02-28

Oracle 12c SCOTTユーザーの権限を確認する

前の記事「Oracle CONNECTおよびRESOURCEロールのまとめ」を書いていて、ふと気になったのですが、サンプル・スキーマのSCOTTユーザーの権限ははどうなっているのでしょうか。

SCOTTユーザーの権限は非推奨の CONNECTロールとRESOURCE ロールを使用していたはずです。Oracle12c では非推奨なので使われなくなっているのでしょうか。

サンプルスキーマの作成スクリプト ORACLE_HOME/rdbms/admin/utlsampl.sql を見ると、

GRANT CONNECT,RESOURCE,UNLIMITED TABLESPACE TO SCOTT IDENTIFIED BY tiger;

となっていました。

まだ、非推奨の RESOURCE ロールと CONNECT ロールが使われているようですね。
また、12c から RESOURCE ロールでは付与されなくなったUNLIMITED TABLESPACE 権限が別に付与されています。コメントに、

Rem mmoore 04/08/91 - use unlimited tablespace priv

と記載されていますので、だいぶ昔から RESOURCE ロールとは別に付与されていたようです。


データディクショナリで確認してみます。

SQL> select * from user_role_privs;

GRANTEE    GRANTED_ROLE
---------- -------------
SCOTT      RESOURCE
SCOTT      CONNECT
SQL> select * from user_sys_privs;

USERNAME  PRIVILEGE            
--------- ---------------------
SCOTT     UNLIMITED TABLESPACE


現セッションで利用できる権限を見てみます。

SQL> select * from session_privs;

PRIVILEGE
----------------------------------------
SET CONTAINER
CREATE INDEXTYPE
CREATE OPERATOR
CREATE TYPE
CREATE TRIGGER
CREATE PROCEDURE
CREATE SEQUENCE
CREATE CLUSTER
CREATE TABLE
UNLIMITED TABLESPACE
CREATE SESSION

昔、CONNECT ロールに付与されていた

  • ALTER SESSION
  • CREATE DATABASE LINK
  • CREATE SYNONYM
  • CREATE VIEW

は付与されてないですね。

非CDB環境で確認しているのですが、非CDBでは必要ないはずのコンテナの切替権限 SET CONTAINER が付与されています。
CONNECT ロールに新たに付与されているようです。

SQL> select * from role_sys_privs where ROLE = 'CONNECT';
ROLE                    PRIVILEGE
-------------------- --------------
CONNECT             SET CONTAINER
CONNECT             CREATE SESSION

2018-02-23

Oracle CONNECTおよびRESOURCEロールのまとめ

Oracle Database 10g R2 から「最低限の権限」原則により CONNECT および RESOURCE ロールは非推奨となった。

Oracle Database セキュリティ・ガイド 10gリリース2(10.2)- 認可: 権限、ロール、プロファイルおよびリソースの制限

注意:
CONNECTおよびRESOURCEロールは、将来のOracle Databaseのリリースで非推奨になる予定のため、使用しないでください。 CONNECTロールが現在保持している権限は、CREATE SESSIONのみです。

なお、非推奨ではあるものの Oracle 12g R2 でも CONNECTおよびRESOURCEロールは事前定義されている。

Oracle® Databaseセキュリティ・ガイド 12cリリース2 (12.2) - Oracle Databaseのインストールで事前に定義されているロール

CONNECTロール

前述の引用にもあるとおり 10g R2 からCONNECT ロールの権限は CREATE SESSION のみになったが、それ以前は以下の権限が付与されていた。

Oracle Database セキュリティ・ガイド 10gリリース2(10.2)- CONNECTロール変更への対処

  • ALTER SESSION
  • CREATE CLUSTER
  • CREATE DATABASE LINK
  • CREATE SEQUENCE
  • CREATE SESSION
  • CREATE SYNONYM
  • CREATE TABLE
  • CREATE VIEW

Oracle 12c から CONNECT ロールに新たに SET CONTAINER 権限が付与されているようです。

RESOURCEロール

RESOURECE ロールに付与されている権限は以下のとおり。

Oracle® Databaseセキュリティ・ガイド 12cリリース2 (12.2) - Oracle Databaseのインストールで事前に定義されているロール

  • CREATE CLUSTER
  • CREATE INDEXTYPE
  • CREATE OPERATOR
  • CREATE PROCEDURE
  • CREATE SEQUENCE
  • CREATE TABLE
  • CREATE TRIGGER
  • CREATE TYPE

Oracle 12c R1 から RESOUCEロールは UNLIMITED TABLESPACE 権限を付与しなくなった。

Oracle® Databaseセキュリティ・ガイド 12cリリース1 (12.1) - Oracle Databaseセキュリティ・ガイドのこのリリースの変更

このリリース以降、RESOURCEロールがデフォルトでUNLIMITED TABLESPACEシステム権限を付与しなくなりました。このシステム権限をユーザーに付与する場合、手動で付与する必要があります。

なお、 UNLIMITED TABLESPACE はロールに付与することはできないので以下のように直接ユーザに付与する。

GRANT UNLIMITED TABLESPACE TO username;

参考

2018-02-01

Oracle 非CDB(Non-CDB)はいつまで使えるのか

Oracle 非CDB(Non-CDB)アーキテクチャは、12c から非推奨となっているのですが、いつまで使える(サポートされる)のか現時点の情報をまとめておきます。

まず、日本語版の「Oracle® Databaseアップグレード・ガイド 12cリリース2 (12.2) 」

Oracle Databaseアップグレード・ガイドのこのリリースでの変更

The new release of Oracle Database provides improvements to upgrade performance, automation, and reporting.

には、

  • 非CDBアーキテクチャの非推奨
    非CDBアーキテクチャはOracle Database 12cリリース1では非推奨で、Oracle Database 12c リリース2より後ではサポートされず、使用できない場合があります。

との記載があります。

一方、英語版の ”Oracle® Database Database Upgrade Guide 12c Release 2 (12.2)”

Deprecated Features in Oracle Database 12c Release 2 (12.2)

Review the deprecated features listed in this section to prepare to use alternatives after you upgrade.

には、

  • Deprecation of Non-CDB Architecture
    The non-CDB architecture was deprecated in Oracle Database 12c. It can be desupported and unavailable in a release after Oracle Database 19c.

との記載があります。

英語版の日付は “December 2017” になっているのに、日本語版は「2017年7月」になっているので日本語版はアップデートされていないようです。近いうちにアップデートされるのかな、と思います。

昨年 18c の発表がありましたが、18c ではなく 19c より後はサポートされないと書いてますね。英語で “after Oracle Database 18c” と書いた場合、「通常」18c は含まれないので “after Oracle Database 18c” で良いと思うのですが、18c まではサポートするということを明確にするため、わざと “after Oracle Database 19c” と記載しているのかもしれません。

こちらは、Oralce の Mike Dietrich さんのブログなのですが、”after Oracle Database 12c Release 2” と書いてあるのに、”12c Release 2” から非CDB構成が使えなくなる(サポートされなくなる)という誤解が結構あったみたいですね。

Non-CDB architecture still exists in Oracle Database 12.2.

Non-CDB architecture still exists in Oracle Database 12.2./ Oracle 18 / Oracle 19. The statement yet about non-CDB disappearing in the near future.

また、非CDBのニーズが思ったより強かったので、18c までサポートすることになったのかもしれません。(推測です。)

まとめ

非CDB(Non-CDB)アーキテクチャは 18c まではサポートされそう。


注: 最新・正確な情報はご自身でご確認ください。

2017-12-12

ODP.NET ラッパー(Facade)クラスの作成

方針

  • コンストラクタで接続(デストラクタで切断)
  • プロバイダファクトリクラス(DbProviderFactory)を利用する。
  • Oracle 固有クラスについてはリフレクションを使用する。
  • Oracle 固有クラスを利用する場合は独立した関数を作成する。
  • パラメータの「名前によるバインド」(BindByName)をデフォルトにする。

コード

ODP.NET管理対象外ドライバ前提です。ODP.NET管理対象ドライバを使用する場合は、Oracle.DataAccess.Client の部分を Oracle.ManagedDataAccess に書き換えてください。(未テスト)

ODP.NET管理対象ドライバについてはこちらに良いまとめがあります。

ODP.NET Managed Driver(管理対象ドライバ)についての私的まとめ - しばたテックブログ

ODP.NET Managed Driverについて自分の欲しい形でまとまった資料がなかったのでここでまとめておきます。 ODP.NET Managed Driver(管理対象ドライバ)とは ODP.NET Managed Driver(管理対象ドライバ)とはOracle 12cから追加された新しい形のODP.NETで…

VB.NET
Imports System
Imports System.Data
Imports System.Data.Common

Public Class DbFacade
    Implements IDisposable

    Private _factory As DbProviderFactory
    Private _cnn As DbConnection
    Private _cmd As DbCommand
    Private _txn As DbTransaction

    Public Sub New()
        Me.New("Data Source=ExampleDb;User ID=SCOTT;Password=TIGER")
    End Sub

    Public Sub New(ByVal connectionString As String)
        _factory = DbProviderFactories.GetFactory("Oracle.DataAccess.Client")
        DbConnect(connectionString)
    End Sub

    Public Function DbConnect(ByVal connectionString As String) As DbConnection
        _cnn = _factory.CreateConnection()
        Me.ConnectionString = connectionString
        Return _cnn
    End Function

    Public Property ConnectionString() As String
        Get
            Return _cnn.ConnectionString
        End Get
        Set(ByVal value As String)
            If _cnn.ConnectionString <> value Then
                _cnn.ConnectionString = value
            End If
        End Set
    End Property

    Public Sub Open()
        If _cnn.State = ConnectionState.Closed Then _cnn.Open()
    End Sub

    Public Sub Close()
        ClearParams()
        If _cnn.State = ConnectionState.Open Then _cnn.Close()
    End Sub

    ''' <summary>
    ''' BindByNameプロパティをセットする
    ''' </summary>
    ''' <param name="isName">True:有効 False:無効</param>
    Private Sub SetBindByName(ByVal cmd As DbCommand, ByVal isName As Boolean)
        cmd.GetType().GetProperty("BindByName").SetValue(cmd, isName, Nothing)
    End Sub

    Public Function CreateCommand() As DbCommand
        Dim cmd As DbCommand = _factory.CreateCommand()
        cmd.Connection = _cnn
        SetBindByName(cmd, True)
        Return cmd
    End Function

    Public Function CreateCommand(ByVal sql As String) As DbCommand
        Dim cmd As DbCommand = CreateCommand()
        cmd.CommandText = sql
        Return cmd
    End Function

    Public Function CreateDataAdapter() As DbDataAdapter
        Return _factory.CreateDataAdapter()
    End Function

    ''' <summary>
    ''' パラメータを追加する。
    ''' </summary>
    ''' <param name="cmd"></param>
    ''' <param name="param"></param>
    ''' <remarks>Removeしなければ、「PLS-00703: リストに名前付き引数のインスタンスが複数あります。」が発生する</remarks>
    Private Sub AddParam(ByVal cmd As DbCommand, ByVal param As DbParameter)
        If cmd.Parameters.Contains(cmd.Parameters(param.ParameterName)) Then
            cmd.Parameters.Remove(cmd.Parameters(param.ParameterName))
        End If
        cmd.Parameters.Add(param)
    End Sub

    ''' <summary>
    ''' パラメータを追加する
    ''' </summary>
    ''' <param name="name">名称</param>
    ''' <param name="value">値</param>
    ''' <param name="type">DbType</param>
    Public Function AddParam(ByVal name As String, ByVal value As Object, ByVal type As DbType) As IDataParameter
        If value.GetType().IsArray Then
            Return AddParamPLSQLAssociativeArray(name, value, type)
        End If
        If _cmd Is Nothing Then _cmd = CreateCommand()
        Dim param As DbParameter = _cmd.CreateParameter
        param.ParameterName = name
        param.DbType = type
        param.Value = value
        AddParam(_cmd, param)
        Return param
    End Function

    ''' <summary>
    ''' パラメータを追加する
    ''' </summary>
    ''' <param name="name">名称</param>
    ''' <param name="type">DbType</param>
    ''' <param name="direction">Direction</param>
    ''' <param name="size">Size</param>
    ''' <remarks>ストアド戻り値用</remarks>
    Public Function AddParam(ByVal name As String, ByVal type As DbType, ByVal direction As ParameterDirection, ByVal size As Integer) As IDataParameter
        If _cmd Is Nothing Then _cmd = CreateCommand()
        Dim param As DbParameter = _cmd.CreateParameter()
        param.ParameterName = name
        param.DbType = type
        param.Direction = direction
        param.Size = size
        AddParam(_cmd, param)
        Return param
    End Function

    ''' <summary>
    ''' パラメータを追加する
    ''' </summary>
    ''' <param name="cmd">DbCommand</param>
    ''' <param name="name">名称</param>
    ''' <param name="sourceColumn">列名</param>
    ''' <param name="type">DbType</param>
    ''' <remarks>DataAdapter用</remarks>
    Public Function AddParam(ByVal cmd As DbCommand, ByVal name As String, ByVal sourceColumn As String, ByVal type As DbType) As IDataParameter
        Dim param As DbParameter = cmd.CreateParameter()
        param.ParameterName = name
        param.SourceColumn = sourceColumn
        param.DbType = type
        AddParam(cmd, param)
        Return param
    End Function

    ''' <summary>
    ''' RefCursor型のパラメータを追加する
    ''' </summary>
    ''' <param name="name">名称</param>
    Public Function AddParamRefCursor(ByVal name As String) As IDataParameter
        If _cmd Is Nothing Then _cmd = CreateCommand()

        Dim asm As System.Reflection.Assembly = _factory.GetType().Assembly

        Dim paramType As Type = asm.GetType("Oracle.DataAccess.Client.OracleParameter")
        Dim param As Object = Activator.CreateInstance(paramType)

        paramType.GetProperty("ParameterName").SetValue(param, name, Nothing)
        paramType.GetProperty("Direction").SetValue(param, ParameterDirection.Output, Nothing)

        Dim oraDbType As Type = asm.GetType("Oracle.DataAccess.Client.OracleDbType")
        paramType.GetProperty("OracleDbType").SetValue(param, oraDbType.GetField("RefCursor").GetValue(Nothing), Nothing)
        AddParam(_cmd, DirectCast(param, DbParameter))
        Return DirectCast(param, IDataParameter)
    End Function

    ''' <summary>
    ''' PL/SQL連想配列のパラメータを追加する
    ''' </summary>
    ''' <param name="name">名称</param>
    ''' <param name="value">値</param>
    ''' <param name="type">DbType</param>
    Public Function AddParamPLSQLAssociativeArray(ByVal name As String, ByVal value As Object, ByVal type As DbType) As IDataParameter
        If _cmd Is Nothing Then _cmd = CreateCommand()

        Dim asm As System.Reflection.Assembly = _factory.GetType().Assembly

        Dim oraParamType As Type = asm.GetType("Oracle.DataAccess.Client.OracleParameter")
        Dim oraParam As Object = Activator.CreateInstance(oraParamType)

        oraParamType.GetProperty("ParameterName").SetValue(oraParam, name, Nothing)
        oraParamType.GetProperty("Size").SetValue(oraParam, CType(value, Array).Length, Nothing)

        Dim oraCollectionType As Type = asm.GetType("Oracle.DataAccess.Client.OracleCollectionType")
        oraParamType.GetProperty("CollectionType").SetValue(oraParam, oraCollectionType.GetField("PLSQLAssociativeArray").GetValue(Nothing), Nothing)

        Dim param As IDataParameter = DirectCast(oraParam, IDataParameter)
        param.DbType = type
        param.Value = value

        AddParam(_cmd, DirectCast(param, DbParameter))

        Return param
    End Function

    ''' <summary>
    ''' パラメータをクリアする
    ''' </summary>
    Public Sub ClearParams()
        If _cmd Is Nothing Then Exit Sub
        _cmd.Parameters.Clear()
    End Sub

    ''' <summary>
    ''' DataTableを返す
    ''' </summary>
    ''' <param name="sql"></param>
    Public Function GetDataTable(ByVal sql As String) As DataTable
        If _cmd Is Nothing Then _cmd = CreateCommand()
        _cmd.CommandText = sql
        Dim da As DbDataAdapter = _factory.CreateDataAdapter()
        da.SelectCommand = _cmd
        Dim dt As DataTable = New DataTable
        da.Fill(dt)
        Return dt
    End Function

    ''' <summary>
    ''' RefCursorをDataSetにセットする
    ''' </summary>
    ''' <param name="dataSet"></param>
    ''' <param name="procedureName"></param>
    Public Sub FillRefCursor(ByRef dataSet As DataSet, ByVal procedureName As String)
        If _cmd Is Nothing Then _cmd = CreateCommand()
        _cmd.CommandText = procedureName
        _cmd.CommandType = CommandType.StoredProcedure
        Dim da As DbDataAdapter = _factory.CreateDataAdapter()
        da.SelectCommand = _cmd
        da.Fill(dataSet)
    End Sub

    ''' <summary>
    ''' RefCursorをDataTableにセットする
    ''' </summary>
    ''' <param name="dataTable"></param>
    ''' <param name="procedureName"></param>
    Public Sub FillRefCursor(ByRef dataTable As DataTable, ByVal procedureName As String)
        If _cmd Is Nothing Then _cmd = CreateCommand()
        _cmd.CommandText = procedureName
        _cmd.CommandType = CommandType.StoredProcedure
        Dim da As DbDataAdapter = _factory.CreateDataAdapter()
        da.SelectCommand = _cmd
        da.Fill(dataTable)
    End Sub

    ''' <summary>
    ''' SQLを実行する
    ''' </summary>
    ''' <param name="sql"></param>
    Public Function ExecuteSql(ByVal sql As String) As Integer
        If _cmd Is Nothing Then _cmd = CreateCommand()
        _cmd.CommandText = sql
        Open()
        Dim result As Integer = _cmd.ExecuteNonQuery()
        If _txn Is Nothing OrElse _txn.Connection Is Nothing Then
            Close()
        End If
        Return result
    End Function

    ''' <summary>
    ''' ストアドプロシージャを実行する
    ''' </summary>
    ''' <param name="procedureName"></param>
    Public Function ExecuteStored(ByVal procedureName As String) As Integer
        If _cmd Is Nothing Then _cmd = CreateCommand()
        _cmd.CommandType = CommandType.StoredProcedure
        _cmd.CommandText = procedureName
        Open()
        Dim result As Integer = _cmd.ExecuteNonQuery()
        If _txn Is Nothing OrElse _txn.Connection Is Nothing Then
            Close()
        End If
        Return result
    End Function

    Public Function ExecuteReader(ByVal sql As String) As DbDataReader
        If _cmd Is Nothing Then _cmd = CreateCommand()
        _cmd.CommandText = sql
        Open()
        Dim result As DbDataReader = _cmd.ExecuteReader(CommandBehavior.CloseConnection)
        ' Close()
        Return result
    End Function

    Public Function ExecuteScalar(ByVal sql As String) As Object
        If _cmd Is Nothing Then _cmd = CreateCommand()
        _cmd.CommandText = sql
        Open()
        Dim result as Object =_cmd.ExecuteScalar()
        Close()
        Return result
    End Function

    Public Sub BeginTransaction()
        Open()
        _txn = _cnn.BeginTransaction()
    End Sub

    Public Sub Commit()
        _txn.Commit()
        Close()
    End Sub

    Public Sub Rollback()
        _txn.Rollback()
        Close()
    End Sub

    Private disposedValue As Boolean = False        ' 重複する呼び出しを検出するには

    ' IDisposable
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' TODO: 明示的に呼び出されたときにマネージ リソースを解放します
                If _txn IsNot Nothing Then _txn.Dispose()
                If _cmd IsNot Nothing Then _cmd.Dispose()
                If _cnn IsNot Nothing Then
                    If _cnn.State = ConnectionState.Open Then
                        _cnn.Close()
                    End If
                    _cnn.Dispose()
                End If
            End If

            ' TODO: 共有のアンマネージ リソースを解放します
        End If
        Me.disposedValue = True
    End Sub

#Region " IDisposable Support "
    ' このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
    Public Sub Dispose() Implements IDisposable.Dispose
        ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(ByVal disposing As Boolean) に記述します。
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region

End Class
C#
using System;
using System.Data;
using System.Data.Common;

public class DbFacade : IDisposable
{
    private DbProviderFactory _factory;
    private DbConnection _cnn;
    private DbCommand _cmd;
    private DbTransaction _txn;

    public DbFacade() : this("Data Source=ExampleDb;User ID=SCOTT;Password=TIGER"){}

    public DbFacade(string connectionString)
    {
        _factory = DbProviderFactories.GetFactory("Oracle.DataAccess.Client");
        DbConnect(connectionString);
    }

    public DbConnection DbConnect(string connectionString)
    {
        _cnn = _factory.CreateConnection();
        ConnectionString = connectionString;
        return _cnn;
    }

    public string ConnectionString
    {
        get
        {
            return _cnn.ConnectionString;
        }
        set
        {
            if ((_cnn.ConnectionString != value))
            {
                _cnn.ConnectionString = value;
            }
        }
    }

    public void Open()
    {
        if ((_cnn.State == ConnectionState.Closed))
        {
            _cnn.Open();
        }
    }

    void Close()
    {
        ClearParams();
        if (_cnn.State == ConnectionState.Open)
        {
            _cnn.Close();
        }
    }

    /// <summary>
    /// BindByNameプロパティをセットする
    /// </summary>
    /// <param name="isName">True:有効 False:無効</param>
    void SetBindByName(DbCommand cmd, bool isName)
    {
        cmd.GetType().GetProperty("BindByName").SetValue(cmd, isName, null);
    }

    public DbCommand CreateCommand()
    {
        DbCommand cmd = _factory.CreateCommand();
        cmd.Connection = _cnn;
        SetBindByName(cmd, true);
        return cmd;
    }

    public DbCommand CreateCommand(string sql)
    {
        DbCommand cmd = CreateCommand();
        cmd.CommandText = sql;
        return cmd;
    }

    public DbDataAdapter CreateDataAdapter()
    {
        return _factory.CreateDataAdapter();
    }

    /// <summary>
    /// パラメータを追加する。
    /// </summary>
    /// <param name="cmd"></param>
    /// <param name="param"></param>
    /// <remarks>Removeしなければ、「PLS-00703: リストに名前付き引数のインスタンスが複数あります。」が発生する</remarks>
    private void AddParam(DbCommand cmd, DbParameter param)
    {
        if (cmd.Parameters.Contains(cmd.Parameters[param.ParameterName]))
        {
            cmd.Parameters.Remove(cmd.Parameters[param.ParameterName]);
        }
        cmd.Parameters.Add(param);
    }

    /// <summary>
    /// パラメータを追加する
    /// </summary>
    /// <param name="name">名称</param>
    /// <param name="value">値</param>
    /// <param name="type">DbType</param>
    public IDataParameter AddParam(string name, object value, DbType type)
    {
        if (value.GetType().IsArray)
        {
            return AddParamPLSQLAssociativeArray(name, value, type);
        }
        if (_cmd == null)
        {
            _cmd = CreateCommand();
        }
        DbParameter param = _cmd.CreateParameter();
        param.ParameterName = name;
        param.DbType = type;
        param.Value = value;
        AddParam(_cmd, param);
        return param;
    }

    /// <summary>
    /// パラメータを追加する
    /// </summary>
    /// <param name="name">名称</param>
    /// <param name="type">DbType</param>
    /// <param name="direction">Direction</param>
    /// <param name="size">Size</param>
    /// <remarks>ストアド戻り値用</remarks>
    public IDataParameter AddParam(string name, DbType type, ParameterDirection direction, int size)
    {
        if (_cmd == null)
        {
            _cmd = CreateCommand();
        }
        DbParameter param = _cmd.CreateParameter();
        param.ParameterName = name;
        param.DbType = type;
        param.Direction = direction;
        param.Size = size;
        AddParam(_cmd, param);
        return param;
    }

    /// <summary>
    /// パラメータを追加する
    /// </summary>
    /// <param name="cmd">DbCommand</param>
    /// <param name="name">名称</param>
    /// <param name="sourceColumn">列名</param>
    /// <param name="type">DbType</param>
    /// <remarks>DataAdapter用</remarks>
    public IDataParameter AddParam(DbCommand cmd, string name, string sourceColumn, DbType type)
    {
        DbParameter param = cmd.CreateParameter();
        param.ParameterName = name;
        param.SourceColumn = sourceColumn;
        param.DbType = type;
        AddParam(cmd, param);
        return param;
    }

    /// <summary>
    /// RefCursor型のパラメータを追加する
    /// </summary>
    /// <param name="name">名称</param>
    public IDataParameter AddParamRefCursor(string name)
    {
        if (_cmd == null)
        {
            _cmd = CreateCommand();
        }
        System.Reflection.Assembly asm = _factory.GetType().Assembly;
        Type paramType = asm.GetType("Oracle.DataAccess.Client.OracleParameter");
        object param = Activator.CreateInstance(paramType);
        paramType.GetProperty("ParameterName").SetValue(param, name, null);
        paramType.GetProperty("Direction").SetValue(param, ParameterDirection.Output, null);
        Type oraDbType = asm.GetType("Oracle.DataAccess.Client.OracleDbType");
        paramType.GetProperty("OracleDbType").SetValue(param, oraDbType.GetField("RefCursor").GetValue(null), null);
        AddParam(_cmd, param as DbParameter);
        return param as IDataParameter;
    }

    /// <summary>
    /// PL/SQL連想配列のパラメータを追加する
    /// </summary>
    /// <param name="name">名称</param>
    /// <param name="value">値</param>
    /// <param name="type">DbType</param>
    public IDataParameter AddParamPLSQLAssociativeArray(string name, object value, DbType type)
    {
        if (_cmd == null)
        {
            _cmd = CreateCommand();
        }
        System.Reflection.Assembly asm = _factory.GetType().Assembly;
        Type oraParamType = asm.GetType("Oracle.DataAccess.Client.OracleParameter");
        object oraParam = Activator.CreateInstance(oraParamType);
        oraParamType.GetProperty("ParameterName").SetValue(oraParam, name, null);
        oraParamType.GetProperty("Size").SetValue(oraParam, ((Array)(value)).Length, null);
        Type oraCollectionType = asm.GetType("Oracle.DataAccess.Client.OracleCollectionType");
        oraParamType.GetProperty("CollectionType").SetValue(oraParam, oraCollectionType.GetField("PLSQLAssociativeArray").GetValue(null), null);
        IDataParameter param = ((IDataParameter)(oraParam));
        param.DbType = type;
        param.Value = value;
        AddParam(_cmd, param as DbParameter);
        return param;
    }

    /// <summary>
    /// パラメータをクリアする
    /// </summary>
    public void ClearParams()
    {
        if (_cmd == null)
        {
            return;
        }
        _cmd.Parameters.Clear();
    }

    /// <summary>
    /// DataTableを返す
    /// </summary>
    /// <param name="sql"></param>
    public DataTable GetDataTable(string sql)
    {
        if (_cmd == null)
        {
            _cmd = CreateCommand();
        }
        _cmd.CommandText = sql;
        DbDataAdapter da = _factory.CreateDataAdapter();
        da.SelectCommand = _cmd;
        DataTable dt = new DataTable();
        da.Fill(dt);
        return dt;
    }

    /// <summary>
    /// RefCursorをDataSetにセットする
    /// </summary>
    /// <param name="dataSet"></param>
    /// <param name="procedureName"></param>
    public void FillRefCursor(ref DataSet dataSet, string procedureName)
    {
        if (_cmd == null)
        {
            _cmd = CreateCommand();
        }
        _cmd.CommandText = procedureName;
        _cmd.CommandType = CommandType.StoredProcedure;
        DbDataAdapter da = _factory.CreateDataAdapter();
        da.SelectCommand = _cmd;
        da.Fill(dataSet);
    }

    /// <summary>
    /// RefCursorをDataTableにセットする
    /// </summary>
    /// <param name="dataTable"></param>
    /// <param name="procedureName"></param>
    public void FillRefCursor(ref DataTable dataTable, string procedureName)
    {
        if (_cmd == null)
        {
            _cmd = CreateCommand();
        }
        _cmd.CommandText = procedureName;
        _cmd.CommandType = CommandType.StoredProcedure;
        DbDataAdapter da = _factory.CreateDataAdapter();
        da.SelectCommand = _cmd;
        da.Fill(dataTable);
    }

    /// <summary>
    /// SQLを実行する
    /// </summary>
    /// <param name="sql"></param>
    public int ExecuteSql(string sql)
    {
        if (_cmd == null)
        {
            _cmd = CreateCommand();
        }
        _cmd.CommandText = sql;
        Open();
        int result = _cmd.ExecuteNonQuery();
        if (_txn == null || _txn.Connection == null)
        {
            Close();
        };
        return result;
    }

    /// <summary>
    /// ストアドプロシージャを実行する
    /// </summary>
    /// <param name="procedureName"></param>
    public int ExecuteStored(string procedureName)
    {
        if (_cmd == null)
        {
            _cmd = CreateCommand();
        }
        _cmd.CommandType = CommandType.StoredProcedure;
        _cmd.CommandText = procedureName;
        Open();
        int result = _cmd.ExecuteNonQuery();
        if (_txn == null || _txn.Connection == null)
        {
            Close();
        };
        return result;
    }

    public DbDataReader ExecuteReader(string sql)
    {
        if (_cmd == null)
        {
            _cmd = CreateCommand();
        }
        _cmd.CommandText = sql;
        Open();
        DbDataReader result = _cmd.ExecuteReader(CommandBehavior.CloseConnection);
        // Close();
        return result;
    }

    public object ExecuteScalar(string sql)
    {
        if (_cmd == null)
        {
            _cmd = CreateCommand();
        }
        _cmd.CommandText = sql;
        Open();
        object result = _cmd.ExecuteScalar();
        Close();
        return result;
    }

    public void BeginTransaction()
    {
        Open();
        _txn = _cnn.BeginTransaction();
    }

    public void Commit()
    {
        _txn.Commit();
        Close();
    }

    public void Rollback()
    {
        _txn.Rollback();
        Close();
    }

    #region IDisposable Support
    private bool disposedValue = false; // 重複する呼び出しを検出するには

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: マネージ状態を破棄します (マネージ オブジェクト)。
                if (_txn != null)
                {
                    _txn.Dispose();
                }
                if (_cmd != null)
                {
                    _cmd.Dispose();
                }
                if (_cnn != null)
                {
                    if (_cnn.State == ConnectionState.Open)
                    {
                        _cnn.Close();
                    }
                    _cmd.Dispose();
                }
            }

            // TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下のファイナライザーをオーバーライドします。
            // TODO: 大きなフィールドを null に設定します。

            disposedValue = true;
        }
    }

    // TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードが含まれる場合にのみ、ファイナライザーをオーバーライドします。
    // ~DbFacade() {
    //   // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
    //   Dispose(false);
    // }

    // このコードは、破棄可能なパターンを正しく実装できるように追加されました。
    public void Dispose()
    {
        // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
        Dispose(true);
        // TODO: 上のファイナライザーがオーバーライドされる場合は、次の行のコメントを解除してください。
        // GC.SuppressFinalize(this);
    }
    #endregion
}

利用方法

とりあえず、

VB.NET
Using db As New DbFacade()
    'Update
    db.ExecuteSql("UPDATE emp SET sal=sal*1.1 WHERE empno=7369")
    'DataTable
    Dim dt As DataTable = db.GetDataTable("select * from EMP")
End Using
C#
using (DbFacade db = new DbFacade())
{
    // Update
    db.ExecuteSql("UPDATE emp SET sal=sal*1.1 WHERE empno=7369");
    // DataTable
    DataTable dt = db.GetDataTable("select * from EMP");
}