简体   繁体   中英

mySQL Search Statement with Multiple filters

I've been trying to figure this out for a few days now, but haven't been able to find a solution. Most likely due to the fact that I don't think I'm asking the right question.

Here is goes...

I am trying to create a search on my website of a list of attorneys.

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

So Attorneys have multiple categories and multiple locations -> one to Many Relationship with both Table 2 and Table 3

First Question: Do I have Table 1 set up correctly? Do I need to have multiple location and category columns (ie location1, location2, location3, etc...) Or am I complicating this?

Next...

I want a checkbox style search on my site. A user can search by Location and/or by Category. And with checkbox they can choose multiple locations and/or categories. The checkbox values are the IDs of the locations and categories (not the names)

So three ways this can happen.

  1. Search by locations ONLY
  2. Search by categories ONLY
  3. Search by categories within locations

I have two problems.

  1. I can get scenarios 1 & 2 to work, but only if ONE checkbox is selected.
  2. I have no idea how to even begin to get scenario 3 to work.

Here is what I have for scenario 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. Basically it seems to only search the LAST checkbox that has been selected, ignoring the ones before it.

For scenario 3 - Again I'm just not sure where to start, how do I join together the category search within the location search?

If someone can point me in the right direction that would be great, thanks so much! This is my first try creating something like this!

Here is my form code if necessary - all created dynamically

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

As pointed out by @JonathanLeffler your structure for Table 1 is a perfect example of what not to do. Your Category* and Location* fields should be removed from Table 1 and replaced with two many-to-many tables - attorney_locations(attorney_id, location_id) and attorney_categories(attorney_id, category_id).

Start by amending your db structure and then we can address the other issues.

Next round -

<?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)";
}

You should print out what you get from your implode() :

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

It won't be valid SQL syntax, because you need it to read something like:

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

So, you are not generating valid SQL, as you would surely see if you printed the SQL.

Your Table1 is a relational disaster. CategoryN or LocationN columns are SQL 'code smells'. It isn't clear what that table means; is it documenting 'AND' or 'OR' connections between the categories and locations, and is Category5 associated only with Location5 or is it also relevant to Location1? Without knowing what the data is supposed to mean, we can't reliably provide the correct design.


Given that each attorney may practice up to 5 categories of law, and may practice in up to 5 locations, then a better design for the tables is probably:

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)

This will actually be significantly easier to query, too, because you won't have to look up the same location name in each of five different columns, nor look up the same category in each of five different columns.

Well, since you are using PHP i just recommend you do an IF statement for each of the filters you want, maybe a few to make 2 or 3 filters work together. your database is set correctly, just create PHP if's with different queries and it should work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM