วิธีเข้าร่วมรายการและรายการ/ตารางขนาดใหญ่โดยใช้ LINQ

เริ่มแรกฉันมีรายการดังกล่าว:

List<Car> cars = db.Car.Where(x => x.ProductionYear == 2005).ToList();

จากนั้นฉันพยายามเข้าร่วมรายการนี้กับตารางใหญ่สองตารางโดยใช้ LINQ ดังนี้:

var joinedList = (from car in cars
                  join driver in db.Driver.ToList() 
                    on car.Id equals driver.CarId
                  join building in db.Building.ToList() 
                    on driver.BuildingId equals building.Id
                  select new Building
                  {
                     Name = building.Name;
                     Id = building.Id;
                     City = building.City;
                  }).ToList();

ทั้งตาราง Driver และ Building มีประมาณ 1 ล้านแถว เมื่อฉันรันการเข้าร่วมนี้ ฉันพบว่ามีข้อยกเว้นหน่วยความจำไม่เพียงพอ ฉันจะทำให้การเข้าร่วมนี้ทำงานได้อย่างไร ฉันควรดำเนินการเข้าร่วมกับฐานข้อมูลหรือไม่ ถ้าใช่ ฉันจะนำรายการ cars ไปยัง db ได้อย่างไร ขอบคุณล่วงหน้า.


person jason    schedule 30.06.2017    source แหล่งที่มา
comment
db.Driver.ToList() ลบ ToList() ออกจากสิ่งนี้ มันจะป้องกันการดึงตารางไดรเวอร์ทั้งหมดในหน่วยความจำ ในทำนองเดียวกันจาก db.Building   -  person Rahul Singh    schedule 30.06.2017
comment
@RahulSingh ฉันทำอย่างนั้น แต่ฉันยังคงได้รับข้อยกเว้น   -  person jason    schedule 30.06.2017
comment
คุณวางแผนจะทำอะไรกับคอลเลกชันเหล่านี้?   -  person Gilad Green    schedule 30.06.2017
comment
ใช่ นั่นจะไม่ช่วยอะไรหาก เข้าร่วม ของคุณยังคงสร้างบันทึกนับพันล้านรายการ คุณควรกรองและดึงข้อมูลเป็นชุด (หากแสดงข้อมูลนี้ใน UI หรือบางอย่าง)   -  person Rahul Singh    schedule 30.06.2017
comment
@GiladGreen ฉันกำลังพยายามสร้างอาคารตามลำดับสำหรับรถแต่ละคัน   -  person jason    schedule 30.06.2017
comment
ใช่ แต่แล้วคุณวางแผนจะทำอะไรกับมัน   -  person Gilad Green    schedule 30.06.2017
comment
@GiladGreen ฉันกำลังพยายามแสดงข้อมูลอาคารบนเว็บไซต์ ASP.NET MVC   -  person jason    schedule 30.06.2017
comment
@jason คุณได้รับผลลัพธ์กลับมากี่รายการ? แทนที่ .ToList() สุดท้ายด้วย .Count() แล้วตรวจสอบ   -  person Zein Makki    schedule 30.06.2017
comment
@ user3185569 มันแตกต่างกันไป บางทีก็ไม่กี่สิบ บางทีก็ไม่กี่พัน   -  person jason    schedule 30.06.2017
comment
ลองใช้ stackoverflow.com/a/11978832/5621827 หรือใช้ตัวกรองหรือการเพจในขณะที่ดึงข้อมูลบันทึกที่ฉันไม่คิดว่า ui จะต้องมีบันทึกจำนวนมากในแต่ละครั้ง   -  person jitender    schedule 30.06.2017
comment
@jason คุณมีบันทึกกี่รายการในตาราง car, building และ driver?   -  person Zein Makki    schedule 30.06.2017


คำตอบ (3)


แม้ว่าคุณได้ลบ .ToList() แทนที่ใน .AsQueryable()

AsQueryable เร็วกว่า ToList และ AsEnumerable ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

  • หากคุณสร้าง IQueryable แบบสอบถามอาจถูกแปลงเป็น sql และทำงานบนเซิร์ฟเวอร์ฐานข้อมูล

  • หากคุณสร้าง IEnumerable แถวทั้งหมดจะถูกดึงเข้าสู่หน่วยความจำ
    เป็นออบเจ็กต์ก่อนที่จะเรียกใช้แบบสอบถาม

  • ในทั้งสองกรณี หากคุณไม่เรียก ToList() หรือ ToArray() ดังนั้น query
    จะถูกดำเนินการทุกครั้งที่มีการใช้งาน ดังนั้น สมมติว่าคุณมี
    IQueryable และคุณกรอก 4 กล่องรายการจากนั้น จากนั้นแบบสอบถามจะถูกรันกับฐานข้อมูล 4 ครั้ง

ดังนั้นต่อไปนี้แบบสอบถามที่ใช้ Linq

var joinedList = (from car in db.Car.Where(x => x.ProductionYear == 2005).AsQueryable()
              join driver in db.Driver.AsQueryable() 
                on car.Id equals driver.CarId
              join building in db.Building.AsQueryable() 
                on driver.BuildingId equals building.Id
              select new Building
              {
                 Name = building.Name,
                 Id = building.Id,
                 City = building.City,
              }).ToList();
person kari kalan    schedule 30.06.2017
comment
ความแตกต่างระหว่าง db.Driver และ db.Driver.AsQueryable คืออะไร? - person Zein Makki; 30.06.2017

แม้ว่าคุณจะลบการเรียก .ToList() ในการเข้าร่วมของคุณ โค้ดของคุณจะยังคงดึงข้อมูลทั้งหมดและดำเนินการเข้าร่วมในหน่วยความจำ ไม่ใช่ในเซิร์ฟเวอร์ SQL เนื่องจากคุณกำลังใช้รายการท้องถิ่น cars ในการเข้าร่วมของคุณ ข้อมูลด้านล่างควรแก้ปัญหาของคุณได้:

var joinedList = (from car in db.Car.Where(x => x.ProductionYear == 2005)
                  join driver in db.Driver 
                    on car.Id equals driver.CarId
                  join building in db.Building 
                    on driver.BuildingId equals building.Id
                  select new Building
                  {
                     Name = building.Name;
                     Id = building.Id;
                     City = building.City;
                  }).ToList();

คุณสามารถลบ .ToList() สุดท้ายออกและทำการเพจได้ หากคุณคาดว่าจะได้รับบันทึกมากเกินไปในผลลัพธ์

person Zein Makki    schedule 30.06.2017
comment
ตอนนี้ใช้หน่วยความจำน้อยลงแต่ยังคงใช้หน่วยความจำมากกว่า 1 GB ไม่มีวิธีลดสิ่งนี้เหรอ? และมีวิธีเพิ่มหน่วยความจำหลังจากที่ฉันเข้าร่วมเสร็จแล้วหรือไม่ ขอบคุณสำหรับคำตอบ. - person jason; 30.06.2017
comment
@jason joinedList ควรเป็นทุกสิ่งที่คุณต้องกังวลหลังจากรันโค้ดข้างต้น ทุกสิ่งที่ไม่ได้ใช้อีกต่อไปจะถูกทำเครื่องหมายสำหรับการรวบรวมขยะ (และจะถูกรวบรวมในอนาคตเมื่อ GC ถูกไล่ออก) อย่างไรก็ตาม หาก joinedList มีรายการมากเกินไป ให้ตรวจสอบบรรทัดสุดท้ายในคำตอบของฉันและใช้การแบ่งหน้า - person Zein Makki; 30.06.2017
comment
ในกรณีหนึ่ง ฉันได้รับข้อยกเว้นหน่วยความจำไม่เพียงพอในการเข้าร่วมนั้น ฉันทำสิ่งที่คุณได้ทำอย่างแน่นอน - person jason; 30.06.2017
comment
@jason คำตอบข้างต้นแก้ปัญหาที่ db.Driver, db.Building และ db.Card มีบันทึกมากเกินไปและคุณดึงสิ่งเหล่านี้เข้าสู่หน่วยความจำเพราะคุณกำลังทำ join ผิดวิธีอย่างไรก็ตามหากผลลัพธ์ที่ส่งคืนเป็นรายการขนาดใหญ่ที่ไม่พอดี ลงในหน่วยความจำ จากนั้นเพจจิ้งก็เป็นวิธีแก้ปัญหาสำหรับสิ่งนั้น โปรดจำไว้ว่า select new Building กำลังสร้างวัตถุในหน่วยความจำ หากคุณมีมากเกินไป (ฉันเดาว่ามากกว่า 2 GB คุณจะได้รับข้อยกเว้นหน่วยความจำไม่เพียงพอ) เพจจิ้งแก้ปัญหานั้นด้วยการสร้างส่วนของวัตถุตามความต้องการ - person Zein Makki; 30.06.2017
comment
ฉันไม่รู้ว่าทำไม แต่เมื่อฉันเพิ่ม AsQueryable() หลังตาราง มันก็ทำงานได้อย่างมีประสิทธิภาพมาก - person jason; 30.06.2017
comment
@jason นี่ไม่สมเหตุสมผลสำหรับฉัน db.Driver เป็นอนุพันธ์ของ IQueryable อยู่แล้ว และวิธีการนั้นก็แค่ทำการแคสติ้งถ้ามี ต้องมีปัจจัยอื่นที่ส่งผลต่อการทดสอบการดำเนินการของคุณ - person Zein Makki; 30.06.2017

ก่อนอื่น อย่าพยายาม ToList() ในขณะที่ใช้ LINQ (คุณสามารถทำได้) แต่ให้แน่ใจว่าคุณใช้ ToList() ให้น้อยลงที่สุดเท่าที่จะเป็นไปได้ในสถานการณ์ที่หายากมากเท่านั้น ทุกครั้งที่คุณจะได้ OutOfMemoryException เมื่อตารางมีหลายแถว นี่คือรหัสสำหรับคำถามของคุณ:

var joinedList = (from car in db.Car.GetQueryable().Where(x => x.ProductionYear == 2005)
              join driver in db.Driver.GetQueryable() on car.Id equals driver.CarId
              join building in db.Building.GetQueryable() on driver.BuildingId equals building.Id
              select new Building
              {
                 Name = building.Name;
                 Id = building.Id;
                 City = building.City;
              }).ToList();
person Starlord Live    schedule 30.06.2017
comment
ในสถานการณ์ที่หายากมาก .. นี่เป็นสิ่งที่ผิด คุณสามารถแจงนับได้หลายรายการหากคุณทำตามคำแนะนำนี้ ซึ่งส่งผลต่อประสิทธิภาพการทำงานและไปกลับ คำแนะนำที่ดีคือทำความเข้าใจการดำเนินการที่เลื่อนออกไป แล้วคุณจะรู้ว่าเมื่อใดควรใช้ .ToList() และเมื่อใดไม่ใช้ - person Zein Makki; 30.06.2017
comment
ใช่ ฉันเห็นด้วยกับคุณทุกประการ หากคุณเป็นผู้เขียนโค้ดที่ดี คุณจะรู้ว่าเมื่อใดควรใช้และควรใช้ที่ไหนตามความต้องการของคุณหลังจากเข้าใจการดำเนินการแล้ว บางครั้งมันก็มีประโยชน์ตามตรรกะทางธุรกิจ - person Starlord Live; 30.06.2017