SQL BETWEEN 查询返回不正确的结果

Posted

技术标签:

【中文标题】SQL BETWEEN 查询返回不正确的结果【英文标题】:SQL BETWEEN Query returning incorrect results 【发布时间】:2016-07-08 16:14:20 【问题描述】:

我正在编写一个 vb.net 应用程序,它正在搜索 Access 2010 数据库并且遇到了一个我似乎无法解决的问题。下面这段代码就是问题所在。

 Using SQLcon 'public property containing OleDbConnection
    Dim ds As New DataSet
    Dim da As New OleDbDataAdapter
    Dim cmd As New OleDbCommand

    cmd.Connection = SQLcon
    cmd.CommandType = CommandType.Text

    Dim heightMin As Double = OverallHeight - 0.25
    Dim heightMax As Double = OverallHeight + 0.125

    cmd.CommandText = "SELECT * FROM [CAGE - BARREL] WHERE [Type]=@p1 AND [Valve Size]=@p2 AND [Cage Height] BETWEEN @p3 AND @p4"
    cmd.Parameters.AddWithValue("@p1", CageType) '[Type]
    cmd.Parameters.AddWithValue("@p2", ValveSize) '[Valve Size]
    cmd.Parameters.AddWithValue("@p3", heightMin) '[Cage Height]
    cmd.Parameters.AddWithValue("@p4", heightMax) '[Cage Height]

    da = New OleDbDataAdapter(cmd)
    SQLcon.Open()
    da.Fill(ds, "[CAGE - BARREL]")
    SQLcon.Close()
End Using

当我使用一组特定的值直接在 Access 中运行 SQL 语句时,我得到了预期的 3 个结果。当我通过我的程序运行它时,我得到 6,其中 3 个值超出了 BETWEEN 语句的范围。我已经仔细检查了 Access 中字段的数据类型和 vb.net 中的变量。我知道我错过了一些东西。有没有人知道我哪里出错了?

更新:

使用的值如下。

@p1="INLET"
@p2="10.50" 'this is a text field
@p3=heightMin=6 'this is a number/double
@p4=heightMax=6.375 'this is a number/double

.net 程序返回的值包括超出上述范围的 5,5 和 3.688 以及正确的 6、6 和 6.188。

【问题讨论】:

您能否edit 您的问题提供说明问题的示例值?例如,“当我将 OverallHeight 指定为 ___ 时,我得到的行包含的 [Cage Height] 值超出了 heightMinheightMax 的范围。 如果不使用.AddWithValue,例如cmd.Parameters.Add(New OleDbParameter With .ParameterName = "@p3", .OleDbType = OleDbType.Double, .Value = heightMin),它是否有效? (对于其他参数也是如此。) [Cage - Barrel] 是一张桌子 【参考方案1】:

我无法重现该问题(或者)。也就是说,我倾向于使用某种形式的>= AND <= 来代替BETWEEN

测试数据:

Id  CageType    ValveSize   Height
1     A            XS       0.25
2     A            S        0.51
3     A            S2       0.55
4     A            M        0.95
5     A            L        1.26
6     B            S        0.58

代码:

Dim sql1 = "SELECT * FROM Cage WHERE CageType=@p1 AND ValveSize=@p2 AND Height BETWEEN @p3 AND @p4"
Dim sql2 = "SELECT * FROM Cage WHERE ValveSize=@p2 AND Height BETWEEN @p3 AND @p4"

Dim t As String = "A"
Dim v As String = "S"
Dim minH As Double = 0.45
Dim maxH As Double = 0.65

Dim dt As New DataTable
Using dbcon As New OleDbConnection(ACEConnStr)
    Using cmd As New OleDbCommand(sql2, dbcon)
        dbcon.Open()

        ' only used with 'sql1':
        'cmd.Parameters.Add("@p1", OleDbType.VarChar).Value = t
        cmd.Parameters.Add("@p2", OleDbType.VarChar).Value = v
        cmd.Parameters.Add("@p3", OleDbType.Double).Value = minH
        cmd.Parameters.Add("@p4", OleDbType.Double).Value = maxH

        dt.Load(cmd.ExecuteReader)
        dgv1.DataSource = dt
    End Using
End Using

结果:

我认为您的数据或数据类型有些奇怪。列出了 2 个 SQL 语句,仅用于测试并减少所需的测试数据量。 "SELECT * FROM Cage WHERE ValveSize=@p2 AND Height > @p3 AND Height <= @p4" 返回相同的结果。

请注意,OleDBConnectionOleDbCommand 之类的东西会分配资源,并且应该在您使用完它们后进行处置。 Using 语句为我们做到了这一点。我还使用了Add 而不是AddWithValue,以便可以指定数据类型,让VB/OleDB 没有猜测发生了什么的余地。此外,不需要临时适配器或DataSet - 您可以直接用DataReader 填充DataTable

还要注意OleDb 不使用命名参数,它们只是位置占位符。使用OleDb,您必须 Add 它们按照它们在SQL 中出现的确切顺序。你的可以,但这解释了为什么上面的代码可以注释掉 @p1 参数并且仍然有效。


后记

正如在某些时候可能会被删除的 cmets 中所述,真正的问题是 OleDbCommand 被重新使用。因此,它可能具有上次遗留下来的参数或值。这意味着发布的代码不是导致问题的真实代码。

DbCommand 对象是特定于查询的对象,因为它们可能包含参数,这是另一个像Using 块一样创建、使用和处置它们的原因。


另见:

What do BETWEEN and the devil have in common? Can we stop using AddWithValue() already?

(这些是博客 - 某些知名人士的有趣意见 - 不是刻在石头上的宙斯的话)

【讨论】:

【参考方案2】:

我认为这是一些时髦的 OleDb 特性。在这些情况下,您不妨直言不讳:

SELECT * 
FROM [CAGE - BARREL] 
WHERE [Type] = @p1 
    AND [Valve Size] = @p2 
    AND [Cage Height] >= @p3 
    AND [Cage Height] <= @p4

【讨论】:

使用该方法时,我的 .net 程序返回了 0 条记录。虽然它在 Access 中完美运行。这真是令人沮丧。 @Mike 很奇怪。你确定这不是数据类型问题吗?你的参数的数据类型是什么,cage height列的数据类型是什么? Access 是 Number 类型,size double 并且我的代码中的变量是 double。

以上是关于SQL BETWEEN 查询返回不正确的结果的主要内容,如果未能解决你的问题,请参考以下文章