简体   繁体   中英

Creating a SQL query with OR statements based on whether or not checkboxes have been ticked

I am building a search engine for a school project, meant to allow users to look through a large database of schools, and I have set it up so that on one page, the user would be able to tick certain checkboxes, and that after hitting submit, the user would be returned with a list of schools which correspond to what was ticked.

The user is allowed to search schools nationwide along a number of parameters:

  • School type (high school, middle school, trade school, university, etc...)
  • School status (private or public)
  • City
  • State
  • etc.

For my question, I'll only focus on the part related to school types, as I can later apply the logic with the other form elements which function similarly.

For school types, along with a number of other form elements, I have decided to use checkboxes, so that for instance, if a user wants the search to return both middle schools and high schools, he would only need to tick both the "middle school" and "high school" checkboxes.

So, this implies using OR statements in my search, so that I would, for instance, have a SQL query like this:

SELECT schools.schoolId, schools.schoolName, villages.villageName FROM schools, villages
WHERE schools.villageId = villages.villageId

And here is where I would set up the condition that only the information pertaining to middle schools, high schools, and trade schools be returned by the query:

AND (schools.schoolType = "MiddleSchool" OR schools.schoolType = "HighSchool" OR schools.schoolType = "TradeSchool")

It is the construction of this statement that I am having trouble with.

As it is, because the school types can vary as the database evolves, I cannot hard code my query construction, nor do I wish to.

I use PHP to generate my query based on the form results.

To generate a list of all available school types, I read all the existing school types from the database (MySQL) using a DISTINCT query. I use this to build a list of all the available school types, which I use to generate the search form.

As such, I have to use a similar method to compare the form results with the database, meaning that, on my results page, to see which checkboxes have been ticked, I need to see what checkboxes could have existed at the moment the form was sent, so I once again read all of the available types from the database: (some code cut off, this is just the query I use which I know works, but this is just for context)

try 
{
    $pdo = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
    $req_sql_type_school = "SELECT DISTINCT schoolType AS types FROM schools";
    $res_type_schools = $pdo->query($req_sql_type_school);
    $res_type_schools->setFetchMode(PDO::FETCH_ASSOC);
} catch (PDOException $e) 
{
    die("Could not connect to the database $dbname :" . $e->getMessage());
}

After that, I iterate over the query results to create an array of the school types: (I know this works too, but again, it's just for context)

$arrayTypes = array();
while ($row = $res_type_schools->fetch())
{
    $arrayTypes[]=$row['types'];
}

Then, this is where I struggle. It is at this point that I must create the "AND..." statement to the start of the query which I mentioned above, which was:

SELECT schools.schoolId, schools.schoolName, villages.villageName FROM schools, villages
WHERE schools.villageId = villages.villageId

To see whether or not something was ticked by the user, I iterate over the array to check whether or not the corresponding checkbox was ticked. To do this, here is the code I wrote. Note: basictext() just formats the string to the format I used for my checkbox values, which is lowercase-only strings with no spaces and no accents):

foreach($arrayTypes as $arr)
{
    if(isSet($_GET[basictext($arr)]))
    {
        echo "[x] Type ".$arr." <br/>";
    } 
    else 
    { 
        echo "[ ] Type ".$arr." <br/>"; 
    }
}

This allows me to check which types were checked by the user on the search form.

However, I don't know how to translate this to the AND statement I wanted, which was this one:

AND (schools.schoolType = "MiddleSchool" OR schools.schoolType = "HighSchool" OR schools.schoolType = "TradeSchool")

I had thought of placing each type inside parentheses and separating each type with an OR , and only writing inside the parentheses if the type was indeed checked.

This resulted in something like this:

AND ((schools.schoolType = 'MiddleSchool') OR () OR () OR (schools.schoolType = 'HighSchool') OR () OR () OR ())

Needless to say, it was very ugly, and it also didn't work, so I canned it. The problem is that I don't know how many "checked types" there can be, so, it's a little difficult to build a proper query.

One solution I've thought of while writing this is to place each "valid" type inside an array, and then use that array, whose length I would then know. Then I would be able to create the expected AND statement, because I would then have a definite amount of checked checkboxes, and it would also allow me to completely ignore the non-ticked values. Do you think that would work?

So if I'm understanding you right, you simply need to write something to concatenate fragments of an OR statement based on a bit array. Instead of adding an OR for every entry regardless of if it is checked or not, do something like

    first = true
    string orClause = ""
    array schoolTypes
    array checkedSchoolTypes

    for(i = 0; i < checkedSchoolTypes.length; i++)
        if(first && checkedSchoolTypes[i])
           orClause += "schools.schoolType = " + schoolTypes[i]
           first = false
        else if(checkedSchoolTypes[i])
             orClause += "OR schools.schoolType = " + schoolTypes[i]

   query = "This is your query" + orClause + "rest of your query"

*Note this is a solution for a school project not a production environment. If you were do this in a production environment in that case you would want to pass information through a paramaterized stored procedure and generate your statement in the sql based off of the information passed in from the form.

Instead of doing the or as you are suggesting you could do and IN statement as others are suggesting the code would basically be the same with a different format. Let me know if you need any clarifications.

If your schools.schoolType column is an arbitrary VARCHAR data type, there needs to be some relationship between the data the user submits and the data used in the query, unless you predefine a list of possible values beforehand.

It looks like you have some relationship between the arrayTypes array of yours and the $_GET value. So assuming the query wants HighSchool , this can be detected if the user submits highschool using your basictext() function.

From there I would do something like this:

$selectedTypes = array();

foreach($arrayTypes as $arr)
{
    if(isSet($_GET[basictext($arr)]))
    {
        echo "[x] Type ".$arr." <br/>";
        $selectedTypes[] = $arr;
    } 
    else 
    { 
        echo "[ ] Type ".$arr." <br/>"; 
    }
}

This would populate the selectedTypes array with the matching types of what the user submits.

Using this it sounds like you want to add an IN clause to your final query.

$placeholders = implode(',', array_fill(0, count($selectedTypes), '?'));

$template = "SELECT schools.schoolId, 
                    schools.schoolName, 
                    villages.villageName 
               FROM schools, 
                    villages
              WHERE schools.villageId = villages.villageId
                AND schools.schoolType IN ($placeholders)";

$stmt = $pdo->prepare($template);
$stmt->execute($selectedTypes);
$result = $stmt->fetchAll();
$stmt = null;

If selectedTypes contains ["HighSchool", "MiddleSchool", "TradeSchool"] then $placeholders will contain [?, ?, ?] . This is used to construct an SQL template for a prepared statement query using the selectedTypes array as the input data.

$result ought to have the data you're after.

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