.NET DataTable在使用Load(DataReader)方法时跳过了某些行。

25

我正在尝试使用以下代码填充DataTable,以构建一个LocalReport:

MySqlCommand cmd = new MySqlCommand();
cmd.Connection = new MySqlConnection(Properties.Settings.Default.dbConnectionString);
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT ... LEFT JOIN ... WHERE ..."; /* query snipped */

// prepare data
dataTable.Clear();
cn.Open();
// fill datatable
dt.Load(cmd.ExecuteReader());
// fill report
rds = new ReportDataSource("InvoicesDataSet_InvoiceTable",dt);
reportViewerLocal.LocalReport.DataSources.Clear();
reportViewerLocal.LocalReport.DataSources.Add(rds);

有一次我注意到报告不完整,缺少一条记录。我更改了一些条件,以便查询返回正好两行,但是......惊奇的是:报告只显示了一行而不是两行。我尝试进行调试以找出问题所在,但是卡住了。

 dt.Load(cmd.ExecuteReader());

我发现DataReader包含两条记录,但是DataTable只包含一条。偶然间,我给查询添加了一个ORDER BY子句,发现这次报告显示正确。



显然,如果SQL查询字符串中不包含ORDER BY,则DataReader包含两行,但DataTable仅读取它们中的最后一行(否则它将读取全部)。有谁能解释为什么会这样,并说明如何解决这个问题吗?

编辑:当我第一次发布问题时,我说它跳过了第一行;后来我意识到实际上它只读取了最后一行,因此我相应地编辑了文本(当时所有记录都被分成两行,它似乎跳过了第一行,实际上它只显示了最后一行)。这可能是由于它没有唯一标识符来区分MySQL返回的行,所以添加ORDER BY语句使它创建每行的唯一标识符。
这只是一个理论,我没有任何证据支持它,但我的所有测试似乎都导致同样的结果。


3
Mod+1,因为自从第一个beta版以来,我每天都在编写.NET和ADO.NET,但我从未知道我可以直接调用dt.Load()而不需要使用DataAdapter。唉。 - Dave Markle
1
这里情况也类似,Dave。我只是大约一个月前才了解到这件事。 - Joel Coehoorn
今天我发布了一个重复的问题(https://stackoverflow.com/questions/28676382/datatable-load-shows-less-rows-than-source-datareader),最终通过包装查询解决了。我想强调这仅发生在MYSQL上,而不是在SQLSERVER上。我认为问题不应该是由DataTable组件引起的,而可能是Mysql连接器中的错误。有什么想法吗? - usr-local-ΕΨΗΕΛΩΝ
15个回答

13

经过一番摆弄后,我发现DataTable.Load方法在底层数据中需要一个主键列。如果你仔细阅读文档,这一点就很明显了,虽然没有明确说明。

如果你有一个名为“id”的列,它似乎会使用它(这对我很有帮助)。否则,它似乎只是使用第一列,无论它是否唯一,并在读取时覆盖具有相同值的行。如果你没有名为“id”的列,并且你的第一列不是唯一的,我建议在加载数据读取器之前尝试明确设置datatable的主键列。


1
我添加了一个ID列,但没有任何效果。 - Rhyous

6

如果有人像canceriens一样遇到类似的问题,我在调用dt.load(DataReader)之前使用了DataReader.Read ...而不是使用DataReader.HasRows来检查存在性。 哎呀!


5

我曾经遇到了同样的问题。我从你的博客中得到启示,在查询中加入 ORDER BY 子句,以便它们可以一起形成所有记录的唯一键。这解决了问题。有点奇怪。


5

不要使用

dr.Read()

由于它将指针移动到下一行。 删除这行希望它能正常工作。


1
哦,太感谢你提供这个小细节了,它解决了我的问题。 - EduBic

4
今天遇到了这个问题。不幸的是,这个主题中没有什么能解决它的东西,但是当我在另一个SELECT语句中包装我的SQL查询时,它就可以工作了!
例如:
SELECT * FROM (
    SELECT ..... < YOUR NORMAL SQL STATEMENT HERE />
) allrecords

奇怪的事情发生了....

4
我遇到了同样的问题。这是因为所有行的主键相同。它可能被用作结果的键,因此它只是不断地覆盖同一行。
Datatables.Load指向填充方法以了解其工作原理。该页面说明它是主键感知的。由于主键只能出现一次并且被用作行的键...
"然后,填充操作将行添加到DataSet中目标DataTable对象中,如果它们不存在,则创建DataTable对象。在创建DataTable对象时,填充操作通常仅创建列名称元数据。但是,如果MissingSchemaAction属性设置为AddWithKey,则还会创建适当的主键和约束。" (http://msdn.microsoft.com/en-us/library/zxkb3c3d.aspx)

可能不仅影响主键。如果主键不在结果中,它可能会影响另一列的键。 - Bluebaron
我回去检查了一下,只是为了确保。没错。阅读器返回5行,但dt.Load只给你阅读器中的最后一行。只是让大家知道我不是在胡说八道。我想这是第一次有人真正指出了这个问题。请检查一下! :) - Bluebaron

1

我知道这是一个旧问题,但对于我来说,在查询Access数据库并注意到查询中缺少1行时,解决方法是更改以下内容:

    if(dataset.read())  - Misses a row.

    if(dataset.hasrows) - Missing row appears.

1
对于像我一样遇到这个线程的其他人,关于DataTable由MySql中的唯一ID填充的答案是正确的。但是,如果表包含多个唯一ID,但仅从MySql命令返回单个ID(而不是使用'*'接收所有列),那么该DataTable将仅按照给定的单个ID组织并且表现为查询中使用了'GROUP BY'。简而言之,DataReader将提取所有记录,而DataTable.Load()仅查看检索到的唯一ID并使用其来填充DataTable,从而跳过信息行。

1

你能从SQL Profiler中获取实际运行的查询并尝试运行它吗?它可能不是你期望的结果。

当使用SqlDataAdapter.Fill(dataTable)时,你是否得到相同的结果?

你尝试过在读取器上使用不同的命令行为吗?MSDN文档


是的,我已经尝试在MySQL客户端中运行查询。它们之间唯一的区别是一个查询中包含了"ORDER BY Invoice.ID"语句,而另一个没有,但是它们都按照相同的顺序返回两个行。我现在会去检查SqlDataAdapter。 - Tom
有什么进展吗?你碰巧在查询中使用了 TOP 吗? - StingyJack

0

不确定为什么您在数据表中丢失了行,可能需要关闭读取器吗?无论如何,这是我通常加载报告的方式,每次都有效...

        Dim deals As New DealsProvider()
        Dim adapter As New ReportingDataTableAdapters.ReportDealsAdapter
        Dim report As ReportingData.ReportDealsDataTable = deals.GetActiveDealsReport()
        rptReports.LocalReport.DataSources.Add(New ReportDataSource("ActiveDeals_Data", report))

好奇是否仍会发生。


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