Зв'язок з 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 для цього, я просто зробив щось на кшталт наступного (де в пункті містяться всі значення аргументу codeIds):

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

У Linq to Sql я не можу знайти еквівалент пункту "IN". Найкраще, що я знайшов поки що (що не працює):

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

Проблема полягає в тому, що я не можу динамічно генерувати список пропозицій "АБО" для linq to sql, оскільки вони встановлюються під час компіляції.

Як можна виконати пункт де, який перевіряє стовпець, у динамічному списку значень, використовуючи Linq до 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 ??
Кіран Солкар

2
@KiranSolkar: Тоді, мабуть, codeIDsбуло б List<int>, і все було б добре.
Джон Скіт

@JonSkeet Чи це не чутливість до регістру? Якщо codeIDs - це список рядків верхнього регістру, а codeData.codeId - це рядковий регистр, він вийде з ладу.
PersyJack

@PersyJack: Нічого в питанні про це не повинно бути чутливим до регістру. Щодо того, чи це було б, я не можу пригадати, чи LINQ для SQL застосовує чутливість до регістру за замовчуванням чи дозволяє налаштуванням db керувати ним.
Джон Скіт

1
@PersyJack LINQ для SQL генерує T-SQL-запит, який потім запускається на SQL Server, використовуючи параметри бази даних для чутливості до регістру. Хоча, якщо людина не є обережною і матеріалізує результати запитів, перед застосуванням LINQ до об'єктів пам'яті, вони можуть зазнати наслідків невідповідності чутливості до регістру.
Зареф

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 не підтримує .Contains ()
DJ van Wyk

1

Я використовував метод у відповіді Джона Скіта, але мені трапився інший 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
    );
}

Тест на продуктивність

Це не було віддалено науково. Я думаю, структура вашої бази даних та кількість ідентифікаторів, що беруть участь у списку, мали б суттєвий вплив.

Я встановив тест, в якому робив 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-до-SQL. Такі міркування щодо об'єктів LINQ не застосовуються.
Герт Арнольд

ICollection може походити і від LINQ-SQL, це загальний шлях
MG

Питання полягає в тому, як побудувати вираз, який перекладається на правильний SQL. Це не має нічого спільного з тим, як шукати локальну колекцію. Ваша відповідь буде обманювати лише майбутніх читачів, які не знають про цю відмінність.
Герт Арнольд
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.