如何实现更高效的搜索功能?

时间:2016-04-27 06:26:25

标签: mysql sql database vb.net

在我的数据库中有3列,分别是姓名,年龄,性别。 在程序中,我只想使用1个搜索按钮。单击该按钮时,程序将确定文本框中的哪三个已输入并搜索正确的数据。

您如何使用查询?例如,如果Name和Gender有文本,则查询:

Select * from table Where (Name = @name) AND (Gender = @gender)

当只输入名称时,我只查询名称。我是否必须按文本框检查文本框是否有用户输入,然后为每个文本框写入多个查询?或者有更好的方法吗?

编辑(29/5/16):我尝试用另一种方式做这个

myCommand = New MySqlCommand("Select * from project_record Where
                       (FloatNo = @floatNo OR FloatNo = 'None') AND 
                       (DevCompanyName = @devCompanyName OR DevCompanyName = 'None') AND 
                       (DevType = @devType OR DevType = 'None') AND 
                       (LotPt = @lotPt OR LotPt = 'None') AND
                       (Mukim = @mukim OR Mukim = 'None') AND
                       (Daerah = @daerah OR Daerah = 'None') AND
                       (Negeri = @negeri OR Negeri = 'None') AND
                       (TempReference = @tempRef OR TempReference = 'None')", sqlConn)

但正如你可以猜到的那样它也不会有效地工作,因为如果我只输入DevType的输入并将其他文本框留空,则查询不会仅提取DevType的所有记录。它只会显示为无记录。

8 个答案:

答案 0 :(得分:4)

Select * from table 
Where (Name = @name OR @name is Null) 
  AND (Gender = @gender OR @gender is Null)
 ...

它应该是一个查询

答案 1 :(得分:3)

其他答案解释了如何简化查询。摆脱ORs尤其重要,因为它们会禁止使用索引。

一旦干净地构建了查询,您需要考虑数据集并确定通常用于过滤的列。然后为他们制作一些INDEXes。您将无法提供“所有”可能的索引,因此我会警告您考虑数据集。

构建索引时,可以使用单列索引或多列索引。对于您的数据类型,我建议从几个2列索引开始。确保每个索引都以不同的列开头。

对于Where (Name = @name) AND (Gender = @gender),以下是一些注意事项:

INDEX(gender) is useless because of low 'cardinality';
INDEX(gender, name) might be useful, but the following would be better:
INDEX(name)

nameDevCompanyName之类的内容几乎是唯一的,因此1列索引可能很好。

如果您有genderage,则INDEX(age, gender) 可能有用。

MySQL几乎不会为单个SELECT使用两个索引。

顺便说一下,WHERE的构造可以在存储过程中完成。您需要CONCATPREPARE

答案 2 :(得分:2)

原始答案

(向下滚动以查看更新)

您可以尝试以下方法:

  • 构建一个仅包含具有输入的文本框值的列表
  • 将该列表的项目与“AND”字符串
  • 一起设置为联接字符串
  • 将该字符串附加到标准SELECT语句

代码如下所示:

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    Dim Predicate1 As String = Me.TextBox1.Text
    Dim Predicate2 As String = Me.TextBox2.Text
    Dim Predicate3 As String = Me.TextBox3.Text
    Dim PredicateList As New List(Of String)
    Dim WhereClause As String
    Dim Query As String

    If Predicate1 <> String.Empty Then
        PredicateList.Add("Name=""" & Predicate1 & """")
    End If
    If Predicate2 <> String.Empty Then
        PredicateList.Add("Age=""" & Predicate2 & """")
    End If
    If Predicate3 <> String.Empty Then
        PredicateList.Add("Gender=""" & Predicate3 & """")
    End If

    WhereClause = String.Join(" AND ", PredicateList.ToArray)
    Query = "SELECT * FROM TABLE WHERE " & WhereClause
    MessageBox.Show(Query)

End Sub

更新

除了SQL注入的注释之外,这里还有一个更新的样本。

Dim Command As SqlClient.SqlCommand
Dim Predicate1 As String = Me.TextBox1.Text
Dim Predicate2 As String = Me.TextBox2.Text
Dim Predicate3 As String = Me.TextBox2.Text
Dim ParameterList As New List(Of SqlClient.SqlParameter)
Dim PredicateList As New List(Of String)
Dim BaseQuery As String = "SELECT * FROM TABLE WHERE "

If Predicate1 <> String.Empty Then
    PredicateList.Add("name = @name")
    ParameterList.Add(New SqlClient.SqlParameter("@name", Predicate1))
End If
If Predicate2 <> String.Empty Then
    PredicateList.Add("age = @age")
    ParameterList.Add(New SqlClient.SqlParameter("@age", Predicate2))
End If
If Predicate3 <> String.Empty Then
    PredicateList.Add("gender = @gender")
    ParameterList.Add(New SqlClient.SqlParameter("@gender", Predicate3))
End If

Command = New SqlClient.SqlCommand(BaseQuery & String.Join(" AND ", PredicateList.ToArray))
Command.Parameters.AddRange(ParameterList.ToArray)

答案 3 :(得分:2)

COALESCE是你的朋友。您可以使用它来使where子句忽略参数为NULL的比较。

Select * from table Where (Name = COALESCE(@name,table.Name))
AND (Gender = COALESCE(@gender,table.Gender))

因此,如果@name参数为NULL,COALESCE(@name,table.Name)将返回&#39;名称&#39;的值。当前行的列和(Name = COALESCE(@name,table.Name))将始终为真。

这假设如果在文本框中没有输入任何值,则相应的参数将为NULL。如果它是一个诸如“无”之类的值,则可以使用NULLIF函数来映射“无”和“无”。为NULL

Select * from table Where 
(Name = COALESCE( NULLIF( @name, 'None'), table.Name)) 
AND (Gender = COALESCE( NULLIF( @gender, 'None'), table.Gender))

答案 4 :(得分:1)

How to implement a more efficient search?

答案部分取决于您对效率的定义。我怀疑你意味着更少的代码和更少的块等等。但从根本上说,运行一个新的SELECT *查询来应用过滤器是低效的,因为你的基础数据集可以是所有行,你只是摆弄用户查看它

我有一个数据库,其中包含 Fish Color (字符串), Bird Group 列中的随机数据>(int)和 Active ,对于问题中的名称年龄性别应该足够相似 - 或者最底层的其他事情。

数据表

填充数据表并将其绑定到DGV:

' form level object
Private dtSample As DataTable
...
' elsewhere
Dim sql = "SELECT  Id, Name, Descr, `Group`, Fish, Bird, Color, Active FROM Sample"
Using dbcon As MySqlConnection = New MySqlConnection(MySQLConnStr)
    ' create SELECT command with the Query and conn
    Dim cmd As New MySqlCommand(sql, dbcon)
    ...
    daSample.Fill(dtSample)
    daSample.FillSchema(dtSimple, SchemaType.Source)
End Using
dgv2.DataSource = dtSample

展望未来,我们可以在不发出新查询的情况下过滤该表的用户 视图

过滤器控件

如果某些字段仅限于某些选择,例如Gender,则可以使用ComboBox代替TextBox。这是为了帮助用户成功并避免拼写错误(制作 Mael 而不是男性;或者在这里,正确拼写 Baracuda 我的意思是 Baraccuda ,呃 Barracuda 正确。

为了便于说明, Fish 是用户可以输入任何内容的东西,但 Bird 被限制为一组选择。如果有Bird表,则可以绑定或填充cboBird。但您也可以从主/基表中填充它:

Dim birds = dtSample.AsEnumerable.Where(Function(d) d.IsNull(5) = False).
            Select(Function(d) d.Field(Of String)("Bird")).
            Distinct.
            ToArray()
cboBird.Items.AddRange(birds)

如果&#34; Finch&#34;是一个合法的选择,但数据库中没有,它不会显示在列表中。根据应用程序的不同,这可能是一件好事:

  • 如果用户在Finch上过滤并且没有结果记录,则您不需要MessageBoxStatusBar消息来解释空结果集。
  • 如果列表中没有某些内容,则表示预先表示没有这些内容。然后,训练为什么已知元素不在列表中。
  • 另一方面,在最近添加新记录的情况下,您每次使用这些过滤器控件之前都必须重新填充这些过滤器控件。如果控件位于Dialog或不同TabPage,则可以根据需要轻松完成。
  • 它并不总是适用,但它可以帮助用户避免拼写错误。

取决于应用程序,这两种方法是否有价值。

DBNull /&#39;无&#39;

我不确定你为什么要添加&#39;没有&#39;每个条款。如果有人想要查看所有“John”或所有“Cod&#39;记录,似乎他们也不会对“没有”感兴趣。就个人而言,Null / DBNull似乎是一种更好的处理方式,但很容易添加或不添加任何一种形式。

使用DBNull / None过滤只是那些似乎更多有价值。鸟类列表的上述代码会过滤掉DBNull,我也会过滤none。然后,在将结果添加到ComboBox之前,添加“无”#39;首先是项目,所以它位于顶部。

这又取决于应用程序的功能; Or = 'None',在这种情况下可能非常有意义。

过滤

使用{{1>}用于 Fish 群组,{{1>}用于 Bird Color < / em>和 Active TextBox,代码可以形成过滤器:

ComboBox
  • 使用适合应用程序需要的CheckBox
  • 如果相关控件具有值,则过滤条件仅添加到列表中(如上所述,这可能包括&#39;无&#39; )。
  • 对于Dim filterTerms As New List(Of String) Dim filterFmt = "{0} = '{1}' " ' OR: ' Dim filterFmt = "{0} = '{1}' OR {0} Is Null" ' OR: ' Dim filterFmt = "{0} = '{1}' OR {0} = 'none'" If String.IsNullOrEmpty(tbSearchFish.Text) = False Then Dim txt = tbSearchFish.Text.Replace("'", "''") filterTerms.Add(String.Format(filterFmt, "Fish", txt)) End If If cboBird.SelectedIndex > -1 Then filterTerms.Add(String.Format(filterFmt, "Bird", cboBird.SelectedItem.ToString)) End If If String.IsNullOrEmpty(tbGroup.Text) = False Then Dim n As Int32 If Int32.TryParse(tbGroup.Text, n) Then filterTerms.Add(String.Format(filterFmt, "[Group]", n)) End If End If If cboColor.SelectedIndex > -1 Then filterTerms.Add(String.Format(filterFmt, "Color", cboColor.SelectedItem.ToString)) End If If chkActive.Checked Then ' NOTE: I do not have TreatTinyAsBoolean turned on ' for some reason filterTerms.Add(String.Format(filterFmt, "Active", "1")) End If If filterTerms.Count > 0 Then Dim filter = String.Join(" AND ", filterTerms) dtSample.DefaultView.RowFilter = filter Dim rows = dtSample.DefaultView.Count End If ,它会转义任何嵌入的标记,例如可能在filterFmtTextBox等名称中找到的标记。它用两个滴答代替。
  • 由于群组是数字,因此会测试有效的O'Malley输入
  • 如果D'Artgnan列表中有元素,则会创建过滤字符串
  • 过滤器已应用于Int32(您也可以使用filterTermsDefaultView.Filter),以便代码无需查询数据库即可提供过滤功能
  • DataView会告诉您当前视图中有多少行。

唯一有一半棘手的问题是像性别 Active 这样的布尔值,因为这些布尔实际上解决了三个选择:BindingSource。为此,我会使用Rows并将其忽略为SelectedIndex 0。我没有为此烦恼,因为{Any/Either, A, B}概念已被充分涵盖。结果:

enter image description here

是否更多&#34;高效&#34;?

仍然取决于  enter image description here它不会重新查询数据库以获取应用已经拥有的行   enter image description here没有创建新的ComboBoxCombo或其他DBProvider对象,只是列表。
 enter image description here无需在循环中动态创建带有N个参数的SQL语句,以避免SQL注入/特殊字和字符。
 enter image description here它甚至无法在数据库中查询过滤条款的项目。如果DB中有静态列表,则第一次使用过滤器时可以加载一次  enter image description here删除过滤器很容易,不需要在没有DBConnection子句的情况下再次查询  enter image description here适用的DBCommand可帮助用户找到所需内容,避免拼写错误  enter image description here SQL&#34;更清洁&#34;。更多&#34;高效?代码并没有真正搞乱新SQL,只是一些WHERE子句。

代码少吗?我不知道,因为我们只看到了结果。它并没有把我作为很多代码来做它所做的事情。

答案 5 :(得分:1)

  

在我的数据库中有3列,分别是姓名,年龄,性别。在程序中,我只想使用1个搜索按钮。单击该按钮时,程序将确定文本框中的哪3个输入并搜索正确的数据。

     

当只输入名称时,我只查询名称。我是否必须按文本框检查文本框是否有用户输入,然后为每个文本框写入多个查询?或者有更好的方法吗?

SELECT * FROM `table`
WHERE (`name` = @name AND `name` IS NOT NULL)
OR (`age` = @age AND (`age`>0 OR `age` IS NOT NULL))
OR (`gender` = @gender AND `gender` IS NOT NULL);

如果所有文本框都有值,则使用上述查询,结果将不是一条记录(就像在字段之间使用逻辑AND一样)。如果你只想要那条记录,你将使用php从其他结果中过滤服务器端。

您可以在Fiddle

中自行查看结果

修改

为了解决上述不便(在需要时不会轻易带来单个结果),我从answer获得了一些帮助,并将上述查询重写为:

SELECT *, IF(`name`=@name, 10, 0) + IF(`age`=@age, 10, 0) + IF(`gender`=@gender, 10, 0) AS `weight` 
FROM `table` 
WHERE (`name` = @name AND `name` IS NOT NULL) 
OR (`age` = @age AND (`age`>0 OR `age` IS NOT NULL)) 
OR (`gender` = @gender AND `gender` IS NOT NULL) 
HAVING `weight`=30;

enter image description here  或仍然获得结果为weight的所有记录

SELECT *, IF(`name`=@name, 10, 0) + IF(`age`=@age, 10, 0) + IF(`gender`=@gender, 10, 0) AS `weight` 
FROM `table` WHERE (`name` = @name AND `name` IS NOT NULL) 
OR (`age` = @age AND (`age`>0 OR `age` IS NOT NULL)) 
OR (`gender` = @gender AND `gender` IS NOT NULL) 
ORDER BY `weight` DESC;

enter image description here

答案 6 :(得分:1)

你非常接近。我们来看看

(FloatNo = @floatNo OR FloatNo = 'None')

所以你希望字段是给定输入还是'None'?但是(表示)你的表中没有FloatNo'无'的记录。你真正想做的是找出输入是否为空(即为空):

(FloatNo = @floatNo OR @floatNo = '')

对于用户错误输入空白的情况,您也可以忽略这一点:

(FloatNo = @floatNo OR TRIM(@floatNo) = '')

整件事:

myCommand = New MySqlCommand(
  "Select * from project_record Where
         (FloatNo = @floatNo OR TRIM(@floatNo) = '') AND 
         (DevCompanyName = @devCompanyName OR TRIM(@devCompanyName) = '') AND 
         (DevType = @devType OR TRIM(@devType) = '') AND 
         (LotPt = @lotPt OR TRIM(@lotPt) = '') AND
         (Mukim = @mukim OR TRIM(@mukim) = '') AND
         (Daerah = @daerah OR TRIM(@daerah) = '') AND
         (Negeri = @negeri OR TRIM(@negeri) = '') AND
         (TempReference = @tempRef OR TRIM(@tempRef) = '')", sqlConn)

答案 7 :(得分:0)

您的方法有什么问题?

改变 (FloatNo = @floatNo OR FloatNo =&#39;无&#39;)

(FloatNo = @floatNo OR FloatNo =&#39;&#39;或FloatNo IS NULL)

为每个标准做到这一点 在此之后,您的查询将考虑空值和NULL值。