【问题标题】:How to populate DataTable correctly using OLeDB Queries如何使用 OLeDB 查询正确填充 DataTable
【发布时间】:2016-03-08 12:41:45
【问题描述】:

Edit 2 Please Read:这是一个非常具体的问题,因为代码运行良好,只是无法正常工作我本来希望的。在发布答案或评论之前,请通读并确保您了解我需要帮助的地方。非常感激。

我是新手程序员/编码员。我正在尝试用所有员工假期的 Access 数据库表 中的选定员工假期的详细信息填充 DataTable。目前,使用查询,我的代码计算数据库的 [Holiday] 表中有多少个假期具有所选员工的 PayrollNo。从那里,它在程序的 DataTable 中填充新行,其中 第一个假期与关联的 PayrollNo 按员工已休的假期数量填充。

例如:Ben 有 3 个假期,但表格将在 3 行中填充他的第一个假期。

这表明程序正确计算了他的假期,但我在选择员工的每个假期时做错了。

它还显示它正在以我想要的格式写入表格。

我想要做的似乎很简单;让表格中填满 Ben 的所有 3 个假期,而不仅仅是他的第一个。

下面是我的代码部分,它对所选员工在[Holiday] 中的假期数进行循环迭代。

DataTable dt = new DataTable(); //Creates Table
dt.Clear();

dt.Columns.Add("Hol/Abs", typeof(string));      // Column 0  
dt.Columns.Add("FirstDay", typeof(DateTime));   // Column 1 
dt.Columns.Add("LastDay", typeof(DateTime));    // Column 2
dt.Columns.Add("TotalDays", typeof(int));       // Column 3
dt.Columns.Add("Reason", typeof(string));       // Column 4

LblName.Text = PassName; //Loads Name

string ConnString = @"Provider = Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\\HoliPlanData.accdb;Persist Security Info=False";

string Query = "SELECT PayrollNo FROM [Employee] WHERE (FirstName + ' ' + LastName) = @Name"; //Will supply selected Employee's PayrollNo

string CountHolQuery = "SELECT COUNT(*) FROM [Holiday] WHERE PayrollNo = @PayrollNo"; //Will count all of that Employee's Holidays
string CountAbsQuery = "SELECT COUNT(*) FROM [Absences] WHERE PayrollNo = @PayrollNo"; //Will count all of that Employee's Absences
string GetStartQuery = "SELECT StartDate FROM [Holiday] WHERE PayrollNo = @PayrollNo"; // Will select the start date of Holidays
string GetEndQuery = "SELECT EndDate FROM [Holiday] WHERE PayrollNo = @PayrollNo"; // Will select the start date of Absences
string GetReasonQuery = "SELECT Reason FROM [Holiday] WHERE PayrollNo = @PayrollNo";
string AbsGetStartQuery = "SELECT StartDate FROM [Absences] WHERE PayrollNo = @PayrollNo";
string AbsGetEndQuery = "SELECT EndDate FROM [Absences] WHERE PayrollNo = @PayrollNo";
string AbsGetReasonQuery = "SELECT Comments FROM [Absences] WHERE PayrollNo = @PayrollNo";

using (OleDbConnection conn = new OleDbConnection(ConnString))
using (OleDbCommand GetPayroll = new OleDbCommand(Query, conn))            
{
    conn.Open();                
    GetPayroll.Parameters.Add("@Name", OleDbType.VarChar).Value = LblName.Text;
    int GotPayroll = Convert.ToInt32(GetPayroll.ExecuteScalar());   //Uses Query to Get PayrollNo
    OleDbCommand CountRowsInHol = new OleDbCommand(CountHolQuery, conn);
    OleDbCommand CountRowsInAbs = new OleDbCommand(CountAbsQuery, conn);
    CountRowsInHol.Parameters.AddWithValue("@PayrollNo", OleDbType.Integer).Value = GotPayroll;
    CountRowsInAbs.Parameters.AddWithValue("@PayrollNo", OleDbType.Integer).Value = GotPayroll;
    int HolidayCount = (int) (CountRowsInHol.ExecuteScalar()); //Uses CountHolQuery to Get HowMany lines are in [Holiday] 
    int AbsenceCount = (int)(CountRowsInAbs.ExecuteScalar()); //Uses CountAbsQuery to Get HowMany lines are in [Absences] 
    int HolLoopCount = 1;

    while (HolLoopCount <= HolidayCount) //Will go though all SelectedPayroll's holidays' in [Holiday]
    {                                        
        OleDbCommand GetStart = new OleDbCommand(GetStartQuery, conn);
        OleDbCommand GetEnd = new OleDbCommand(GetEndQuery, conn);
        OleDbCommand GetReason = new OleDbCommand(GetReasonQuery, conn);
        GetStart.Parameters.Add("@PayrollNo", OleDbType.Integer).Value = GotPayroll;                    
        GetEnd.Parameters.Add("@PayrollNo", OleDbType.Integer).Value = GotPayroll;
        GetReason.Parameters.Add("@PayrollNo", OleDbType.Integer).Value = GotPayroll;
        DateTime StartHold = Convert.ToDateTime(GetStart.ExecuteScalar());
        DateTime EndHold = Convert.ToDateTime(GetEnd.ExecuteScalar());
        string ReasonHold = (GetReason.ExecuteScalar()).ToString();
        DataRow NewLine = dt.NewRow();
        NewLine["Hol/Abs"] = "Holiday";
        NewLine["FirstDay"] = StartHold;
        NewLine["LastDay"] = EndHold;
        NewLine["TotalDays"] = GetNoWeekends(StartHold,EndHold);
        NewLine["Reason"] = ReasonHold;
        dt.Rows.Add(NewLine);
        HolLoopCount = HolLoopCount + 1;
    }

    dataGridView1.DataSource = dt;
    dataGridView1.Refresh();
}

我的假设是,我必须遍历 [Holiday] 数据表本身,或者在代码中创建另一个数据表来存储所有关联的行,然后再从那里提取所显示表的详细信息。 任何帮助或建议将不胜感激。

编辑

我添加了其余代码来显示查询和过程。还要说明我是如何填写表格的,尽管概述了这不是问题发生的地方。

【问题讨论】:

  • 您的代码听起来不必要地在同一数据上循环 N 次。相反,您将创建一个选择并一次性填充数据。您正在寻找的是 DataTable.Load( cmd.ExecuteReader() ) 或 adapter.Fill( datatable )。我更喜欢使用 Linq 来做这样的事情。
  • 为朋友的建议干杯,问题是我已经用 C# 编写了这个项目的几乎所有代码,我的老板也要求我用 C# 来做。我的问题是在 1 个查询中选择多个值时,我不知道如何管理它们。它也是循环的,因为如果员工有超过 1 个假期,则需要多次执行查询+填充链。如果不清楚,问题是如何告诉程序不要选择它已经选择的行?
  • 乔希,我可以向你保证,我已经阅读了你的原始问题和编辑内容:) 在我的回答中,我实际上向你展示了为什么你的问题不起作用,你错过了吗?您始终只使用 PayrollNo 作为标准来选择(标量为单个字段)相同的第一行数据。当您使用 ExecuteScalar 时,即使查询应该返回多行和多列,您也明确要求第一行的最左侧列数据。

标签: c# datatable


【解决方案1】:

首先对您的代码稍作(不完整)修改:

  string ConnString = @"Provider = Microsoft.ACE.OLEDB.12.0;Data Source=|DataDirectory|\\HoliPlanData.accdb;Persist Security Info=False";
  string Query = "SELECT PayrollNo FROM [Employee] WHERE (FirstName + ' ' + LastName) = @Name"; //Will supply selected Employee's PayrollNo

  string getHolidayQuery = @"SELECT StartDate, EndDate, Reason 
     FROM [Holiday] 
     WHERE PayrollNo = @PayrollNo"; // Will select data from Holidays

  string getAbsencesQuery = @"SELECT StartDate, EndDate, Comments 
     FROM [Absences] 
     WHERE PayrollNo = @PayrollNo";

  var holidayData = new DataTable();
  var absenceData = new DataTable();

  using (OleDbConnection conn = new OleDbConnection(ConnString))
  {
    var getPayroll = new OleDbCommand(Query, conn);
    getPayroll.Parameters.AddWithValue("@name", LblName.Text);

    var holidaysQuery = new OleDbCommand(getHolidayQuery, conn);
    var absencesQuery = new OleDbCommand(getAbsencesQuery, conn);
    conn.Open();

    int GotPayroll = Convert.ToInt32(getPayroll.ExecuteScalar());   //Uses Query to Get PayrollNo
    holidaysQuery.Parameters.AddWithValue("@PayrollNo", GotPayroll);
    absencesQuery.Parameters.AddWithValue("@PayrollNo", GotPayroll);

    holidayData.Load(holidaysQuery.ExecuteReader());
    absenceData.Load(absencesQuery.ExecuteReader());

    conn.Close();
  }

foreach (DataRow row in holidayData.AsEnumerable())
{
    // here you could craft a new data table
    // however there is a discrepancy. 
    // How would the data in Holiday would relate to those
    // in absences. With the relation known, we might have 
    // gotten a single datatable from the database.
    // Probably you should give data samples.


    //    DateTime StartHold = Convert.ToDateTime(GetStart.ExecuteScalar());
    //    DateTime EndHold = Convert.ToDateTime(GetEnd.ExecuteScalar());
    //    string ReasonHold = (GetReason.ExecuteScalar()).ToString();

    // Above lines are sort of saying:
    //    DateTime StartHold = (DateTime)row["StartDate"];
    //    DateTime EndHold = (DateTime)row["EndDate"];
    //    string ReasonHold = (string)row["Reason"];
    // without roundtripping to database per field

  }
//...

基本上,我的意思是,不是对每个字段执行一个标量,而是对您的数据库进行一次调用。使用您的标量方法,查询中没有任何内容可以将“第一”行与第二行或第三行区分开来。即:对于 payrollNo 5,假设有 3 个假期条目:

5, Jan 22, 2016, Jan 26, 2016
5, Feb 1, 2016, Feb 5, 2016
5, Feb 22, 2016, Feb 26, 2016

对于仅使用 PayrollNo 作为条件的查询,您将始终从第一行获取值(例如,2016 年 1 月 22 日为 startDate)。

首先将这些数据放入本地数据表中,至少提供了一个可迭代的结构。

从这些数据或直接从源创建数据表会更容易,使用 Linq,正如我最初所说的(对于 Linq,它是 IQueryable 而不是 DataTable,但也可能与 DataTable 一起使用)。而 Linq 是 C# 本身的一部分 :) 意味着语言集成查询 - 内置于 C# 本身的 IOW 查询语言。这是从示例 Northwind 查询一些数据的 Linq 代码(使用 codeplex 中的 IQToolkit) - 这是您需要的所有代码,包括 Form 和 DataGridView:

void Main()
{
  string path   = @"D:\data\Northwind.accdb";
  string conStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source="+path;

  var provider= new AccessQueryProvider(new OleDbConnection(conStr), 
                    new ImplicitMapping(), QueryPolicy.Default);

  var sampleOrders = provider.GetTable<Order>("Orders")
                  .Where (o => o.OrderDate == new DateTime(1997,1,1));

  Form f = new Form{Height=800,Width=1024};
  DataGridView dgv = new DataGridView { Dock=DockStyle.Fill };
  dgv.DataSource = sampleOrders.ToList();

  f.Controls.Add(dgv);
  f.Show();
}

// Entity Class
public class Order {
 public int OrderID { get; set; }
 public string CustomerID { get; set; }
 public int EmployeeID { get; set; }
 public DateTime OrderDate { get; set; }
 public DateTime? RequiredDate { get; set; }
 public DateTime? ShippedDate { get; set; }
 public int ShipVia { get; set; }
 public decimal? Freight { get; set; }
 public string ShipCity { get; set; }
 public string ShipCountry { get; set; }
}

【讨论】:

  • 这是一个绝妙的帮助。使用这种方法,我将调整我的代码,并希望得到我想要的结论。谢谢你:)
  • 哦,在看到这个之前我在上面评论过 :) 很高兴我能提供帮助。
【解决方案2】:

要从 MS Access 数据库中获取 DataTable,请使用以下示例代码 sn-p:

列表 1. 使用 OleDb 对象库从 MS Access 获取 DataTable

private DataTable GetDataTable(string strCN, string strSQL)
{
    try
    {
        using (OleDbConnection _conn = new OleDbConnection(strCN))
        {
            using (OleDbCommand _command = new OleDbCommand())
            {
                _command.CommandType = CommandType.Text;
                _command.Connection = _conn;
                _command.CommandText = strSQL;
                _conn.Open();

                using (OleDbDataReader _dr = _command.ExecuteReader())
                {
                    DataTable _dt = new DataTable();
                    _dt.Load(_dr);
                    return _dt;
                }
            }
        }
    }
    catch
    {
        throw;
    }
}

其中strCN 是连接字符串,strSQL 是您的SELECT 查询。您可以调用此方法并将各种SQL 查询作为参数传递。

与您的情况相关,它看起来不像是正确的 SQL SELECT 查询来完成这项工作。如果您确实需要获取DataTable,然后将其绑定到数据感知控件(如GridView),那么您应该使用上面提供的方法。如果您只需要获取一个标量值,请使用清单 2 中的以下方法。

清单 2. 使用 OleDb 对象库从 MS Access 获取标量值

/// <summary>
/// Read DB Read Scalar on SQL command input
/// </summary>
/// <param name="SQL">string</param>
/// <returns>string</returns>
private static string ReadScalar(string ConnString, string SQL)
{
    string _ret;
    try
    {
        using (OleDbConnection _conn = new OleDbConnection(ConnString))
        {
            using (OleDbCommand _command = new OleDbCommand(SQL, _conn))
            {
                _command.CommandType = CommandType.Text;
                _conn.Open();
                _ret = _command.ExecuteScalar().ToString();
            }
            return _ret;
        }
    }
    catch { return null; }
}

希望这会有所帮助。

【讨论】:

  • 谢谢亚历克斯。然而,这不是我实际提出的问题的答案。
  • 好像您看到了问题的标题,但仅此而已。
  • 我也是这么想的 :) 除了轻微的本地处理。
  • @Josh,拜托,他想帮忙。无需争论,只需更好地解释您的需求并获得更好的帮助。
  • @Josh - 你是我在 StackOverflow 上看到的最不愉快的 cmets。我已经成为会员好几年了,从未见过如此幼稚的回应。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-06
  • 2019-12-08
  • 2012-08-13
  • 1970-01-01
  • 2020-05-01
  • 1970-01-01
相关资源
最近更新 更多