【问题标题】:Linq Query Returns Incorrect Result SetLinq 查询返回不正确的结果集
【发布时间】:2009-11-17 21:27:30
【问题描述】:

我有一个非常复杂的 Linq to SQL 查询,它从 Microsoft SQL Server 数据库返回一个结果集。查询是使用类似于以下的语法创建的:

Dim db as MyDataContext = MyGetDataContextHelper()
Dim qry = From rslt in db.MyView Select ColumnList

If userParam1 IsNot Nothing Then
    qry = qry.Where(lambda for the filter)
End If

etc....

Return qry.ToList()

有几个用户指定的查询过滤器,包括一个进行地理半径搜索的过滤器。

这就是问题所在。我在最后的“ToList”调用上设置了一个休息时间。当中断被击中时,我使用 Linq to SQL Debug Visualizer 查看生成的 SQL 语句。我将该复杂的 SQL 语句复制到 SQL Server Management Studio 查询窗口中,并针对我的数据库执行它以获得我想要的结果集。所以生成的 SQL 似乎产生了预期的结果。但是,当我执行查询对象的“ToList”方法时,返回的列表具有较少的行和一些不同的行。我也尝试过使用 DataContext 日志属性写入文件,结果相同。该查询在 SQL Management Studio 中生成正确的结果集,但从 ToList 方法生成的结果不正确。

怎么可能?如果生成的 SQL 只是通过与 SQL Server 的连接传递,它不应该完全生成我在 SQL Server Management Studio 中看到的结果集吗?我假设我对 Linq to SQL 机制有一些误解,即它不仅仅是对 SQL Server 的传递。对吗?

编辑: 根据下面的请求,这里是由 Linq 生成的 SQL 的一个非常精简的版本,为简洁起见,删除了大部分结果列。它在 SQL Management Studio 中产生正确的结果,但返回给我的应用程序的结果不同。

SELECT [t3].[Id]
FROM (
    SELECT DISTINCT [t1].[Id]
    FROM (
        SELECT [t0].[Id], [t0].[ItemDate]
        FROM [dbo].[MySearchView] AS [t0]
        ) AS [t1]
    WHERE (EXISTS(
        SELECT NULL AS [EMPTY]
        FROM [dbo].[ZipCoverage] AS [t2]
        WHERE ([t2].[Id] = [t1].[Id]) 
        AND ([t2].[Latitude] >= (41.09046 - (0.5))) 
        AND ([t2].[Latitude] <= (41.09046 + (0.5))) 
        AND ([t2].[Longitude] >= (-73.43106 - (0.5))) 
        AND ([t2].[Longitude] <= (-73.43106 + (0.5))) 
        AND (ABS(3956.08833132861 * 2 * ATN2(SQRT(POWER(SIN((((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Latitude]) - 0.717163818159029) / (CONVERT(Float,2))), 2) + (COS(0.717163818159029) * COS((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Latitude]) * POWER(SIN((((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Longitude]) - -1.28161377022951) / (CONVERT(Float,2))), 2))), SQRT((1 - POWER(SIN((((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Latitude]) - 0.717163818159029) / (CONVERT(Float,2))), 2)) + (COS(0.717163818159029) * COS((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Latitude]) * POWER(SIN(((CONVERT(Float,CONVERT(Float,0.0174532925199433))) * [t2].[Longitude]) / (CONVERT(Float,2))), 2))))) <= 5))) 
        AND ([t1].[ItemDate] <= '11/17/2009 8:12:42 PM')
    ) AS [t3]

更新 2009-11-17 能够就这个问题联系 MS。创建了一个示例应用程序,我将其提交给了他们的支持代表。他们已经复制了这个问题并正在研究。收到回复后会发布答案。

更新 2009-12-21 在微软的帮助下终于得到了正确的答案。请参阅下面我接受的答案以获得解释。

【问题讨论】:

  • 能否请您发布您的实际 LINQ 查询?你说这很复杂。鉴于其他一切似乎都井然有序,剩下的选择是您的 LINQ 查询包含仅运行时处理,翻译器无法将其转换为 SQL。
  • 我在下面回复了您对我的回答的评论。
  • 没有ORDER BY吗?我们讨论了多少行,您如何验证结果是否不同?有没有可能是顺序不同,结果看起来不一样?
  • 有一个 ORDER BY 子句,为简洁起见,我将其删除。我通过行数验证结果 - SQL Management Studio 中的查询为 13 个,Linq 的 ToList 返回为 8 个 - 以及 ID 列,其中一些相同,一些不同。
  • 您是否验证了调试输出显示的查询实际上是 Linq 发送到服务器的内容?我会运行快速跟踪以查看实际传递的内容,然后将其与您在 Management Studio 中运行的内容进行比较。除此之外,我没有任何其他建议,抱歉。

标签: .net sql-server linq linq-to-sql


【解决方案1】:

好吧,在与 Microsoft 非常有帮助的支持代表反复讨论之后,我们终于找到了问题的根源。不幸的是,我没有在我原来的帖子中提供足够的信息让这里的任何人做出决定,所以我在这方面道歉。

这就是问题所在 - 作为构造相关 LINQ 查询的代码的一部分,我声明了一个 .Net 变量,如下所示:

Dim RadCvtFactor As Decimal = Math.PI / 180

事实证明,当这被传递给 SQL 时,参数声明(如 LINQ 日志文件中所证明的那样)是 DECIMAL(29, 4)。由于声明中的比例值,一个无效值被传递到 RDBMS,导致查询结果出现奇怪的差异。

将 .Net 变量声明为 Single 值,如下所示:

Dim RadCvtFactor As Single = Math.PI / 180

彻底解决问题。

微软代表承认这种参数转换可能是一个“潜在问题”,并将咨询产品团队。

感谢所有提交答案的人。

【讨论】:

    【解决方案2】:

    立即想到的一件事是权限问题。程序和手动执行的查询是否可能在不同的凭据下运行,因此对数据库具有不同的访问级别?这会影响查询的结果。

    【讨论】:

    • 虽然这可能解释丢失的行,但我认为同一行在两种上下文中出现不同的可能性极小,除非查询是故意使用基于用户名的 CASE 表达式制作的。我假设这只是一个 SELECT 查询,而不是一个存储过程调用(Bob Mc 可能看不到这样的逻辑)。
    • 是的,这只是一个 SELECT 查询,而不是存储的 proc 调用。
    • 我使用程序的凭据登录到 SQL Server Management Studio 并尝试了查询。我得到了正确的结果,所以似乎不是权限。不过,谢谢。
    【解决方案3】:

    我将从查看您的 DataContext 开始。如果您的 DataContext 没有从 SQL Server 更新,那么您可能会返回旧版本的表。

    DataContext 维护数据库在创建时的状态。您希望为每组操作使用新的上下文。

    【讨论】:

    • 这是个好主意,也许可以尝试对单行进行非常简单的查询。更新数据库中的行,看看 Linq 是否赶上。
    • 当我读到这个答案时,我认为这肯定是问题所在,但我没有高兴地更新了 DBML 文件中的相关表和视图。
    • 不是 DBML,而是实际的 DataContext。每次执行新操作时,您都应该执行 Dim db as context = new MyDbContext() 。如果 MyGetDataContextHelper() 在调用之间创建重用上下文,它不会从数据库中获取最新的表。
    • 啊,现在我明白你的意思了。是的,每次运行此查询时,我都会获得一个新的 DataContext。辅助方法每次都会实例化一个新的数据上下文。
    【解决方案4】:

    另一种可能性是隔离级别和数据的性质。你是在 Linq 下使用 REPEATABLE READ 还是 READ UNCOMMITTED 或 SNAPSHOT?使用 SSMS 时会怎样?显然,如果数据在四处移动,那么宽松的隔离级别会让您跳过行、读取某些行两次、查看行的旧版本等。

    另外,您能否让我们稍微了解一下“非常复杂的查询”是什么样的?您不必使用真实的表名。

    【讨论】:

    • 根据您的要求,我发布了 Linq 生成的查询的精简版。我不确定我将如何使用 REPEATABL READ 或 READ UNCOMMITTED,你能详细说明一下吗?此外,数据是非常静态的——它不是事务性的,更多的是用于报告。谢谢。
    • 我不使用 Linq,但我的理解是,您可以使用以下内容显式设置隔离级别: IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted ... 但是既然您说数据是相对静态,我仍然猜测(a)您正在比较的查询不是实际发送的查询,或者(b)您的 DataContext 是陈旧的。
    【解决方案5】:

    您可以使用DebuggerWriter 来检查发送到服务器的实际 SQL。

    【讨论】:

    • 谢谢,我已经这样做了,结果相同。当我注意到我使用“DataContext 日志属性”写入文件时,这就是我在原始帖子中的意思。除非你的意思是别的。如果有,请说明。
    【解决方案6】:
    qry.ToList()
    

    此语句创建并返回您想要的列表。如果您想稍后使用列表,则需要将结果分配给某些东西(例如局部变量)。

    编辑:感谢更新。

    我怀疑一定有一些你没有告诉我们的东西也可能是一个问题,它可能存在于这里:

    Dim db as MyDataContext = MyGetDataContextHelper()
    

    此方法是否连接到与您使用 sql studio 时连接的数据库相同的数据库?

    • 检查数据上下文的 Connection 属性。
    • 通过使用 sql 分析器监视查询,确保向数据库发出查询。
    • 发出一个非常简单的查询并确认它返回正确的结果。

    【讨论】:

    • 我的代码应该是 Return qry.ToList(),因为代码 sn-p 是返回列表的函数的一部分。我已经编辑了代码以反映这一点。
    【解决方案7】:

    这听起来可能很愚蠢,但总是值得检查一下,您是否在 SSMS 中连接到与您的应用程序相同的数据库环境? :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-09-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-12
      相关资源
      最近更新 更多