DataReader を使用してデータを取得する

DataReader を使用してデータを取得するには、Command オブジェクトのインスタンスを作成した後、Command.ExecuteReader を呼び出して DataReader を作成し、データ ソースから行を取得します。 DataReader では、手続きロジックがデータ ソースからの結果を順番に効率的に処理できるようにするバッファリングされないデータ ストリームが提供されます。 DataReader はデータをメモリにキャッシュしないため、大量のデータを取得する場合に適しています。

DataReader を使用する例を次に示します。ここで、reader は有効な DataReader を、command は有効な Command オブジェクトを表します。

reader = command.ExecuteReader();  
reader = command.ExecuteReader()

クエリ結果から行を取得するには、DataReader.Read メソッドを使用します。 返された行の各列にアクセスするには、その列の名前または序数を DataReader に渡します。 ただし、DataReader では、最適のパフォーマンスが得られるように、ネイティブのデータ型を使用して列の値にアクセスできる一連のメソッド (GetDateTimeGetDoubleGetGuidGetInt32 など) が提供されています。 データ プロバイダー固有の DataReader に対する型指定されたアクセサー メソッドの一覧については、OleDbDataReader および SqlDataReader を参照してください。 基になるデータ型がわかっているときは、型指定されたアクセサー メソッドを使用すると、列の値を取得するときに必要な型変換の量が少なくなります。

次に、DataReader オブジェクトを反復処理して各行から 2 つの列を返す例を示します。

static void HasRows(SqlConnection connection)
{
    using (connection)
    {
        SqlCommand command = new(
          "SELECT CategoryID, CategoryName FROM Categories;",
          connection);
        connection.Open();

        SqlDataReader reader = command.ExecuteReader();

        if (reader.HasRows)
        {
            while (reader.Read())
            {
                Console.WriteLine("{0}\t{1}", reader.GetInt32(0),
                    reader.GetString(1));
            }
        }
        else
        {
            Console.WriteLine("No rows found.");
        }
        reader.Close();
    }
}
Private Sub HasRows(ByVal connection As SqlConnection)
    Using connection
        Dim command As SqlCommand = New SqlCommand( _
          "SELECT CategoryID, CategoryName FROM Categories;", _
          connection)
        connection.Open()

        Dim reader As SqlDataReader = command.ExecuteReader()

        If reader.HasRows Then
            Do While reader.Read()
                Console.WriteLine(reader.GetInt32(0) _
                  & vbTab & reader.GetString(1))
            Loop
        Else
            Console.WriteLine("No rows found.")
        End If

        reader.Close()
    End Using
End Sub

DataReader の終了

DataReader オブジェクトを使い終えたら、Close メソッドを必ず呼び出します。

Command に出力パラメーターまたは戻り値が含まれている場合、DataReader が閉じられるまで、それらの値を使用することはできません。

DataReader が開かれている間、Connection はその DataReader によって排他的に使用されています。 元の DataReader が閉じられるまでは、別の DataReader の作成など、どのようなコマンドもその Connection に対して実行できません。

Note

クラスの Finalize メソッド内では、ConnectionDataReader、またはその他のマネージド オブジェクトに対して、Close または Dispose を呼び出さないでください。 終了処理では、クラスに直接所有されているアンマネージ リソースだけを解放してください。 クラスがアンマネージド リソースを所有していない場合は、クラス定義に Finalize メソッドを含めないでください。 詳しくは、「ガベージ コレクション」をご覧ください。

NextResult による複数の結果セットの取得

DataReader から複数の結果セットが返される場合は、NextResult メソッドを呼び出して、結果セットを順番に反復処理します。 SqlDataReader メソッドを使用して、2 つの SELECT ステートメントの結果を処理する ExecuteReader の例を次に示します。

static void RetrieveMultipleResults(SqlConnection connection)
{
    using (connection)
    {
        SqlCommand command = new(
          "SELECT CategoryID, CategoryName FROM dbo.Categories;" +
          "SELECT EmployeeID, LastName FROM dbo.Employees",
          connection);
        connection.Open();

        SqlDataReader reader = command.ExecuteReader();

        while (reader.HasRows)
        {
            Console.WriteLine("\t{0}\t{1}", reader.GetName(0),
                reader.GetName(1));

            while (reader.Read())
            {
                Console.WriteLine("\t{0}\t{1}", reader.GetInt32(0),
                    reader.GetString(1));
            }
            reader.NextResult();
        }
    }
}
Private Sub RetrieveMultipleResults(ByVal connection As SqlConnection)
    Using connection
        Dim command As SqlCommand = New SqlCommand( _
          "SELECT CategoryID, CategoryName FROM Categories;" & _
          "SELECT EmployeeID, LastName FROM Employees", connection)
        connection.Open()

        Dim reader As SqlDataReader = command.ExecuteReader()

        Do While reader.HasRows
            Console.WriteLine(vbTab & reader.GetName(0) _
              & vbTab & reader.GetName(1))

            Do While reader.Read()
                Console.WriteLine(vbTab & reader.GetInt32(0) _
                  & vbTab & reader.GetString(1))
            Loop

            reader.NextResult()
        Loop
    End Using
End Sub

DataReader からのスキーマ情報の取得

DataReader が開かれている間に GetSchemaTable メソッドを使用して、現在の結果セットについてのスキーマ情報を取得できます。 GetSchemaTable は、現在の結果セットのスキーマ情報を含む行と列が設定されている DataTable オブジェクトを返します。 DataTable には結果セットの列ごとに 1 行が設定されます。 スキーマ テーブルの各列は、結果セットの行で返される列の 1 つのプロパティにマップされます。ColumnName はそのプロパティの名前であり、列の値はプロパティの値です。 DataReader のスキーマ情報を出力する例を次に示します。

static void GetSchemaInfo(SqlConnection connection)
{
    using (connection)
    {
        SqlCommand command = new(
          "SELECT CategoryID, CategoryName FROM Categories;",
          connection);
        connection.Open();

        SqlDataReader reader = command.ExecuteReader();
        DataTable schemaTable = reader.GetSchemaTable();

        foreach (DataRow row in schemaTable.Rows)
        {
            foreach (DataColumn column in schemaTable.Columns)
            {
                Console.WriteLine(string.Format("{0} = {1}",
                   column.ColumnName, row[column]));
            }
        }
    }
}
Private Sub GetSchemaInfo(ByVal connection As SqlConnection)
    Using connection
        Dim command As SqlCommand = New SqlCommand( _
          "SELECT CategoryID, CategoryName FROM Categories;", _
          connection)
        connection.Open()

        Dim reader As SqlDataReader = command.ExecuteReader()
        Dim schemaTable As DataTable = reader.GetSchemaTable()

        Dim row As DataRow
        Dim column As DataColumn

        For Each row In schemaTable.Rows
            For Each column In schemaTable.Columns
                Console.WriteLine(String.Format("{0} = {1}", _
                  column.ColumnName, row(column)))
            Next
            Console.WriteLine()
        Next
        reader.Close()
    End Using
End Sub

OLE DB のチャプターの使用

階層構造の行セット、つまりチャプター (OLE DB では DBTYPE_HCHAPTER 型、ADO では adChapter 型) は、OleDbDataReader を使用して取得できます。 チャプターを含むクエリが DataReader として返されるとき、チャプターはその DataReader の列として返され、DataReader オブジェクトとして公開されます。

ADO.NET の DataSet を使用することで、テーブル間の親子のリレーションシップを使用して階層構造の行セットを表すこともできます。 詳しくは、「DataSet、DataTable、および DataView」をご覧ください。

MSDataShape プロバイダーを使用して、顧客リストの顧客別オーダーのチャプター列を生成するコード サンプルを次に示します。

Using connection As OleDbConnection = New OleDbConnection(
    "Provider=MSDataShape;Data Provider=SQLOLEDB;" &
    "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind")

    Using custCMD As OleDbCommand = New OleDbCommand(
        "SHAPE {SELECT CustomerID, CompanyName FROM Customers} " &
        "APPEND ({SELECT CustomerID, OrderID FROM Orders} AS CustomerOrders " &
        "RELATE CustomerID TO CustomerID)", connection)

        connection.Open()

        Using custReader As OleDbDataReader = custCMD.ExecuteReader()

            Do While custReader.Read()
                Console.WriteLine("Orders for " & custReader.GetString(1))
                ' custReader.GetString(1) = CompanyName  

                Using orderReader As OleDbDataReader = custReader.GetValue(2)
                    ' custReader.GetValue(2) = Orders chapter as DataReader  

                    Do While orderReader.Read()
                        Console.WriteLine(vbTab & orderReader.GetInt32(1))
                        ' orderReader.GetInt32(1) = OrderID  
                    Loop
                    orderReader.Close()
                End Using
            Loop
            ' Make sure to always close readers and connections.  
            custReader.Close()
        End Using
    End Using
End Using
using (OleDbConnection connection = new OleDbConnection(
    "Provider=MSDataShape;Data Provider=SQLOLEDB;" +
    "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=northwind"))
{
    using (OleDbCommand custCMD = new OleDbCommand(
        "SHAPE {SELECT CustomerID, CompanyName FROM Customers} " +
        "APPEND ({SELECT CustomerID, OrderID FROM Orders} AS CustomerOrders " +
        "RELATE CustomerID TO CustomerID)", connection))
    {
        connection.Open();

        using (OleDbDataReader custReader = custCMD.ExecuteReader())
        {

            while (custReader.Read())
            {
                Console.WriteLine("Orders for " + custReader.GetString(1));
                // custReader.GetString(1) = CompanyName  

                using (OleDbDataReader orderReader = (OleDbDataReader)custReader.GetValue(2))
                {
                    // custReader.GetValue(2) = Orders chapter as DataReader  

                    while (orderReader.Read())
                        Console.WriteLine("\t" + orderReader.GetInt32(1));
                    // orderReader.GetInt32(1) = OrderID  
                    orderReader.Close();
                }
            }
            // Make sure to always close readers and connections.  
            custReader.Close();
        }
    }
}

Oracle REF CURSOR による結果の取得

.NET Framework Data Provider for Oracle は、クエリ結果を返すために、Oracle REF CURSOR の使用をサポートしています。 Oracle REF CURSOR は OracleDataReader として返されます。

ExecuteReader メソッドを使用して、Oracle の REF CURSOR を表す OracleDataReader オブジェクトを取得できます。 DataSet を設定するために使用される OracleDataAdapter に対する SelectCommand として 1 つまたは複数の Oracle の REF CURSOR を返す OracleCommand を指定することもできます。

Oracle データ ソースから返された REF CURSOR にアクセスするには、クエリ用の OracleCommand を作成し、OracleCommandParameters コレクションに、REF CURSOR を参照する出力パラメーターを追加します。 パラメーターの名前は、クエリの REF CURSOR パラメーターの名前と一致させる必要があります。 パラメーターの型を OracleType.Cursor に設定します。 OracleCommandOracleCommand.ExecuteReader() メソッドでは、REF CURSOR に対する OracleDataReader が返されます。

OracleCommand で複数の REF CURSOR が返される場合は、複数の出力パラメーターを追加します。 OracleCommand.ExecuteReader() メソッドを呼び出すことにより、異なる REF CURSOR にアクセスできます。 ExecuteReader() を呼び出すと、最初の REF CURSOR を参照している OracleDataReader が返されます。 次に OracleDataReader.NextResult() メソッドを呼び出すと、後続の REF CURSOR にアクセスできます。 OracleCommand.Parameters コレクションのパラメーターが REF CURSOR 出力パラメーターの名前と一致していても、OracleDataReader は、Parameters コレクションに追加された順に出力パラメーターにアクセスします。

たとえば、次のような Oracle パッケージとパッケージ本体があるとします。

CREATE OR REPLACE PACKAGE CURSPKG AS
  TYPE T_CURSOR IS REF CURSOR;
  PROCEDURE OPEN_TWO_CURSORS (EMPCURSOR OUT T_CURSOR,
    DEPTCURSOR OUT T_CURSOR);
END CURSPKG;  
  
CREATE OR REPLACE PACKAGE BODY CURSPKG AS
  PROCEDURE OPEN_TWO_CURSORS (EMPCURSOR OUT T_CURSOR,
    DEPTCURSOR OUT T_CURSOR)
  IS
  BEGIN
    OPEN EMPCURSOR FOR SELECT * FROM DEMO.EMPLOYEE;
    OPEN DEPTCURSOR FOR SELECT * FROM DEMO.DEPARTMENT;
  END OPEN_TWO_CURSORS;
END CURSPKG;

次のコードで作成する OracleCommand では、OracleType.Cursor 型の 2 つのパラメーターを OracleCommand.Parameters コレクションに追加することで、上の Oracle パッケージから REF CURSOR を返します。

Dim cursCmd As OracleCommand = New OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn)  
cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output  
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output  
OracleCommand cursCmd = new OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn);  
cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;  
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;  

OracleDataReaderRead() メソッドと NextResult() メソッドを使用して、上のコマンドの結果を返すコードを次に示します。 REF CURSOR パラメーターは順番に返されます。

oraConn.Open()  
  
Dim cursCmd As OracleCommand = New OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn)  
cursCmd.CommandType = CommandType.StoredProcedure  
cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output  
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output  
  
Dim reader As OracleDataReader = cursCmd.ExecuteReader()  
  
Console.WriteLine(vbCrLf & "Emp ID" & vbTab & "Name")  
  
Do While reader.Read()  
  Console.WriteLine("{0}" & vbTab & "{1}, {2}", reader.GetOracleNumber(0), reader.GetString(1), reader.GetString(2))  
Loop  
  
reader.NextResult()  
  
Console.WriteLine(vbCrLf & "Dept ID" & vbTab & "Name")  
  
Do While reader.Read()  
  Console.WriteLine("{0}" & vbTab & "{1}", reader.GetOracleNumber(0), reader.GetString(1))  
Loop  
' Make sure to always close readers and connections.  
reader.Close()  
oraConn.Close()  
oraConn.Open();  
  
OracleCommand cursCmd = new OracleCommand("CURSPKG.OPEN_TWO_CURSORS", oraConn);  
cursCmd.CommandType = CommandType.StoredProcedure;  
cursCmd.Parameters.Add("EMPCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;  
cursCmd.Parameters.Add("DEPTCURSOR", OracleType.Cursor).Direction = ParameterDirection.Output;  
  
OracleDataReader reader = cursCmd.ExecuteReader();  
  
Console.WriteLine("\nEmp ID\tName");  
  
while (reader.Read())  
  Console.WriteLine("{0}\t{1}, {2}", reader.GetOracleNumber(0), reader.GetString(1), reader.GetString(2));  
  
reader.NextResult();  
  
Console.WriteLine("\nDept ID\tName");  
  
while (reader.Read())  
  Console.WriteLine("{0}\t{1}", reader.GetOracleNumber(0), reader.GetString(1));  
// Make sure to always close readers and connections.  
reader.Close();  
oraConn.Close();  

上のコマンドを使用して、Oracle パッケージの結果を DataSet に設定する例を次に示します。

Dim ds As DataSet = New DataSet()  
  
Dim adapter As OracleDataAdapter = New OracleDataAdapter(cursCmd)  
adapter.TableMappings.Add("Table", "Employees")  
adapter.TableMappings.Add("Table1", "Departments")  
  
adapter.Fill(ds)  
DataSet ds = new DataSet();  
  
OracleDataAdapter adapter = new OracleDataAdapter(cursCmd);  
adapter.TableMappings.Add("Table", "Employees");  
adapter.TableMappings.Add("Table1", "Departments");  
  
adapter.Fill(ds);  

Note

OverflowException が発生しないようにするため、DataRow に値を格納する前に、Oracle の NUMBER 型を有効な .NET Framework のデータ型に変換することをお勧めします。 FillError イベントを使用して、OverflowException が発生したかどうかを確認できます。 FillError イベントについて詳しくは、「DataAdapter のイベント処理」をご覧ください。

関連項目