วิธีรับจำนวนแถวโดยใช้ SqlDataReader ใน C #


98

คำถามของฉันคือวิธีรับจำนวนแถวที่ส่งคืนโดยแบบสอบถามโดยใช้SqlDataReaderใน C # ฉันเคยเห็นคำตอบบางอย่างเกี่ยวกับเรื่องนี้ แต่ไม่มีการกำหนดอย่างชัดเจนยกเว้นคำตอบที่ระบุให้ทำ while loop ด้วยRead()วิธีการและเพิ่มตัวนับ

ปัญหาของฉันคือฉันพยายามเติมอาร์เรย์หลายมิติโดยให้แถวแรกเป็นชื่อส่วนหัวของคอลัมน์และทุกแถวหลังจากนั้นเป็นข้อมูลแถว

ฉันรู้ว่าฉันสามารถถ่ายโอนข้อมูลในตัวควบคุมรายการได้และไม่ต้องกังวลกับมัน แต่เพื่อการแก้ไขส่วนตัวของฉันเองและฉันต้องการดึงข้อมูลเข้าและออกจากอาร์เรย์เมื่อฉันเลือกและแสดงในรูปแบบต่างๆ

ดังนั้นฉันคิดว่าฉันไม่สามารถทำวิธีRead()เพิ่ม ++ ได้แล้วเพราะนั่นหมายความว่าฉันจะต้องเปิดRead()แล้วเปิดRead()อีกครั้งเพื่อรับจำนวนแถวแล้วจึงเป็นข้อมูลคอลัมน์

เป็นเพียงตัวอย่างเล็ก ๆ ของสิ่งที่ฉันกำลังพูดถึง:

int counter = 0;    

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

จากนั้นสำหรับลูปเพื่อวิ่งผ่านคอลัมน์และป๊อป

something.Read();

int dbFields = sqlRead.FieldCount;

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

คำตอบ:


97

มีเพียงสองทางเลือก:

  • ค้นหาโดยการอ่านแถวทั้งหมด (จากนั้นคุณอาจเก็บไว้ด้วย)

  • เรียกใช้แบบสอบถามพิเศษ SELECT COUNT (*) ไว้ล่วงหน้า

การผ่านลูป DataReader สองครั้งมีราคาแพงมากคุณจะต้องดำเนินการค้นหาอีกครั้ง

และ (ขอบคุณ Pete OHanlon) ตัวเลือกที่สองจะปลอดภัยพร้อมกันเมื่อคุณใช้ธุรกรรมที่มีระดับการแยก Snapshot

เนื่องจากคุณต้องการจัดเก็บแถวทั้งหมดในหน่วยความจำอย่างไรก็ตามตัวเลือกเดียวที่เหมาะสมคือการอ่านแถวทั้งหมดในที่เก็บข้อมูลแบบยืดหยุ่น ( List<>หรือDataTable) จากนั้นคัดลอกข้อมูลไปยังรูปแบบใดก็ได้ที่คุณต้องการ การทำงานในหน่วยความจำจะมีประสิทธิภาพมากขึ้นเสมอ


5
เฮงค์พูดถูก: ไม่มีสมาชิกของ DataReader ที่อนุญาตให้คุณได้รับจำนวนแถวเนื่องจากเป็นผู้อ่านไปข้างหน้าเท่านั้น ก่อนอื่นคุณควรทำการนับจำนวนก่อนแล้วจึงเรียกใช้แบบสอบถามบางทีอาจเป็นแบบสอบถามหลายผลลัพธ์ดังนั้นคุณจึงกดฐานข้อมูลเพียงครั้งเดียว
flipdoubt

14
ปัญหาเกี่ยวกับการนับเฉพาะคือมีความเป็นไปได้ที่การนับจะแตกต่างจากจำนวนแถวที่ส่งคืนเนื่องจากมีคนอื่นเปลี่ยนแปลงข้อมูลในลักษณะที่นำไปสู่จำนวนแถวที่ถูกส่งกลับ
Pete OHanlon

1
พีทคุณพูดถูกมันคงต้องใช้ IsolationLevel ราคาแพง
Henk Holterman

1
ขอบคุณทุกคน! นี่เริ่มชัดเจนมากขึ้น ดังนั้นจึงเป็นการดีกว่าที่จะถ่ายโอนข้อมูลทั้งหมดไปยังชุดข้อมูลหรือเรียกใช้ผ่าน SQL COUNT (*) จัดเก็บแล้วเรียกใช้แบบสอบถามที่ต้องการ หรือเรากำลังพูดถึงการนับจำนวนและการจัดเก็บทุกอย่างใน DataSet?
Tomasz Iniewicz

4
RepeatableReadระดับแยกไม่ได้ดำเนินการช่วงล็อคจึงยังคงอนุญาตให้บันทึกที่จะแทรกคุณต้องใช้ระดับการแยกหรือSnapshot Serializable
Lukazoid

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
จะใช้ไม่ได้ในกรณีที่เราต้องการ rowcount ก่อนที่จะดึงข้อมูลจากผู้อ่าน
Heemanshu Bhalla

8

ตามด้านบนชุดข้อมูลหรือชุดข้อมูลที่พิมพ์อาจเป็นโครงสร้างทางอารมณ์ที่ดีซึ่งคุณสามารถใช้ในการกรองของคุณได้ SqlDataReader มีไว้เพื่ออ่านข้อมูลอย่างรวดเร็ว ในขณะที่คุณอยู่ในลูป while () คุณยังคงเชื่อมต่อกับฐานข้อมูลและกำลังรอให้คุณทำสิ่งที่คุณกำลังทำอยู่เพื่ออ่าน / ประมวลผลผลลัพธ์ถัดไปก่อนที่จะดำเนินการต่อ ในกรณีนี้คุณอาจได้รับประสิทธิภาพที่ดีขึ้นหากคุณดึงข้อมูลทั้งหมดมาปิดการเชื่อมต่อกับฐานข้อมูลและประมวลผลผลลัพธ์ "ออฟไลน์"

ผู้คนดูเหมือนจะเกลียดชุดข้อมูลดังนั้นสิ่งที่กล่าวมาข้างต้นจึงสามารถทำได้ด้วยการรวบรวมวัตถุที่พิมพ์อย่างรุนแรงเช่นกัน


2
ฉันชอบ DataSets ด้วยตัวเองเนื่องจากเป็นการนำเสนอข้อมูลแบบตารางทั่วไปที่มีการเขียนอย่างดีและมีประโยชน์อย่างยิ่ง น่าแปลกที่ฉันสังเกตเห็นว่าคนส่วนใหญ่ที่ละทิ้ง DataSet สำหรับ ORM เป็นคนเดียวกับที่พยายามเขียนโค้ดของตัวเองให้เป็นแบบทั่วไปมากที่สุด (โดยปกติจะไม่มีจุดหมาย)
MusiGenesis

5
ดาเนียล 'ข้างบน' ไม่ใช่วิธีที่ดีในการอ้างอิงคำตอบอื่น
Henk Holterman

6

คุณไม่สามารถรับจำนวนแถวจากโปรแกรมอ่านข้อมูลได้โดยตรงเนื่องจากเป็นสิ่งที่เรียกว่าเคอร์เซอร์ไฟ - ซึ่งหมายความว่าข้อมูลจะถูกอ่านทีละแถวตามการอ่านที่กำลังดำเนินการ ฉันไม่แนะนำให้ทำการอ่านข้อมูล 2 ครั้งเนื่องจากมีความเป็นไปได้ที่ข้อมูลจะเปลี่ยนไประหว่างการอ่าน 2 ครั้งและคุณจะได้ผลลัพธ์ที่แตกต่างกัน

สิ่งที่คุณทำได้คืออ่านข้อมูลในโครงสร้างชั่วคราวและใช้ข้อมูลนั้นแทนการอ่านครั้งที่สอง หรือคุณจะต้องเปลี่ยนกลไกในการดึงข้อมูลและใช้บางอย่างเช่น 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.