使用多个条件进行Datatable筛选

61
我有一个datatable,其中有4列A、B、C和D,使得在datatable中,列A、B和C的特定组合是唯一的。
目标:找到给定列A、B和C值的组合的列D的值。
我猜循环遍历数据行应该可以做到。是否有一种方法可以使用Datatable.Select()来完成此操作?更具体地说,我能否在选择过滤器中拥有多个条件,即用逻辑AND运算符连接每个A、B和C列的条件?

那么D列是一个计算列,基于A、B和C吗? - OMG Ponies
你考虑过将其作为查询发出,让数据库完成它的任务吗? - Mitch Wheat
8个回答

139

是的,DataTable.Select方法支持布尔运算符,就像在“真实”的SQL语句中使用它们一样:

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

请参考MSDN中的DataColumn.Expression了解DataTable的Select方法所支持的语法。


46

你必须使用 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");

我也给+1。作为编程语言,它比SQL命令更易读。说实话,我不知道哪个更快,但我更喜欢这一个。 - Vali Maties
Linq在这种情况下更快,也更推荐使用。 - ammad khan

7

我发现使用太多的“and”会导致错误的结果返回(至少在.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')"); 

过滤器正常工作!

1
那一定是某种错误。你的两个例子之间没有逻辑上的区别。 - Christian

6
尝试这个, 我认为,这是其中一种简单的解决方案。
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列的值组合是唯一的。


你的解决方案 int rowIndex = ... [0] 只能返回第一行匹配的结果,即使请求是返回“唯一”值,可能有多个值被忽略。为什么不使用 int[] rowIndex = ... 并返回任何结果呢?重点在于,如果所谓的唯一组合不是唯一的,并且存在数据错误,则可以进行纠正。 - LokizFenrir

2
    protected void FindCsv()
    {
        string strToFind = "2";

        importFolder = @"C:\Documents and Settings\gmendez\Desktop\";

        fileName = "CSVFile.csv";

        connectionString= @"Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq="+importFolder+";Extended Properties=Text;HDR=No;FMT=Delimited";
        conn = new OdbcConnection(connectionString);

        System.Data.Odbc.OdbcDataAdapter  da = new OdbcDataAdapter("select * from [" + fileName + "]", conn);
        DataTable dt = new DataTable();
        da.Fill(dt);

        dt.Columns[0].ColumnName = "id";

        DataRow[] dr = dt.Select("id=" + strToFind);

        Response.Write(dr[0][0].ToString() + dr[0][1].ToString() + dr[0][2].ToString() + dr[0][3].ToString() + dr[0][4].ToString() + dr[0][5].ToString());
    }

1
Dim dr As DataRow()


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

A、B、C为列名,第二个参数用于排序表达式。


0

如果你真的不想遇到很多烦人的错误(例如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);

0

试试这个:

class Program
{
    static void Main()
    {
        //  Create Your YourDataTableSample.
        //  And Add New columns and Some rows.
        DataTable YourDataTableSample = new DataTable("SampleDT");
        YourDataTableSample.Columns.Add(new DataColumn("ID", typeof(int)));
        YourDataTableSample.Columns.Add(new DataColumn("MyDate", typeof(DateTime)));
        YourDataTableSample.Rows.Add(100, new DateTime(2021, 1, 1));
        YourDataTableSample.Rows.Add(200, new DateTime(2022, 2, 1));
        YourDataTableSample.Rows.Add(300, new DateTime(2023, 3, 1));
        
        // Select by MyDate.
        DataRow[] TheResult = YourDataTableSample.Select("MyDate > #6/1/2021#");
        
        // Display The Result.
        foreach (DataRow TheRow in TheResult)
        {
            Console.WriteLine(TheRow["ID"]);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接