VB.NET - 处理带有可选参数的复杂 SQL 查询的最佳实践
VB.NET - Best practice for handling complex SQL queries with optional parameters
在我的应用程序中,用户应该可以选择浏览客户已下的订单 - 并且应该可以对搜索应用多个过滤器。这意味着我需要一个动态 SQL 查询,其中可以应用可变数量的参数。
在标准的 WinForms 应用程序中,处理此问题的最佳方法是什么?
到目前为止,我一直在使用 TableAdapter 和存储过程,但据我所知,我不能将它们与可选参数一起使用。因此,例如,如果用户想要查看所有客户订单,这没有问题。但也应该可以这样说,例如“显示过去 2 周内下达的所有订单,其中至少一个产品包含单词 'gift code'”。所以日期和产品名称将是可选参数,但如果我在存储过程中将它们留空,则会出现错误。
为了解决这个问题,我开始使用 SqlCommands 和参数在单独的 class 中构建自己的查询。我根据传入函数的参数为每个命令动态生成 commandText,然后我将参数添加到 SqlCommand,执行它并循环遍历 SqlDataReader 以构建一个项目列表,我将 return 添加到我的程序中。
例如(简体):
Dim cmd As New SqlCommand With {.Connection = con}
cmd.commandText = "SELECT o.id, o.customer_name, o.date, p.productName FROM orders o JOIN order_positions p ON o.id = p.order_id WHERE o.date >= @pDate"
cmd.Parameters.Add("@pDate", SqlDbType.DateTime).Value = searchDate
Dim reader As SqlDataReader = cmd.ExecuteReader()
Dim lstOrderItems As New List(Of OrderDisplayItem)
while reader.read
dim orderId as Integer = reader.Item(0)
dim customerName as String = reader.Item(1)
dim date as Date = reader.Item(2)
dim productName as String = reader.Item(3)
lstOrderItems.add(New OrderDisplayItem With{.id = orderId, .customerName = customerName, .date = date, .productName = productName})
End While
return lstOrderItems
现在显然这只是为了展示我是如何进行的。实际上,我必须创建额外的循环,因为一个订单可能包含一个或多个产品等。
我的问题是:这是处理这个问题的正确方法吗?感觉整个 class 会变得非常大,因为我还有其他查询,比如查找发票、商店销售等等 - 对于每个查询,我都必须编写这些 reader 循环如果我的数据库中的一个小东西发生变化,重新修改一遍。
在 Visual Studio tableAdapters 中真的不可能处理这个问题吗?
That means that I need a dynamic SQL query where a variable amount of parameters can be applied.
不,不是。您可以使用具有一组参数的单个查询,如果您像这样构建 SQL,则只需为您想要忽略的那些参数提供 NULL:
SELECT *
FROM MyTable
WHERE (@Column1 IS NULL OR Column1 = @Column1)
AND (@Column2 IS NULL OR Column2 = @Column2)
您的 VB 代码可能如下所示:
Using connection As New SqlConnection("connection string here"),
command As New SqlCommand(query, connection)
command.Parameters.Add("@Column1", SqlDbType.VarChar, 50).Value = If(TextBox1.TextLength = 0, CObj(DBNull.Value), TextBox1.Text)
command.Parameters.Add("@Column2", SqlDbType.VarChar, 50).Value = If(TextBox2.TextLength = 0, CObj(DBNull.Value), TextBox2.Text)
'...
End Using
当您为参数提供 NULL 时,这会有效地匹配每条记录并且该参数会被有效地忽略。您可以使用您喜欢的任何数据类型的任意数量的参数来做到这一点。
在我的应用程序中,用户应该可以选择浏览客户已下的订单 - 并且应该可以对搜索应用多个过滤器。这意味着我需要一个动态 SQL 查询,其中可以应用可变数量的参数。 在标准的 WinForms 应用程序中,处理此问题的最佳方法是什么?
到目前为止,我一直在使用 TableAdapter 和存储过程,但据我所知,我不能将它们与可选参数一起使用。因此,例如,如果用户想要查看所有客户订单,这没有问题。但也应该可以这样说,例如“显示过去 2 周内下达的所有订单,其中至少一个产品包含单词 'gift code'”。所以日期和产品名称将是可选参数,但如果我在存储过程中将它们留空,则会出现错误。
为了解决这个问题,我开始使用 SqlCommands 和参数在单独的 class 中构建自己的查询。我根据传入函数的参数为每个命令动态生成 commandText,然后我将参数添加到 SqlCommand,执行它并循环遍历 SqlDataReader 以构建一个项目列表,我将 return 添加到我的程序中。 例如(简体):
Dim cmd As New SqlCommand With {.Connection = con}
cmd.commandText = "SELECT o.id, o.customer_name, o.date, p.productName FROM orders o JOIN order_positions p ON o.id = p.order_id WHERE o.date >= @pDate"
cmd.Parameters.Add("@pDate", SqlDbType.DateTime).Value = searchDate
Dim reader As SqlDataReader = cmd.ExecuteReader()
Dim lstOrderItems As New List(Of OrderDisplayItem)
while reader.read
dim orderId as Integer = reader.Item(0)
dim customerName as String = reader.Item(1)
dim date as Date = reader.Item(2)
dim productName as String = reader.Item(3)
lstOrderItems.add(New OrderDisplayItem With{.id = orderId, .customerName = customerName, .date = date, .productName = productName})
End While
return lstOrderItems
现在显然这只是为了展示我是如何进行的。实际上,我必须创建额外的循环,因为一个订单可能包含一个或多个产品等。
我的问题是:这是处理这个问题的正确方法吗?感觉整个 class 会变得非常大,因为我还有其他查询,比如查找发票、商店销售等等 - 对于每个查询,我都必须编写这些 reader 循环如果我的数据库中的一个小东西发生变化,重新修改一遍。 在 Visual Studio tableAdapters 中真的不可能处理这个问题吗?
That means that I need a dynamic SQL query where a variable amount of parameters can be applied.
不,不是。您可以使用具有一组参数的单个查询,如果您像这样构建 SQL,则只需为您想要忽略的那些参数提供 NULL:
SELECT *
FROM MyTable
WHERE (@Column1 IS NULL OR Column1 = @Column1)
AND (@Column2 IS NULL OR Column2 = @Column2)
您的 VB 代码可能如下所示:
Using connection As New SqlConnection("connection string here"),
command As New SqlCommand(query, connection)
command.Parameters.Add("@Column1", SqlDbType.VarChar, 50).Value = If(TextBox1.TextLength = 0, CObj(DBNull.Value), TextBox1.Text)
command.Parameters.Add("@Column2", SqlDbType.VarChar, 50).Value = If(TextBox2.TextLength = 0, CObj(DBNull.Value), TextBox2.Text)
'...
End Using
当您为参数提供 NULL 时,这会有效地匹配每条记录并且该参数会被有效地忽略。您可以使用您喜欢的任何数据类型的任意数量的参数来做到这一点。