Выбор данных с несколькими условиями

У меня есть таблица данных с 4 столбцами A, B, C и D, так что определенная комбинация значений для столбцов A, B и C уникальна в таблице данных.

Цель: найти значение столбца D для заданной комбинации значений столбцов A, B и C.

Я предполагаю, что цикл по набору строк данных должен сделать это. Есть ли способ использовать Datatable.Select() для этого? Чтобы быть более конкретным - могу ли я иметь несколько условий в фильтре выбора, то есть логический оператор И, соединяющий условия для каждого из столбцов A, B и C.


person Arnkrishn    schedule 02.01.2010    source источник
comment
Итак, столбец D — это вычисляемый столбец, основанный на A, B и C?   -  person OMG Ponies    schedule 02.01.2010
comment
рассматривали ли вы возможность испустить это как запрос и позволить базе данных делать то, для чего она существует?   -  person Mitch Wheat    schedule 02.01.2010


Ответы (6)


Да, метод DataTable.Select поддерживает логические операторы так же, как вы использовали бы их в «настоящем» операторе SQL:

DataRow[] results = table.Select("A = 'foo' AND B = 'bar' AND C = 'baz'");

Синтаксис см. в разделе DataColumn.Expression в MSDN. поддерживается методом DataTable Select.

person Michael Petrotta    schedule 02.01.2010

Вы должны использовать DataTable.Select()? Я предпочитаю писать linq-запрос для такого рода вещей.

var dValue=  from row in myDataTable.AsEnumerable()
             where row.Field<int>("A") == 1 
                   && row.Field<int>("B") == 2 
                   && row.Field<int>("C") == 3
             select row.Field<string>("D");
person womp    schedule 02.01.2010
comment
+1 от меня тоже. Это лучше видно как язык программирования, чем как команда SQL. Честно говоря, я не знаю, кто из них быстрее, но этот мне нравится больше. - person Vali Maties; 24.09.2019
comment
Linq работает быстрее и рекомендуется в подобных случаях. - person ammad khan; 25.06.2020

Я обнаружил, что слишком много и будут возвращать неверные результаты (во всяком случае, для .NET 1.1)

DataRow[] results = table.Select("A = 'foo' AND B = 'bar' AND C = 'baz' and D ='fred' and E = 'marg'"); 

В моем случае A было 12-м полем в таблице, и выбор фактически игнорировал его.

Однако, если бы я сделал

DataRow[] results = table.Select("A = 'foo' AND (B = 'bar' AND C = 'baz' and D ='fred' and E = 'marg')"); 

Фильтр работал правильно!

person John Warlow    schedule 06.05.2010
comment
Это должен быть какой-то баг. Между вашими двумя примерами нет никакой логической разницы. - person Christian; 25.09.2017

Попробуйте это,
Я думаю, это одно из простых решений.

int rowIndex = table.Rows.IndexOf(table.Select("A = 'foo' AND B = 'bar' AND C = 'baz'")[0]);
string strD= Convert.ToString(table.Rows[rowIndex]["D"]);

Убедитесь, что комбинация значений для столбцов A, B и C уникальна в таблице данных.

person NNNN    schedule 15.11.2011
comment
Ваше решение int rowIndex = ... [0] может вернуть только первую совпавшую строку, даже если запрос должен вернуть уникальное значение, может быть несколько значений, которые затем просто игнорируются. Почему бы не int[] rowIndex = ... и не вернуть любой результат. Основное внимание уделяется тому, что если так называемая уникальная комбинация не является уникальной и имеется ошибка данных, то ее можно исправить. - person LokizFenrir; 20.05.2015

Dim dr As DataRow()


dr = dt.Select("A="& a & "and B="& b & "and C=" & c,"A",DataViewRowState.CurrentRows)

Где A,B,C — имена столбцов, где второй параметр — для выражения сортировки.

person Amit Agrawal    schedule 15.06.2015

Если вы действительно не хотите сталкиваться с множеством раздражающих ошибок (datediff и т. д., среди прочего, не могут быть оценены в DataTable.Select, и даже если вы сделаете, как предложено, используйте DataTable.AsEnumerable, у вас возникнут проблемы с оценкой полей DateTime) сделайте следующее:

1) Смоделируйте свои данные (создайте класс со столбцами DataTable)

Пример

public class Person
{
public string PersonId { get; set; }
public DateTime DateBorn { get; set; }
}

2) Добавьте этот вспомогательный класс в свой код

public static class Extensions
{
/// <summary>
/// Converts datatable to list<T> dynamically
/// </summary>
/// <typeparam name="T">Class name</typeparam>
/// <param name="dataTable">data table to convert</param>
/// <returns>List<T></returns>
public static List<T> ToList<T>(this DataTable dataTable) where T : new()
{
    var dataList = new List<T>();

    //Define what attributes to be read from the class
    const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;

    //Read Attribute Names and Types
    var objFieldNames = typeof(T).GetProperties(flags).Cast<PropertyInfo>().
        Select(item => new
        {
            Name = item.Name,
            Type = Nullable.GetUnderlyingType(item.PropertyType) ?? item.PropertyType
        }).ToList();

    //Read Datatable column names and types
    var dtlFieldNames = dataTable.Columns.Cast<DataColumn>().
        Select(item => new {
            Name = item.ColumnName,
            Type = item.DataType
        }).ToList();

    foreach (DataRow dataRow in dataTable.AsEnumerable().ToList())
    {
        var classObj = new T();

        foreach (var dtField in dtlFieldNames)
        {
            PropertyInfo propertyInfos = classObj.GetType().GetProperty(dtField.Name);

            var field = objFieldNames.Find(x => x.Name == dtField.Name);

            if (field != null)
            {

                if (propertyInfos.PropertyType == typeof(DateTime))
                {
                    propertyInfos.SetValue
                    (classObj, ConvertToDateTime(dataRow[dtField.Name]), null);
                }
                else if (propertyInfos.PropertyType == typeof(int))
                {
                    propertyInfos.SetValue
                    (classObj, ConvertToInt(dataRow[dtField.Name]), null);
                }
                else if (propertyInfos.PropertyType == typeof(long))
                {
                    propertyInfos.SetValue
                    (classObj, ConvertToLong(dataRow[dtField.Name]), null);
                }
                else if (propertyInfos.PropertyType == typeof(decimal))
                {
                    propertyInfos.SetValue
                    (classObj, ConvertToDecimal(dataRow[dtField.Name]), null);
                }
                else if (propertyInfos.PropertyType == typeof(String))
                {
                    if (dataRow[dtField.Name].GetType() == typeof(DateTime))
                    {
                        propertyInfos.SetValue
                        (classObj, ConvertToDateString(dataRow[dtField.Name]), null);
                    }
                    else
                    {
                        propertyInfos.SetValue
                        (classObj, ConvertToString(dataRow[dtField.Name]), null);
                    }
                }
            }
        }
        dataList.Add(classObj);
    }
    return dataList;
}

private static string ConvertToDateString(object date)
{
    if (date == null)
        return string.Empty;

    return HelperFunctions.ConvertDate(Convert.ToDateTime(date));
}

private static string ConvertToString(object value)
{
    return Convert.ToString(HelperFunctions.ReturnEmptyIfNull(value));
}

private static int ConvertToInt(object value)
{
    return Convert.ToInt32(HelperFunctions.ReturnZeroIfNull(value));
}

private static long ConvertToLong(object value)
{
    return Convert.ToInt64(HelperFunctions.ReturnZeroIfNull(value));
}

private static decimal ConvertToDecimal(object value)
{
    return Convert.ToDecimal(HelperFunctions.ReturnZeroIfNull(value));
}

private static DateTime ConvertToDateTime(object date)
{
    return Convert.ToDateTime(HelperFunctions.ReturnDateTimeMinIfNull(date));
}

}
public static class HelperFunctions
{

public static object ReturnEmptyIfNull(this object value)
{
    if (value == DBNull.Value)
        return string.Empty;
    if (value == null)
        return string.Empty;
    return value;
}
public static object ReturnZeroIfNull(this object value)
{
    if (value == DBNull.Value)
        return 0;
    if (value == null)
        return 0;
    return value;
}
public static object ReturnDateTimeMinIfNull(this object value)
{
    if (value == DBNull.Value)
        return DateTime.MinValue;
    if (value == null)
        return DateTime.MinValue;
    return value;
}
/// <summary>
/// Convert DateTime to string
/// </summary>
/// <param name="datetTime"></param>
/// <param name="excludeHoursAndMinutes">if true it will execlude time from datetime string. Default is false</param>
/// <returns></returns>
public static string ConvertDate(this DateTime datetTime, bool excludeHoursAndMinutes = false)
{
    if (datetTime != DateTime.MinValue)
    {
        if (excludeHoursAndMinutes)
            return datetTime.ToString("yyyy-MM-dd");
        return datetTime.ToString("yyyy-MM-dd HH:mm:ss.fff");
    }
    return null;
}
}

3) Легко преобразовать ваш DataTable (dt) в список объектов с помощью следующего кода:

List<Person> persons = Extensions.ToList<Person>(dt);

4) получайте удовольствие от использования Linq без раздражающего бита row.Field<type>, который вы должны использовать при использовании AsEnumerable

Пример

var personsBornOn1980 = persons.Where(x=>x.DateBorn.Year == 1980);
person JAnton    schedule 31.01.2018