【问题标题】:Datatable select with multiple conditions具有多个条件的数据表选择
【发布时间】:2010-12-31 17:57:12
【问题描述】:

我有一个包含 4 列 A、B、C 和 D 的数据表,因此 A、B 和 C 列的特定值组合在数据表中是唯一的。

目标:针对 A、B 和 C 列的给定值组合,找出 D 列的值。

我猜应该循环遍历一组数据行。有没有办法使用 Datatable.Select() 来完成这个?更具体地说 - 我可以在选择过滤器中有多个条件,即为 A、B 和 C 列中的每一个连接条件的逻辑 AND 运算符。

【问题讨论】:

  • 所以 D 列是计算列,基于 A、B 和 C?
  • 您是否考虑过将其作为查询发出,并让数据库做它该做的事情?

标签: c# select datatable


【解决方案1】:

试试这个:

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"]);
        }
    }
}

【讨论】:

    【解决方案2】:

    如果您真的不想遇到很多烦人的错误(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 的乐趣,而无需使用 AsEnumerable 时必须使用的烦人的 row.Field&lt;type&gt;

    例子

    var personsBornOn1980 = persons.Where(x=>x.DateBorn.Year == 1980);
    

    【讨论】:

      【解决方案3】:

      你必须使用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 命令更容易看到。说真的,我不知道哪一个更快,但我更喜欢这个。
      • Linq 速度更快,建议在此类情况下使用
      【解决方案4】:
      Dim dr As DataRow()
      
      
      dr = dt.Select("A="& a & "and B="& b & "and C=" & c,"A",DataViewRowState.CurrentRows)
      

      其中 A,B,C 是列名 其中第二个参数用于排序表达式

      【讨论】:

        【解决方案5】:

        试试这个,
        我认为,这是简单的解决方案之一。

        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 = ... 并返回任何结果。重点是,如果所谓的唯一组合不是唯一的,存在数据错误,那么可以补救。
        【解决方案6】:
            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());
            }
        

        【讨论】:

          【解决方案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')"); 
          

          过滤器工作正常!

          【讨论】:

          • 这一定是某种错误。你的两个例子在逻辑上没有区别。
          【解决方案8】:

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

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

          DataTable 的Select 方法支持的语法请参见DataColumn.Expression in MSDN

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-01-04
            • 1970-01-01
            • 2020-05-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-03
            相关资源
            最近更新 更多