C #에서 SqlDataReader를 사용하여 행 수를 얻는 방법


98

내 질문은 SqlDataReaderC #에서 사용하는 쿼리 에서 반환되는 행 수를 얻는 방법 입니다. 나는 이것에 대한 몇 가지 답변을 보았지만 Read()메서드 로 while 루프를 수행 하고 카운터를 증가시키는 것을 제외하고는 명확하게 정의되지 않았습니다 .

내 문제는 첫 번째 행이 열 머리글 이름이고 그 이후의 모든 행이 행 데이터가되도록 다차원 배열을 채우려 고한다는 것입니다.

나는 List 컨트롤에 물건을 버리고 그것에 대해 걱정할 필요가 없다는 것을 알고 있지만, 개인적인 수정을 위해 데이터를 선택하고 다른 형식으로 표시 할 때 배열 안팎으로 가져오고 싶습니다.

그래서 나는 할 수 없다고 생각 Read()하고 ++ 방식으로 증가 시킬 수 없다고 생각합니다. 왜냐하면 그것은 행의 양과 열 데이터를 얻기 위해 Read()열었다가 Read()다시 열어야 함을 의미하기 때문입니다 .

내가 말하는 것에 대한 작은 예 :

int counter = 0;    

while (sqlRead.Read())
{
    //get rows
    counter++
}

그런 다음 for 루프가 열을 통과하고 팝합니다.

something.Read();

int dbFields = sqlRead.FieldCount;

for (int i = 0; i < dbFields; i++)
{
   // do stuff to array
}

답변:


97

두 가지 옵션 만 있습니다.

  • 모든 행을 읽고 알아 내십시오 (그런 다음 저장하는 것이 좋습니다).

  • 사전에 특수 SELECT COUNT (*) 쿼리를 실행하십시오.

DataReader 루프를 두 번 통과하는 것은 비용이 많이 들기 때문에 쿼리를 다시 실행해야합니다.

그리고 (Pete OHanlon 덕분에) 두 번째 옵션은 스냅 샷 격리 수준의 트랜잭션을 사용할 때만 동시성에 안전합니다.

어쨌든 메모리에 모든 행을 저장하고 싶기 때문에 유일한 현명한 옵션은 유연한 저장소 ( List<>또는 DataTable)의 모든 행을 읽은 다음 원하는 형식으로 데이터를 복사하는 것입니다. 메모리 내 작업은 항상 훨씬 더 효율적입니다.


5
Henk의 말이 맞습니다. 앞으로 만 읽을 수있는 리더이기 때문에 행 수를 얻을 수있는 DataReader의 구성원이 없습니다. 먼저 카운트를 얻은 다음 쿼리를 실행하는 것이 좋습니다. 아마도 다중 결과 쿼리에서 데이터베이스를 한 번만 조회 할 수 있습니다.
flipdoubt

14
특수 계수의 문제점은 다른 사람이 리턴되는 행 수로 이어지는 방식으로 데이터를 변경했기 때문에 계수가 리턴 된 행 수와 다를 가능성이 있다는 것입니다.
Pete OHanlon

1
Pete, 맞습니다. 값 비싼 IsolationLevel이 필요합니다.
Henk Holterman

1
다들 감사 해요! 이것은 더욱 명확 해지고 있습니다. 그렇다면 모든 정보를 DataSet에 덤프하거나 SQL COUNT (*)를 통해 실행하고 저장 한 다음 필요한 쿼리를 실행하는 것이 더 낫습니까? 아니면 실행 횟수와 모든 것을 DataSet에 저장하는 것에 대해 이야기하고 있습니까?
Tomasz Iniewicz

4
RepeatableRead여전히 레코드를 삽입 할 수 있도록 격리 수준은 당신의 격리 수준을 사용 할 필요가 범위 잠금을 수행하지 않습니다 SnapshotSerializable.
Lukazoid 2014

10

모든 행을 검색 할 필요가없고 이중 쿼리를 작성하지 않으려면 다음과 같이 시도 할 수 있습니다.

using (var sqlCon = new SqlConnection("Server=127.0.0.1;Database=MyDb;User Id=Me;Password=glop;"))
      {
        sqlCon.Open();

        var com = sqlCon.CreateCommand();
        com.CommandText = "select * from BigTable";
        using (var reader = com.ExecuteReader())
        {
            //here you retrieve what you need
        }

        com.CommandText = "select @@ROWCOUNT";
        var totalRow = com.ExecuteScalar();

        sqlCon.Close();
      }

동일한 명령을 재사용하면 자동으로 트랜잭션이 추가되는지 확실하지 않은 트랜잭션을 추가해야 할 수 있습니다.


1
@@ ROWCOUNT가 항상 위에서 실행되는 마지막 쿼리에 의존한다면 누구나 말할 수 있습니까? 많은 연결이 쿼리를 병렬로 실행하는 경우 문제가 있습니까?
YvesR

1
해야 sqlCon.Close();합니까? 나는 using그것이 당신을 위해 그것을해야 한다고 생각했습니다 .
푸르스름한

1
리더에서 데이터를 검색하기 전에 행 개수가 필요한 경우에는 작동하지 않습니다
Heemanshu Bhalla

8

위의 내용에 따라 데이터 세트 또는 유형이 지정된 데이터 세트는 필터링에 사용할 수있는 좋은 테머 러리 구조 일 수 있습니다. SqlDataReader는 데이터를 매우 빠르게 읽는 것을 의미합니다. while () 루프에있는 동안 여전히 DB에 연결되어 있으며 계속 진행하기 전에 다음 결과를 읽고 / 처리하기 위해 수행중인 작업을 기다리고 있습니다. 이 경우 모든 데이터를 가져 와서 DB에 대한 연결을 닫고 결과를 "오프라인"으로 처리하면 더 나은 성능을 얻을 수 있습니다.

사람들은 데이터 세트를 싫어하는 것 같으므로 위의 작업은 강력한 형식의 개체 모음으로도 수행 할 수 있습니다.


2
DataSet은 잘 작성되고 테이블 기반 데이터의 매우 유용한 일반 표현이기 때문에 나도 좋아합니다. 이상하게도, ORM 용 DataSet을 피하는 대부분의 사람들은 가능한 한 일반적으로 (보통 무의미하게) 자신의 코드를 작성하려는 사람들과 동일하다는 것을 알았습니다.
MusiGenesis

5
Daniel, '위'는 다른 답변을 참조하는 좋은 방법이 아닙니다.
Henk Holterman

6

Firehose 커서로 알려진 것이기 때문에 데이터 판독기에서 직접 행 수를 가져올 수 없습니다. 즉, 수행중인 읽기에 따라 행 단위로 데이터를 읽습니다. 두 번의 읽기를 수행하는 사이에 데이터가 변경되어 다른 결과를 얻을 수 있기 때문에 데이터에 대해 두 번의 읽기를 수행하지 않는 것이 좋습니다.

할 수있는 일은 데이터를 임시 구조로 읽어서 두 번째 읽기 대신 사용하는 것입니다. 또는 데이터를 검색하고 대신 DataTable과 같은 것을 사용하는 메커니즘을 변경해야합니다.


5

Pit 답변을 완료하고 더 나은 성능을 얻으려면 : 하나의 쿼리에서 모두 가져오고 NextResult 메서드를 사용하십시오.

using (var sqlCon = new SqlConnection("Server=127.0.0.1;Database=MyDb;User Id=Me;Password=glop;"))
{
    sqlCon.Open();
    var com = sqlCon.CreateCommand();
    com.CommandText = "select * from BigTable;select @@ROWCOUNT;";
    using (var reader = com.ExecuteReader())
    {
        while(reader.read()){
            //iterate code
        }
        int totalRow = 0 ;
        reader.NextResult(); // 
        if(reader.read()){
            totalRow = (int)reader[0];
        }
    }
    sqlCon.Close();
}

1

또한 상위 결과를 반환해야하는 상황에 직면했지만 쿼리와 일치하는 총 행을 가져오고 싶었습니다. 마지막 으로이 솔루션에 도달합니다.

   public string Format(SelectQuery selectQuery)
    {
      string result;

      if (string.IsNullOrWhiteSpace(selectQuery.WherePart))
      {
        result = string.Format(
@"
declare @maxResult  int;
set @maxResult = {0};

WITH Total AS
(
SELECT count(*) as [Count] FROM {2}
)
SELECT top (@maxResult) Total.[Count], {1} FROM Total, {2}", m_limit.To, selectQuery.SelectPart, selectQuery.FromPart);
      }
      else
      {
        result = string.Format(
@"
declare @maxResult  int;
set @maxResult = {0};

WITH Total AS
(
SELECT count(*) as [Count] FROM {2} WHERE {3}
)
SELECT top (@maxResult) Total.[Count], {1} FROM Total, {2} WHERE {3}", m_limit.To, selectQuery.SelectPart, selectQuery.FromPart, selectQuery.WherePart);
      }

      if (!string.IsNullOrWhiteSpace(selectQuery.OrderPart))
        result = string.Format("{0} ORDER BY {1}", result, selectQuery.OrderPart);

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