Search code examples
sqljoinsql-likeconcatenation

Searching in SQL per POST-Variable in joined Table


I tried to solve this for some time and didn't come to a solution.

I read from a table filled with Mobile Devices and list them and give the user the possibilty to search the list per Movile Device Name and Username.

This is my Code:

        SELECT
        *
    FROM
        tbl_mobdev
    LEFT JOIN
        tbl_mobdev_type ON mobdev_type_id = mobdev_type
    LEFT JOIN
        tbl_marke ON marke_id = mobdev_type_marke
    LEFT JOIN
        tbl_user ON tbl_user.id = mobdev_user
    WHERE
        CONCAT(tbl_marke.marke_name,' ',tbl_mobdev_type.mobdev_type_bezeichnung) LIKE '%".$_POST['marke_name']."%'
    AND
        CONCAT(tbl_user.name,' ',tbl_user.vorname) LIKE '%".$_POST['user']."%'
    AND
        mobdev_aktiv = '1'
    ORDER BY
        ".$_GET['sort']." ".$_GET['sort2']."

Everything works as intended as long as tbl_mobdev.mobdev_user contains an User-ID, while table-rows that doesn't contain an User-ID are left out.

Any Solutions for this?

Edit: The solution of putting the "filter" in the LEFT JOIN for tbl_user doesn't work. An SQL like the following results in every row will be shown. If the LIKE contains something only the not matching results will not be joined, the row doesn't get filtered.

SELECT mobdev_id, mobdev_type_bezeichnung, marke_name, tbl_user.name AS  user_name, tbl_user.vorname AS user_vorname
FROM tbl_mobdev
LEFT JOIN tbl_mobdev_type ON mobdev_type_id = mobdev_type
LEFT JOIN tbl_marke ON marke_id = mobdev_type_marke
LEFT JOIN tbl_user ON tbl_user.id = mobdev_user
AND CONCAT( tbl_user.name, ' ', tbl_user.vorname ) LIKE '%%'
WHERE CONCAT( tbl_marke.marke_name, ' ', tbl_mobdev_type.mobdev_type_bezeichnung ) LIKE '%%'
AND mobdev_aktiv = '1'
ORDER BY marke_name ASC 
LIMIT 0 , 30

Edit2: Setting the SQL to the following didn't changed the behavior.

SELECT mobdev_id, mobdev_type_bezeichnung, marke_name, tbl_user.name AS user_name, tbl_user.vorname AS user_vorname
FROM tbl_mobdev
LEFT JOIN tbl_mobdev_type ON mobdev_type_id = mobdev_type
LEFT JOIN tbl_marke ON marke_id = mobdev_type_marke
LEFT JOIN tbl_user ON tbl_user.id = mobdev_user
AND CONCAT( tbl_user.name, ' ', tbl_user.vorname ) LIKE '%G%'
WHERE CONCAT( tbl_marke.marke_name, ' ', COALESCE( tbl_mobdev_type.mobdev_type_bezeichnung, '' ) ) LIKE '%%'
AND mobdev_aktiv = '1'
ORDER BY marke_name ASC 
LIMIT 0 , 30

Rows without matching $_POST['user'] still aren't filtered out.

I uploaded an example picture of the behavior with blurred out names, the left picute is a Select without any filter, right pictura is a Select with an actual Username (Blurred out, but its the exact same name as in mobdev_id '3'.

http://fs5.directupload.net/images/160404/tmdbyzws.png

As you can see, the other five rows are still there, just without the joins. I want them to be gone completely, because they are not matching the filter.

Edit3: Well, i made it now...sadly in a pretty dirty way using PHP. I just leave the code here, if someone comes up with a better solution for this please let me know.

$query = "
    SELECT
        mobdev_id,
        mobdev_type_bezeichnung,
        marke_name,
        tbl_user.name AS user_name,
        tbl_user.vorname AS user_vorname
    FROM
        tbl_mobdev
    LEFT JOIN
        tbl_mobdev_type ON mobdev_type_id = mobdev_type
    LEFT JOIN
        tbl_marke ON marke_id = mobdev_type_marke
    LEFT JOIN
        tbl_user ON tbl_user.id = mobdev_user
    WHERE
        CONCAT(tbl_marke.marke_name,' ',tbl_mobdev_type.mobdev_type_bezeichnung) LIKE '%".$_POST['marke_name']."%'
        ";
if($_POST['user'])
{
    $query .= "
        AND
            CONCAT(tbl_user.name,' ',tbl_user.vorname) LIKE '%".$_POST['user']."%'
        ";
}
$query .= "
    AND
        mobdev_aktiv = '1'
    ORDER BY
        ".$_GET['sort']." ".$_GET['sort2']."
    ";

Solution

  • The problem is in the line

        CONCAT(tbl_user.name,' ',tbl_user.vorname) LIKE '%".$_POST['user']."%'
    

    Your LEFT JOIN on tbl_user will allow the main query to return independent of whether there's a value in that row. However, that particular WHERE clause line effectively overrides that; if tbl_user.name or tbl_user.vorname is null, the result will always be false.

    If I need to do something like that, I'll often put the test into the ON clause of the JOIN statement. That way it filters the JOINed table where it exists, but doesn't cause problems where it doesn't.

    Example query text that should ensure tbl_user gets filtered when required but not when nothing's specified -

    SELECT
        md.mobdev_id, 
        mdt.mobdev_type_bezeichnung, 
        m.marke_name, 
        u.name AS  user_name, 
        u.vorname AS user_vorname
    FROM
        tbl_mobdev md
            LEFT JOIN tbl_mobdev_type mdt
                ON mdt.mobdev_type_id = md.mobdev_type
            LEFT JOIN tbl_marke m
                ON m.marke_id = mdt.mobdev_type_marke
            LEFT JOIN tbl_user u
                ON u.id = md.mobdev_user
                AND CONCAT(u.name,' ',u.vorname) LIKE '%".$_POST['user']."%'
    WHERE
        CONCAT(m.marke_name,' ',COALESCE(mdt.mobdev_type_bezeichnung,'')) 
                LIKE '%".$_POST['m.marke_name']."%'
        AND md.mobdev_aktiv = '1'
    ORDER BY
        ".$_GET['sort']." ".$_GET['sort2']."
    

    Two asides -

    • As Rene M. says above, this code is an SQL Injection vulnerability. It might be safe due to its current application but I still wouldn't roll it out; the chances of someone taking this application or code fragment and later moving it online without doing a full audit is not insignificant, and you've then got a problem. Practice doing it right - don't leave SQL Injection vulnerabilities in code.
    • Please alias your table names! The query having every table name written out in full for each field reference makes it very bulky.