데이터 리더에서 데이터 테이블 채우기


103

저는 C # (MS VS2008)에서 기본적인 작업을하고 있으며 특정 코드보다 적절한 디자인에 대해 더 많은 질문이 있습니다.

데이터 테이블을 만든 다음 데이터 판독기 (SQL 저장 프로 시저 기반)에서 데이터 테이블을로드하려고합니다. 내가 궁금한 것은 데이터 테이블을로드하는 가장 효율적인 방법이 while 문을 수행하는 것인지 아니면 더 나은 방법이 있는지 여부입니다.

나에게 유일한 단점은 while 문에 추가하려는 필드를 수동으로 입력해야한다는 것입니다.하지만 SP의 모든 필드가 하나만 선택하는 것을 원하지 않기 때문에이를 자동화하는 방법도 모릅니다. , 그러나 그것은 내 눈에 큰 문제가 아닙니다.

내가하는 일의 전체 아래에 코드 조각을 포함 시켰지만, 나에게는 코드 자체가 눈에 띄지 않거나 심지어 내가 요청하는 내용이 아닙니다. 더 나아가 내 방법론에 대해 궁금해하면서 내 전략이 잘못되었거나 비효율적 인 경우 나중에 코드 도움을 요청하겠습니다.

var dtWriteoffUpload = new DataTable();
dtWriteoffUpload.Columns.Add("Unit");
dtWriteoffUpload.Columns.Add("Year");
dtWriteoffUpload.Columns.Add("Period");
dtWriteoffUpload.Columns.Add("Acct");
dtWriteoffUpload.Columns.Add("Descr");
dtWriteoffUpload.Columns.Add("DEFERRAL_TYPE");
dtWriteoffUpload.Columns.Add("NDC_Indicator");
dtWriteoffUpload.Columns.Add("Mgmt Cd");
dtWriteoffUpload.Columns.Add("Prod");
dtWriteoffUpload.Columns.Add("Node");
dtWriteoffUpload.Columns.Add("Curve_Family");
dtWriteoffUpload.Columns.Add("Sum Amount");
dtWriteoffUpload.Columns.Add("Base Curr");
dtWriteoffUpload.Columns.Add("Ledger");  

cmd = util.SqlConn.CreateCommand();
cmd.CommandTimeout = 1000;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "proc_writeoff_data_details";
cmd.Parameters.Add("@whoAmI", SqlDbType.VarChar).Value = 

WindowsIdentity.GetCurrent().Name;

cmd.Parameters.Add("@parmEndDateKey", SqlDbType.VarChar).Value = myMostRecentActualDate;
cmd.Parameters.Add("@countrykeys", SqlDbType.VarChar).Value = myCountryKey;
cmd.Parameters.Add("@nodekeys", SqlDbType.VarChar).Value = "1,2";
break;


dr = cmd.ExecuteReader();
while (dr.Read())                    
{
    dtWriteoffUpload.Rows.Add(dr["country name"].ToString(), dr["country key"].ToString());
}

답변:


283

를 허용하는 메서드를 DataTable사용하여 데이터 판독기에서를 직접 로드 할 수 있습니다 .Load()IDataReader

var dataReader = cmd.ExecuteReader();
var dataTable = new DataTable();
dataTable.Load(dataReader);

2
당신은 내 일 (Y) 저장
Uzair Xlade

1
이것이 내가 일주일 동안 찾던 것입니다!
TheTechy

17

아래 코드를 확인하세요. 자동으로 DataTable로 변환됩니다.

private void ConvertDataReaderToTableManually()
    {
        SqlConnection conn = null;
        try
        {
            string connString = ConfigurationManager.ConnectionStrings["NorthwindConn"].ConnectionString;
            conn = new SqlConnection(connString);
            string query = "SELECT * FROM Customers";
            SqlCommand cmd = new SqlCommand(query, conn);
            conn.Open();
            SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
            DataTable dtSchema = dr.GetSchemaTable();
            DataTable dt = new DataTable();
            // You can also use an ArrayList instead of List<>
            List<DataColumn> listCols = new List<DataColumn>();

            if (dtSchema != null)
            {
                foreach (DataRow drow in dtSchema.Rows)
                {
                    string columnName = System.Convert.ToString(drow["ColumnName"]);
                    DataColumn column = new DataColumn(columnName, (Type)(drow["DataType"]));
                    column.Unique = (bool)drow["IsUnique"];
                    column.AllowDBNull = (bool)drow["AllowDBNull"];
                    column.AutoIncrement = (bool)drow["IsAutoIncrement"];
                    listCols.Add(column);
                    dt.Columns.Add(column);
                }
            }

            // Read rows from DataReader and populate the DataTable
            while (dr.Read())
            {
                DataRow dataRow = dt.NewRow();
                for (int i = 0; i < listCols.Count; i++)
                {
                    dataRow[((DataColumn)listCols[i])] = dr[i];
                }
                dt.Rows.Add(dataRow);
            }
            GridView2.DataSource = dt;
            GridView2.DataBind();
        }
        catch (SqlException ex)
        {
            // handle error
        }
        catch (Exception ex)
        {
            // handle error
        }
        finally
        {
            conn.Close();
        }

    }

datareader를 datatable에로드하는 간단한 옵션이 있는데 왜 이것을 사용하겠습니까?
Abbas

@sarathkumar 좋은 일 .. 나는 그런 코드를 찾고 있었다
SimpleGuy aug

@Abbas Coz, 내장 데이터로드가 매우 느립니다
SimpleGuy

dt.Load(reader)또한 항상 작동하는 것은 아닙니다 Object reference not set to an instance of an object. 행을 다시 얻지 못할 때 성가신 오류가 발생합니다. 이와 같은 설명서가 유용합니다. 나는 그것을 시도하고 그것이 불법 캐스트라고 말했기 때문에 루프 column.에서 그 라인을 제거 dtSchema foreach해야 bool했습니다 (bool)drow["IsUnique"]. 나는 그것들이 필요하지 않았고, 새로운 것을 채우기 위해 열 이름을 얻는 DataTable것으로 충분합니다. 이것은 ds.Fill(adapter)큰 테이블을로드 할 수없는 문제를 극복하는 데 도움이 되었습니다 SELECT * FROM MyTable.
vapcguy 2011

한 가지주의 사항-열에 null 값이있는 경우 처리해야합니다. 그렇지 않으면이 함수로 인해 예외가 발생합니다. if (!dr.IsDBNull(i))for루프 내부의 다음 항목 으로 확인 해야 합니다. 그런 다음 작업을 수행합니다 dataRow. 그러나 elsenull을 찾을 경우에 대비 하여 필요합니다 . 이 경우 추가 할 열의 유형을 파악하고 그에 따라 null을 할당 String.Empty해야합니다 (예 : 유형이이면 할당 할 수 System.String있지만 (부울 필드) 또는 . 인 0경우 할당 해야합니다 .System.Int16System.Decimal
vapcguy

13

를로드하려는 경우 대신 DataTable다음을 활용하십시오 SqlDataAdapter.

DataTable dt = new DataTable();

using (SqlConnection c = new SqlConnection(cString))
using (SqlDataAdapter sda = new SqlDataAdapter(sql, c))
{
    sda.SelectCommand.CommandType = CommandType.StoredProcedure;
    sda.SelectCommand.Parameters.AddWithValue("@parm1", val1);
    ...

    sda.Fill(dt);
}

열을 정의 할 필요조차 없습니다. 그냥를 생성 DataTable하고 Fill그것.

여기에 cString연결 문자열 sql이 있으며 저장 프로 시저 명령입니다.


1
여기서 유일한 문제는 채우기 중에 열 / 값이 예외를 발생시키는 경우 a SqlDataReader를 사용하고 필드를 통해 루프를 사용하여 읽을 수있는 것처럼 세부 정보를 제공하지 않는다는 것입니다.
vapcguy

9

Sagi가 답변에서 언급했듯이 DataTable.Load는 좋은 솔루션입니다. 단일 판독기에서 여러 테이블을로드하려는 경우 DataReader.NextResult를 호출 할 필요가 없습니다. DataTable.Load 메서드는 또한 판독기를 다음 결과 집합 (있는 경우)으로 이동합니다.

// Read every result set in the data reader.
while (!reader.IsClosed)
{
    DataTable dt = new DataTable();
    // DataTable.Load automatically advances the reader to the next result set
    dt.Load(reader);
    items.Add(dt);
}

5

나는 이것도 조사했고, SqlDataAdapter.Fill 메서드를 SqlDataReader.Load 함수와 비교 한 후 SqlDataAdapter.Fill 메서드가 내가 사용한 결과 집합보다 두 배 이상 빠르다는 것을 발견했습니다.

사용 된 코드 :

    [TestMethod]
    public void SQLCommandVsAddaptor()
    {
        long AdapterFillLargeTableTime, readerLoadLargeTableTime, AdapterFillMediumTableTime, readerLoadMediumTableTime, AdapterFillSmallTableTime, readerLoadSmallTableTime, AdapterFillTinyTableTime, readerLoadTinyTableTime;

        string LargeTableToFill = "select top 10000 * from FooBar";
        string MediumTableToFill = "select top 1000 * from FooBar";
        string SmallTableToFill = "select top 100 * from FooBar";
        string TinyTableToFill = "select top 10 * from FooBar";

        using (SqlConnection sconn = new SqlConnection("Data Source=.;initial catalog=Foo;persist security info=True; user id=bar;password=foobar;"))
        {
            // large data set measurements
            AdapterFillLargeTableTime = MeasureExecutionTimeMethod(sconn, LargeTableToFill, ExecuteDataAdapterFillStep);
            readerLoadLargeTableTime = MeasureExecutionTimeMethod(sconn, LargeTableToFill, ExecuteSqlReaderLoadStep);
            // medium data set measurements
            AdapterFillMediumTableTime = MeasureExecutionTimeMethod(sconn, MediumTableToFill, ExecuteDataAdapterFillStep);
            readerLoadMediumTableTime = MeasureExecutionTimeMethod(sconn, MediumTableToFill, ExecuteSqlReaderLoadStep);
            // small data set measurements
            AdapterFillSmallTableTime = MeasureExecutionTimeMethod(sconn, SmallTableToFill, ExecuteDataAdapterFillStep);
            readerLoadSmallTableTime = MeasureExecutionTimeMethod(sconn, SmallTableToFill, ExecuteSqlReaderLoadStep);
            // tiny data set measurements
            AdapterFillTinyTableTime = MeasureExecutionTimeMethod(sconn, TinyTableToFill, ExecuteDataAdapterFillStep);
            readerLoadTinyTableTime = MeasureExecutionTimeMethod(sconn, TinyTableToFill, ExecuteSqlReaderLoadStep);
        }
        using (StreamWriter writer = new StreamWriter("result_sql_compare.txt"))
        {
            writer.WriteLine("10000 rows");
            writer.WriteLine("Sql Data Adapter 100 times table fill speed 10000 rows: {0} milliseconds", AdapterFillLargeTableTime);
            writer.WriteLine("Sql Data Reader 100 times table load speed 10000 rows: {0} milliseconds", readerLoadLargeTableTime);
            writer.WriteLine("1000 rows");
            writer.WriteLine("Sql Data Adapter 100 times table fill speed 1000 rows: {0} milliseconds", AdapterFillMediumTableTime);
            writer.WriteLine("Sql Data Reader 100 times table load speed 1000 rows: {0} milliseconds", readerLoadMediumTableTime);
            writer.WriteLine("100 rows");
            writer.WriteLine("Sql Data Adapter 100 times table fill speed 100 rows: {0} milliseconds", AdapterFillSmallTableTime);
            writer.WriteLine("Sql Data Reader 100 times table load speed 100 rows: {0} milliseconds", readerLoadSmallTableTime);
            writer.WriteLine("10 rows");
            writer.WriteLine("Sql Data Adapter 100 times table fill speed 10 rows: {0} milliseconds", AdapterFillTinyTableTime);
            writer.WriteLine("Sql Data Reader 100 times table load speed 10 rows: {0} milliseconds", readerLoadTinyTableTime);

        }
        Process.Start("result_sql_compare.txt");
    }

    private long MeasureExecutionTimeMethod(SqlConnection conn, string query, Action<SqlConnection, string> Method)
    {
        long time; // know C#
        // execute single read step outside measurement time, to warm up cache or whatever
        Method(conn, query);
        // start timing
        time = Environment.TickCount;
        for (int i = 0; i < 100; i++)
        {
            Method(conn, query);
        }
        // return time in milliseconds
        return Environment.TickCount - time;
    }

    private void ExecuteDataAdapterFillStep(SqlConnection conn, string query)
    {
        DataTable tab = new DataTable();
        conn.Open();
        using (SqlDataAdapter comm = new SqlDataAdapter(query, conn))
        {
            // Adapter fill table function
            comm.Fill(tab);
        }
        conn.Close();
    }

    private void ExecuteSqlReaderLoadStep(SqlConnection conn, string query)
    {
        DataTable tab = new DataTable();
        conn.Open();
        using (SqlCommand comm = new SqlCommand(query, conn))
        {
            using (SqlDataReader reader = comm.ExecuteReader())
            {
                // IDataReader Load function
                tab.Load(reader);
            }
        }
        conn.Close();
    }

결과 :

10000 rows:
Sql Data Adapter 100 times table fill speed 10000 rows: 11782 milliseconds
Sql Data Reader  100 times table load speed 10000 rows: 26047 milliseconds
1000 rows:
Sql Data Adapter 100 times table fill speed 1000 rows: 984  milliseconds
Sql Data Reader  100 times table load speed 1000 rows: 2031 milliseconds
100 rows:
Sql Data Adapter 100 times table fill speed 100 rows: 125 milliseconds
Sql Data Reader  100 times table load speed 100 rows: 235 milliseconds
10 rows:
Sql Data Adapter 100 times table fill speed 10 rows: 32 milliseconds
Sql Data Reader  100 times table load speed 10 rows: 93 milliseconds

성능 문제의 경우 SqlDataAdapter.Fill 메서드를 사용하는 것이 훨씬 더 효율적입니다. 그래서 당신이 발로 자신을 쏘고 싶지 않다면 그것을 사용하십시오. 작고 큰 데이터 세트에 대해 더 빠르게 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.