I am stuck while attempting to retrieve data via PHP from a MySQL database when there are multiple keywords in the SQL statement. My expertise is mostly in the frontend so its been quite a struggle to get this going.
I have a screen where i want to show several skills(these are keywords), all retrieved from a single table in the DB. When a user selects on one or more skills and clicks on a link, i want to return the mapped projects for the selected skills, all via the right backend querying(ie another option i had was to return all data to the frontend and use JS to filter as desired, but somehow i don't currently find this optimal for my current scenario)
I have a MySQL table called web_projects
which lists several projects with columns like project_id
, project_name
etc etc
Another table called web_skills
lists several skills with columns like skill_id
, skill_name
etc etc
Since there is a many-to-many relationship (each project can have several skills and each skill can be included in several projects) I have a third table to manage this relationship. This table web_project_skills
has 3 columns id
, project_id
, skill_id
.
The visual view, with the mapping:
[please don't mind the data types, its just a quick table created and will be modified later]
I post data from my application via:
http://localhost/am/get.php?q=getprojectsforskill&skill=HTML5
and retrieve the string:
$q = trim($_GET["skill"]);
The following SQL(it is a researched effort) helps me get values for this request and it works fine:
$st = "SELECT web_projects.*
FROM web_projects
JOIN web_project_skills
ON web_projects.project_id=web_project_skills.project_id
WHERE web_project_skills.skill_id =
(SELECT web_skills.skill_id
FROM web_skills
WHERE web_skills.skill_name = '".$q."'
)";
Now my post method has changed and I get a space separated string, like:
http://localhost/am/get.php?q=getprojectsforskill&skill=HTML5%2CCSS3
My query now includes:
$q = trim($_GET["skill"]);
$pieces = explode(",", $q);
$subQuery = "SELECT web_skills.skill_id
FROM web_skills
WHERE web_skills.skill_name = '$pieces[0]'";
if(count($pieces)>1){
for($i=1;$i<count($pieces);$i++){
$subQuery.=" OR web_skills.skill_name = '$pieces[$i]' ";
}
}
$st = "SELECT web_projects.*
FROM web_projects
JOIN web_project_skills
ON web_projects.project_id=web_project_skills.project_id
WHERE web_project_skills.skill_id = ($subQuery)";
For a request with a single value it works fine since there is no addition to the SQL statement via the for
loop.
But for request with more than one i get errors. Example URL(same as above):
http://localhost/am/get.php?q=getprojectsforskill&skill=HTML5%2CCSS3
The errors I get with the 3 statement changes I attempted in the subQuery variable:
$subQuery.=" OR web_skills.skill_name = '$pieces[$i]' ";
Above statement returns an error: An Error occured! SQLSTATE[21000]: Cardinality violation: 1242 Subquery returns more than 1 row
An Error occured! SQLSTATE[21000]: Cardinality violation: 1242 Subquery returns more than 1 row
$subQuery.=" UNION WHERE web_skills.skill_name='$pieces[$i]' ";
Above statement returns an error: An Error occured! SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE web_skills.skill_name='CSS3' )' at line 7
An Error occured! SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE web_skills.skill_name='CSS3' )' at line 7
$subQuery.=" AND web_skills.skill_name = '$pieces[$i]' ";
Above statement returns an empty array
You don't need a sub-query for this, you can simply just add another join.
SELECT web_projects.*
FROM web_projects
JOIN web_project_skills
ON web_projects.project_id=web_project_skills.project_id
JOIN web_skills
ON web_skills.skill_id=web_project_skills.skill_id
WHERE skill_name = '".$q."'
That should return all projects that are associated with a given skill.
Based on the accepted answer by @juakali92, to anybody curious to know how i got this to work for my current scenario:
[For a request like http://localhost/am/get.php?q=getprojectsforskill&skill=HTML5%2CCSS3]
$q = trim($_GET["skill"]);
$pieces = explode(",", $q);
$st= "SELECT DISTINCT web_projects.*
FROM web_projects
JOIN web_project_skills
ON web_projects.project_id=web_project_skills.project_id
JOIN web_skills
ON web_skills.skill_id=web_project_skills.skill_id
WHERE web_skills.skill_name = '$pieces[0]'";
if(count($pieces)>1){
for($i=1;$i<count($pieces);$i++){
$st.="OR skill_name = '".$pieces[$i]."'";
}
}
I had to do a SELECT DISTINCT
since i was getting multiple results from the tables. I guess its because i have the OR
condition and there are multiple matches in the tables. The AND
returns zero results with or without DISTINCT
.
Note: There are security concerns that will need to be addressed separately.
EDIT/UPDATE:
Without the for
:
$q = trim($_GET["skill"]);
$pieces = explode(",", $q);
$pieces1 = "'" . implode("','", $pieces) . "'";
$st= "SELECT DISTINCT web_projects.*
FROM web_projects
JOIN web_project_skills
ON web_projects.project_id=web_project_skills.project_id
JOIN web_skills
ON web_skills.skill_id=web_project_skills.skill_id
WHERE web_skills.skill_name IN ($pieces1)";
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.