【问题标题】:mySQL Search Statement with Multiple filters具有多个过滤器的 mySQL 搜索语句
【发布时间】:2012-03-11 09:51:28
【问题描述】:

这几天我一直在尝试解决这个问题,但一直未能找到解决方案。很可能是因为我认为我问的问题不正确。

来了……

我正在尝试在我的网站上搜索律师名单。

Table 1
ID (primary)
Name
Category1 (fkey)
Category2 (fkey) 
Category3 (fkey)
Category4 (fkey)
Category5 (fkey)
Location1 (fkey)
Location2 (fkey)
Location3 (fkey)
Location4 (fkey)
Location5 (fkey)

Table 2 - Locations
ID (primary)
Name 

Table 3 - Categories
ID (primary)
Name

所以律师有多个类别和多个位置 -> 与表 2 和表 3 的一对多关系

第一个问题:我是否正确设置了表 1?我是否需要有多个位置和类别列(即位置 1、位置 2、位置 3 等...)还是让这变得复杂?

下一步...

我想在我的网站上进行复选框样式的搜索。用户可以按位置和/或按类别进行搜索。通过复选框,他们可以选择多个位置和/或类别。复选框值是位置和类别的 ID(不是名称)

这可以通过三种方式发生。

  1. 仅按位置搜索
  2. 仅按类别搜索
  3. 按位置内的类别搜索

我有两个问题。

  1. 我可以让场景 1 和 2 工作,但前提是选中一个复选框。
  2. 我什至不知道如何开始让方案 3 发挥作用。

这是我对场景 1 和 2 的内容

$AttorneyLocation = $_POST['AttorneyLocation'];

for ($i="0"; $i<count($AttorneyLocation); $i++) {
    if (!is_numeric($AttorneyLocation[$i])) {
        $AttorneyLocation[$i]="";
    }
    if (empty($AttorneyLocation[$i])) {
        unset($AttorneyLocation[$i]);
    }
}

$AttorneyLocation = implode (" OR ", $AttorneyLocation);

$sqlCommand = "SELECT att_id, att_name, att_logo, att_addy, att_town, att_profile_url FROM attorneys WHERE att_location1='$AttorneyLocation' OR att_location2='$AttorneyLocation' OR att_location3='$AttorneyLocation' OR att_location4='$AttorneyLocation' OR att_location5='$AttorneyLocation'";

Again, this works but only when ONE checkbox is selected, it fails when two or more are selected.基本上它似乎只搜索已选中的 LAST 复选框,而忽略它之前的复选框。

对于场景 3 - 我只是不确定从哪里开始,如何在位置搜索中加入类别搜索?

如果有人能指出我正确的方向,那就太好了,非常感谢!这是我第一次尝试创建这样的东西!

如果需要,这是我的表单代码 - 全部是动态创建的

 <input type='checkbox' name='AttorneyCategory[]' value='$cat_id'> $category<br />
 <input type='checkbox' name='AttorneyLocation[]' value='$loc_id'> $location<br />

【问题讨论】:

  • 快速说明 - 使用 for 循环时,您不应使用 $i&lt;count($AttorneyLocation) 作为第二个表达式,因为每次迭代都会计算计数。您应该首先将 count 的值分配给一个变量,然后在 for 循环中使用它。在这种情况下,这并不重要,但这是一个坏习惯。

标签: php mysql search checkbox


【解决方案1】:

正如@JonathanLeffler 所指出的,您的表 1 结构是不该做什么的完美示例。您的 Category* 和 Location* 字段应从表 1 中删除,并替换为两个多对多表 - 律师位置(律师 ID,位置 ID)和律师类别(律师 ID,类别 ID)。

首先修改你的数据库结构,然后我们可以解决其他问题。

下一轮-

<?php

foreach ($_POST['AttorneyLocation'] AS $key => $val)    {
    if (is_numeric($val)) {
        $_POST['AttorneyLocation'][$key] = intval($val);
    } else {
        unset($_POST['AttorneyLocation'][$key]);
    }
}

$AttorneyLocations = implode (',', $_POST['AttorneyLocation']);

foreach ($_POST['AttorneyCategory'] AS $key => $val)    {
    if (is_numeric($val)) {
        $_POST['AttorneyCategory'][$key] = intval($val);
    } else {
        unset($_POST['AttorneyCategory'][$key]);
    }
}

$AttorneyCategories = implode (',', $_POST['AttorneyCategory']);

$sqlCommand = 'SELECT DISTINCT att_id, att_name, att_logo, att_addy, att_town, att_profile_url FROM attorneys ';

if ($AttorneyLocations) {
    $sqlCommand .= 'INNER JOIN attorney_locations ON attorneys.att_id = attorney_locations.attorney_id ';
}
if ($AttorneyCategories) {
    $sqlCommand .= 'INNER JOIN attorney_categories ON attorneys.att_id = attorney_categories.attorney_id ';
}

$sqlCommand .= 'WHERE 1=1 ';

if ($AttorneyLocations) {
    $sqlCommand .= "AND attorney_locations.location_id IN ($AttorneyLocations) ";
}
if ($AttorneyCategories) {
    $sqlCommand .= "AND attorney_categories.category_id IN ($AttorneyCategories)";
}

【讨论】:

  • 使用新的表格设置,搜索位置和/或类别可以完美运行,谢谢。但是,当我同时搜索位置和类别时,它会失败。我猜我需要一个 if ($AttorneyLocations && $AttorneyCategories) 语句,但我不知道如何编写 sql 命令来查询我的数据库。
  • 在按类别和位置搜索时打印查询输出会得到什么?它昨天在我的测试数据库上为我工作。
  • 我对您的代码有两个问题,主要是语法。为什么,在您的 sql 命令中,您使用单引号 (') 而在另一个中使用双引号 (")。什么是 Where 1=1 语句。谢谢,
  • 我在最后两个连接到 $sqlCommand 的语句中使用了双引号,因为需要扩展 $AttorneyLocations 和 $AttorneyCategories 变量。如果我使用单引号,它们将是字符串文字 - PHP Manual。使用WHERE 1=1 是一种简单的方法,可以知道以下条件可以与 WHERE 子句进行与运算,而不是使用数组和内爆。
【解决方案2】:

您应该打印出您从implode() 获得的信息:

$AttorneyLocation = implode (" OR ", $AttorneyLocation);

它不是有效的 SQL 语法,因为您需要它来读取类似的内容:

location = 'location1' OR location = 'location2' OR ...

因此,您没有生成有效的 SQL,如果您打印了 SQL,您肯定会看到。

您的Table1 是一场关系灾难。 CategoryNLocationN 列是 SQL 的“代码气味”。目前尚不清楚该表的含义;它是否记录了类别和位置之间的“AND”或“OR”连接,Category5 仅与 Location5 相关联还是与 Location1 相关?在不知道数据的含义的情况下,我们无法可靠地提供正确的设计。


鉴于每位律师最多可以执业 5 类法律,并且最多可以在 5 个地点执业,那么表格的更好设计可能是:

Attorney
ID           (pkey)
Name
...other details...

AttorneyCategory
AttorneyID   (fkey - references Attorney)
Category     (fkey - references Category)
(AttorneyID, Category) (pkey)

AttorneyLocation 
AttorneyID   (fkey - references Attorney)
Location     (fkey - references Location)
(AttorneyID, Category) (pkey)

这实际上也将更容易查询,因为您不必在五个不同列中的每一列中查找相同的位置名称,也不必在五个不同列中的每一列中查找相同的类别。

【讨论】:

  • 每个类别都可以与任何位置相关联。一名律师可以在 5 个不同的地点从事 5 种不同类型的法律。
  • 我已经更改了我的 SQL 命令以包含“IN”方法,并从我的 implode 中删除了 OR,只插入了一个逗号。方案 1 和 2 现在工作正常。但我仍在处理场景 3 - 你如何建议我“清理”我的表以使其工作并看起来更好?
  • 谢谢,我没有意识到有一个多对多的关系。
【解决方案3】:

好吧,既然您使用的是 PHP,我只建议您为所需的每个过滤器执行一个 IF 语句,也许几个以使 2 或 3 个过滤器一起工作。您的数据库设置正确,只需使用不同的查询创建 PHP,它应该可以工作。

【讨论】:

  • 你能给我一个使用 IF 语句使多个过滤器一起工作的例子吗?
  • 嗯,它应该是这样的: if(statment_one&&statment_two){ //这里是你的查询,两个过滤器应用 }
  • 无论怎么想,数据库设计都不正确。这明显违反了 1NF(第一范式或最小范式)。我不是规范化狂热者,但这是一个非常糟糕的设计。
猜你喜欢
  • 2016-11-07
  • 1970-01-01
  • 1970-01-01
  • 2017-06-17
  • 1970-01-01
  • 1970-01-01
  • 2011-09-06
  • 2019-07-12
  • 2012-09-13
相关资源
最近更新 更多