DataReader를 사용한 데이터 검색

DataReader를 사용하여 데이터를 검색하려면 Command 개체의 인스턴스를 만든 다음 데이터 원본에서 행을 검색하려면 Command.ExecuteReader를 호출하여 DataReader를 만듭니다. DataReader는 프로시저 논리가 데이터 원본에서 순차적으로 가져오는 결과를 효율적으로 처리할 수 있도록 버퍼링되지 않은 데이터 스트림을 제공합니다. DataReader는 데이터가 메모리에 캐시되지 않기 때문에 대량의 데이터를 검색할 때 좋은 선택입니다.

다음 예제에서는 reader가 올바른 DataReader를 나타내고 command가 올바른 Command 개체를 나타낼 때 DataReader를 사용하는 방법을 보여 줍니다.

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

쿼리 결과에서 행을 가져오려면 DataReader.Read 메서드를 사용합니다. 열의 이름이나 서수를 DataReader에 전달하여 반환된 행의 각 열에 액세스할 수 있습니다. 그러나 최고의 성능을 위해 DataReaderGetDateTime, GetDouble, GetGuid, GetInt32 등의 네이티브 데이터 형식의 열 값에 액세스할 수 있도록 하는 일련의 메서드를 제공합니다. 데이터 공급자별 DataReaders의 형식화된 접근자 메서드 목록을 보려면 OleDbDataReaderSqlDataReader를 참조하세요. 기본 데이터 형식을 알고 있을 때 형식화된 접근자 메서드를 사용하면 열 값을 검색할 때 필요한 형식 변환의 양이 줄어듭니다.

다음 예에서는 DataReader 개체를 반복하고 각 행에서 두 개의 열을 반환합니다.

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가 열려 있는 동안에는 해당 DataReader에서 Connection을 단독으로 사용합니다. DataReader를 닫아야 다른 DataReader의 작성을 비롯하여 Connection에 대해 명령을 실행할 수 있습니다.

참고 항목

Connection, DataReader 또는 클래스의 Finalize 메서드에 있는 다른 모든 관리형 개체에 대해 Close 또는 Dispose를 호출하지 마세요. 종료자에서는 클래스에 직접 속한 관리되지 않는 리소스만 해제합니다. 클래스에 관리되지 않는 리소스가 없는 경우 클래스 정의에 Finalize 메서드를 포함하지 마세요. 자세한 내용은 가비지 컬렉션을 참조하세요.

NextResult를 사용하여 여러 개의 결과 집합 검색

DataReader가 여러 결과 집합을 반환하는 경우 NextResult 메서드를 호출하여 결과 집합을 순차적으로 반복합니다. 다음 예제에서는 SqlDataReaderExecuteReader 메서드를 사용하여 두 가지 SELECT 문 결과를 처리하는 방법을 보여 줍니다.

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은 결과 집합의 각 열마다 한 행씩 포함하게 됩니다. 스키마 테이블의 각 열은 결과 집합의 행에 반환된 열의 속성에 매핑됩니다. 여기서 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를 사용할 수 있습니다. 자세한 내용은 DataSets, DataTable, DataViews를 참조하세요.

다음 코드 예제에서는 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 개체를 검색할 수 있습니다. 하나 이상의 Oracle REF CURSOR를 DataSet를 채우는 데 사용되는 OracleDataAdapter에 대한 SelectCommand로 반환하는 OracleCommand를 지정할 수도 있습니다.

Oracle 데이터 소스에서 반환된 REF CURSOR에 액세스하려면 쿼리용 OracleCommand를 만들고 REF CURSOR를 참조하는 출력 매개 변수를 OracleCommandParameters 컬렉션에 추가합니다. 매개 변수 이름은 쿼리의 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 출력 매개 변수 이름과 같기는 하지만 Parameters 컬렉션에 추가된 순서에 따라 OracleDataReader에 의해 액세스됩니다.

예를 들어, 다음과 같은 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;

다음 코드에서는 OracleType.Cursor 형식의 두 매개 변수를 OracleCommand.Parameters 컬렉션에 추가하여 이전 Oracle 패키지에서 REF CURSOR를 반환하는 OracleCommand를 만듭니다.

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();  

다음 예제에서는 위의 명령을 사용하여 DataSet에 Oracle 패키지의 결과를 채웁니다.

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);  

참고 항목

OverflowException을 방지하려면 DataRow에 값을 저장하기 전에 Oracle NUMBER 형식을 유효한 .NET Framework 형식으로 변환하는 것이 좋습니다. FillError 이벤트를 사용하면 OverflowException의 발생 여부를 알 수 있습니다. FillError 이벤트에 관한 자세한 내용은 DataAdapter 이벤트 처리를 참조하세요.

참고 항목