Linq เป็น SQL วิธีการทำ“ โดยที่ [คอลัมน์] ใน (รายการค่า)”


101

ฉันมีฟังก์ชันที่ฉันได้รับรายการรหัสและฉันต้องการส่งคืนรายการที่ตรงกับคำอธิบายที่เชื่อมโยงกับรหัส เช่น:

public class CodeData
{
    string CodeId {get; set;}
    string Description {get; set;}
}

public List<CodeData> GetCodeDescriptionList(List<string> codeIDs)
    //Given the list of institution codes, return a list of CodeData
    //having the given CodeIds
}

ดังนั้นถ้าฉันสร้าง sql สำหรับสิ่งนี้ด้วยตัวเองฉันก็จะทำสิ่งต่อไปนี้ (โดยที่ in clause มีค่าทั้งหมดในอาร์กิวเมนต์ codeIds):

Select CodeId, Description FROM CodeTable WHERE CodeId IN ('1a','2b','3')

ใน Linq ถึง Sql ฉันไม่สามารถหาส่วนคำสั่ง "IN" ที่เทียบเท่าได้ สิ่งที่ดีที่สุดที่ฉันพบ (ซึ่งใช้ไม่ได้) คือ:

 var foo = from codeData in channel.AsQueryable<CodeData>()
           where codeData.CodeId == "1" || codeData.CodeId == "2"
           select codeData;

ปัญหาคือฉันไม่สามารถสร้างรายการคำสั่ง "OR" แบบไดนามิกสำหรับ linq ถึง sql ได้เนื่องจากตั้งค่าในเวลาคอมไพล์

หนึ่งจะบรรลุโดยที่ประโยคที่ตรวจสอบคอลัมน์อยู่ในรายการค่าแบบไดนามิกโดยใช้ Linq to Sql ได้อย่างไร

คำตอบ:


159

ใช้

where list.Contains(item.Property)

หรือในกรณีของคุณ:

var foo = from codeData in channel.AsQueryable<CodeData>()
          where codeIDs.Contains(codeData.CodeId)
          select codeData;

แต่คุณอาจทำได้เช่นกันในสัญลักษณ์จุด:

var foo = channel.AsQueryable<CodeData>()
                 .Where(codeData => codeIDs.Contains(codeData.CodeId));

วิธีใช้ในกรณี CodeId คือ Integer ??
Kiran Solkar

2
@KiranSolkar: ถ้าอย่างนั้นก็น่าcodeIDsจะเป็นList<int>และทุกอย่างจะดี
Jon Skeet

@JonSkeet กรณีนั้นไม่สำคัญเหรอ? ถ้า codeIDs เป็นรายการของสตริงตัวพิมพ์ใหญ่และ codeData.codeId เป็นสตริงตัวพิมพ์เล็กก็จะล้มเหลว
PersyJack

@PersyJack: ไม่มีอะไรในคำถามเกี่ยวกับเรื่องนี้ที่ต้องคำนึงถึงตัวพิมพ์เล็กและใหญ่ ว่าจะเป็นหรือไม่ฉันจำไม่ได้ว่า LINQ กับ SQL บังคับใช้ case-sensitive ตามค่าเริ่มต้นหรือให้การตั้งค่า db ควบคุม
Jon Skeet

1
@PersyJack LINQ เป็น SQL สร้างแบบสอบถาม T-SQL ซึ่งจะทำงานบน SQL Server โดยใช้การตั้งค่าฐานข้อมูลสำหรับกรณีไว แม้ว่าหากไม่มีใครระมัดระวังและทำให้ผลลัพธ์ของคิวรีเป็นจริงก่อนที่จะใช้ LINQ กับอ็อบเจ็กต์ในหน่วยความจำพวกเขาอาจได้รับผลของความไวต่อตัวพิมพ์ที่ไม่ตรงกัน
Zarepheth

26

คุณยังสามารถใช้:

List<int> codes = new List<int>();

codes.add(1);
codes.add(2);

var foo = from codeData in channel.AsQueryable<CodeData>()
          where codes.Any(code => codeData.CodeID.Equals(code))
          select codeData;

1
ฉันต้องใช้สิ่งนี้เนื่องจากการใช้งาน IQToolkit ของเราไม่รองรับเนื้อหา ()
DJ van Wyk

1

ผมเคยใช้วิธีการในคำตอบจอน Skeet Concatแต่อีกคนหนึ่งที่เกิดขึ้นกับผมใช้ Concatวิธีการดำเนินการที่ดีกว่าเล็กน้อยในการทดสอบ จำกัด แต่มันเป็นความยุ่งยากและฉันอาจจะเพียงแค่ติดกับContainsหรือบางทีฉันอาจจะเขียนวิธีการช่วยเหลือการทำเช่นนี้สำหรับฉัน ไม่ว่าจะด้วยวิธีใดนี่เป็นอีกทางเลือกหนึ่งหากใครสนใจ:

วิธีการ

// Given an array of id's
var ids = new Guid[] { ... };

// and a DataContext
var dc = new MyDataContext();

// start the queryable
var query = (
    from thing in dc.Things
    where thing.Id == ids[ 0 ]
    select thing 
);

// then, for each other id
for( var i = 1; i < ids.Count(); i++ ) {
    // select that thing and concat to queryable
    query.Concat(
        from thing in dc.Things
        where thing.Id == ids[ i ]
        select thing
    );
}

การทดสอบประสิทธิภาพ

นี่ไม่ใช่วิทยาศาสตร์จากระยะไกล ฉันคิดว่าโครงสร้างฐานข้อมูลของคุณและจำนวน ID ที่เกี่ยวข้องในรายการจะมีผลกระทบอย่างมาก

ฉันตั้งค่าการทดสอบโดยที่ฉันทำการทดลอง 100 ครั้งต่อครั้งConcatและการContainsทดลองแต่ละครั้งเกี่ยวข้องกับการเลือก 25 แถวที่ระบุโดยรายการคีย์หลักแบบสุ่ม ฉันใช้วิธีนี้ประมาณหนึ่งโหลและส่วนใหญ่Concatวิธีการออกมาเร็วขึ้น 5-10% แม้ว่าครั้งเดียวContainsวิธีนี้จะชนะเพียงแค่ smidgen ก็ตาม


0
 var filterTransNos = (from so in db.SalesOrderDetails
                    where  ItemDescription.Contains(ItemDescription)
                            select new { so.TransNo }).AsEnumerable();    


listreceipt = listreceipt.Where(p => filterTransNos.Any(p2 => p2.TransNo == p.TransNo)).ToList();

-1

นี่คือวิธีที่ฉันทำได้โดยใช้ HashSet

        HashSet<String> hs = new HashSet<string>(new String[] { "Pluto", "Earth", "Neptune" });
        String[] arr =
        {
            "Pluto",
            "Earth",
            "Neptune",
            "Jupiter",
            "Saturn",
            "Mercury",
            "Pluto",
            "Earth",
            "Neptune",
            "Jupiter",
            "Saturn",
            "Mercury",
            // etc.
        };
        ICollection<String> coll = arr;

        String[] arrStrFiltered = coll.Where(str => hs.Contains(str)).ToArray();

โดยพื้นฐานแล้ว HashSet เกือบจะเป็น O (1) ดังนั้นความซับซ้อนของคุณจึงยังคงเป็น O (n)


นี่เป็นเรื่องเกี่ยวกับ LINQ-to-SQL การพิจารณา LINQ-to-Object ดังกล่าวใช้ไม่ได้
Gert Arnold

ICollection สามารถมาจาก LINQ-SQL ได้เช่นกันซึ่งเป็นวิธีทั่วไป
มก

คำถามเกี่ยวกับวิธีสร้างนิพจน์ที่แปลเป็น SQL ที่ถูกต้อง ไม่มีส่วนเกี่ยวข้องกับวิธีค้นหาคอลเล็กชันในเครื่อง คำตอบของคุณจะหลอกลวงผู้อ่านในอนาคตที่ไม่ทราบถึงความแตกต่างนี้
Gert Arnold
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.