PDO advanced search from multiple tables - php

I am trying to create a person search, based on multiple select boxes, radio buttons and dropdowns.
Some of the select boxes are arrays so I need to consider all of the selected options to show the results.
There are different tables for services, languages, about, work experience etc.
But something is wrong with my logic. At the moment, when I am trying the search it goes like this: I select English language from the language dropdow, but there is nobody who speaks English so there are no results which is correct. Then if I select one of the services (let's sat cleaning), then there will be one result because the same person has listed cleaning as their service, but this is wrong because he doesn't speak English.
I am using union all and LIKE in my query, but can somebody tell me why it still showing results? If i am doing something wrong, could you please point me in the right direction?
This is my query and PHP code so far:
if (isset($_POST['userServices']) && !empty($_POST['userServices'])){
$userServices = $_POST['userServices'];
for ($i = 0; $i < count($userServices); $i++) {
$services = $userServices[$i];
}
}
if (isset($_POST['languagesArray']) && !empty($_POST['languagesArray'])){
$languagesArray = $_POST['languagesArray'];
for ($i = 0; $i < count($languagesArray); $i++) {
$languages = $languagesArray[$i];
}
}
$search = $user_home->runQuery("
SELECT nanny_services.user_id, userFirstName
FROM hoidja.nanny_services
JOIN services on nanny_services.service_id = services.service_id
JOIN tbl_users ON tbl_users.user_id = nanny_services.user_id
WHERE (services.service_id LIKE :service_id)
GROUP BY nanny_services.user_id;
UNION ALL
SELECT user_language.user_id, userFirstName
FROM hoidja.user_language
JOIN languages on user_language.user_language_id = languages.user_language_id
JOIN tbl_users ON tbl_users.user_id = user_language.user_id
WHERE (languages.user_language_id LIKE :language_id)
GROUP BY user_language.user_id
");
$search->execute(array(
':service_id' => $services,
':language_id' => $languages,
));
$search_results = $search->fetchAll(PDO::FETCH_ASSOC);
echo json_encode($search_results);

Something like this
SELECT nanny_services.user_id, userFirstName
FROM hoidja.nanny_services
JOIN services on nanny_services.service_id = services.service_id
JOIN tbl_users ON tbl_users.user_id = nanny_services.user_id
WHERE (services.service_id = :service_id)
AND (languages.user_language_id = :language_id)
GROUP BY nanny_services.user_id;
without UNION

Related

Performance, sql heavy join vs multiple small request

I have the following Mysql database structure
[Table - Category1]
[Table Category1 -> Category2 ] (One to N relation)
[Table - Category2]
[Table Category2 -> Item ] (One to N relation)
[Table - Item]
and I want to get everything into an array in PHP with the following structure
$arr[$i]['name'] = 'name of something in category1';
$arr[$i]['data'][$j]['name'] = 'name of something in category2';
$arr[$i]['data'][$j]['data'][$k]['name'] = 'name of something in item';
So basically I don't know if I should use one "heavy" sql request with JOIN like the following one or use an iterative method
The join request
SELECT c1.name as c1name, c2.name as c2name, i.name
FROM category1 c1
LEFT JOIN category1_to_category2 c1tc2 ON c1.id = c1tc2.id_category1
LEFT JOIN category2 c2 ON c1tc2.id_category2 = c2.id
LEFT JOIN category2_to_item c2ti ON c2.id = c2ti.id_category2
LEFT JOIN item i ON c2ti.id_item = i.id
The iterative method
$sql = 'SELECT id, name FROM category1';
$result = $mysqli->query($sql);
$arr = array();
$i = 0;
while ($arr[$i] = $result->fetch_assoc()) {
$join = $mysqli->query('SELECT c2.id, c2.name FROM category2 c2 LEFT JOIN category1_to_category2 c1tc2 ON c2.id = c1tc2.id_category 2 WHERE c1tc2.id_category1 = '.$arr[$i]['id']);
$j = 0;
while ($arr[$i]['data'][$j] = $join->fetch_assoc())
/* same request as above but with items */
$i++;
}
The iterative solution will make around 10 * 20 request which seems a lot to me that's why I would choose the first solution (4 JOIN single request).
However, with the single request solution, my array will look like that
$arr[0]['c1name'];
$arr[0]['c2name'];
$arr[0]['iname'];
And it will require some PHP traitement to obtain the desired array which I require to display in tabs in an HTML page. So my question is, is it better to have one big SQL request with some PHP array manipulation or to have multiple small request without the PHP array manipulation ? I know that in most case, getting all the data from SQL is a better solution but in this case I'm not sure. By the way, my only consideration is the loading time of my web page.
Thanks in advance for your help =).
It is typically better, and your example is no exception, to have the SQL server do as much of the data formatting and iteration as possible as SQL servers are typically more efficient at the task than common programming languages.
Add to this that you are cutting down on query load of the server and you have a very good reason for using complex joins.
The only downside is complex SQL queries can be hard to format and debug, if not already using a 3rd party SQL tool I would recommend getting one.
To go with the answer by Wobbles (that I agree with), I would suggest that you do a single query but you store the last key for each of c1name, c2name and iname. When these change you increment the relevant array subscript and initialise the lower level ones again to build up your array.
Something like this:-
<?php
$sql = "SELECT c1.name AS c1name, c2.name AS c2name, i.name AS iname
FROM category1 c1
LEFT JOIN category1_to_category2 c1tc2 ON c1.id = c1tc2.id_category1
LEFT JOIN category2 c2 ON c1tc2.id_category2 = c2.id
LEFT JOIN category2_to_item c2ti ON c2.id = c2ti.id_category2
LEFT JOIN item i ON c2ti.id_item = i.id"
$result = $mysqli->query($sql);
$arr = array();
$i = 0;
$j = 0;
$k = 0;
$c1name = '';
$c2name = '';
$iname = '';
while ($row = $result->fetch_assoc())
{
switch(true)
{
case $row['c1name'] != $c1name :
$i++;
$j = 0;
$k = 0;
$arr[$i]['name'] = $row['c1name'];
$arr[$i]['data'][$j]['name'] = $row['c2name'];
$arr[$i]['data'][$j]['data'][$k]['name'] = $row['iname'];
break;
case $row['c2name'] != $c2name :
$j++;
$k = 0;
$arr[$i]['data'][$j]['name'] = $row['c2name'];
$arr[$i]['data'][$j]['data'][$k]['name'] = $row['iname'];
break;
default :
$k++;
$arr[$i]['data'][$j]['data'][$k]['name'] = $row['iname'];
break;
}
$c1name = $row['c1name'];
$c2name = $row['c2name'];
$iname = $row['iname'];
}
As an aside there is some code at work that is used to generate a menu. Just 2 levels, and it was originally coded as one query for the first level and then one query for each of the records in the first level to get all the items below it. Not complex (there are only ~16 items in the first level, and on average under 10 items below each of those). I rewrote that to a single joined query. Typical time to generate that menu dropped from 0.25 seconds down to 0.004 seconds. It is easy for the time taken sending queries to the database to rapidly become excessive.

Php for loop and mysql query

I'm running a second database search using mysql inside a for loop but I can't get it show the correct amount of rows:
Original search:
$topicemailsql = "select se.id as id, se.users as users, se.topic as topic, se.body as body, se.postID as postID, DATE_FORMAT(se.sent, '%d.%m.%Y %H:%i:%s' ) as sent, (SELECT u.email from users u where u.users_id in (se.users)) as emails from sentEmail se LEFT OUTER JOIN topics t on (t.ID = se.theader_) where t.ID = '$topicID'";
$topicemailsqlquery = mysql_query($topicemailsql)or die(mysql_error());
$numrows = mysql_num_rows($topicemailsqlquery);
php for loop:
for ($i=0; $i < $numrows ; $i++){
$sqlarray = mysql_fetch_array($topicemailsqlquery);
$users = $sqlarray['users'];
$sqlemail = ('select email from users where users_id in ("'.$users.'")');
//echo $sqlemail;
$emailsqlquery = mysql_query($sqlemail)or die(mysql_error());
$amountofusers = mysql_num_rows($emailsqlquery);
$sqlarrayemail = mysql_fetch_array($emailsqlquery);
echo $amountofusers;
//echo $sqlarrayemail['email'];
for ($a=0; $a < $amountofusers ; $a++){
if($a == 0){
$email = $sqlarrayemail['email'];
}
else if($a < $amountofusers){
$email = $sqlarrayemail['email'].','.$sqlarrayemail['email'];
}
}
}
So based on this the $amountofusers should return more than 1 row but now it always return only one row.
When I echo the $sqlemail it should return 2 rows because it looks like this:
select email from users where users_id in ("4,82") --> this should return 2 rows and a count of 2 but it only returns one row.
Where does it go wrong?
Br,
Toby
I think, it's a very bad idea to store multiple values in one field. Furthermore I wouldn't fire up queries in a loop, if I can avoid it. Better fetch the data in one go and let PHP do the rest. If you try your query i.e. with PHPAdmin you would use
SELECT email
FROM users
WHERE users_id IN (4, 82)
The IN operator needs a comma separated list of arguments. You give one single value
"4,82"
That's a huge difference. MySQL would accept ("4","82") too (other DBMS are not as tolerant) and handle the not needed conversion for you.

Inner/Left join with two different where clauses

i'm in the process of joining two tables together under two different conditions. For primary example, lets say I have the following nested query:
$Query = $DB->prepare("SELECT ID, Name FROM modifications
WHERE TYPE =1 & WFAbility = '0'");
$Query->execute();
$Query->bind_result($Mod_ID,$Mod_Name);
and this query:
$Query= $DB->prepare("SELECT `ModID` from `wfabilities` WHERE `WFID`=?");
$Query->bind_param();
$Query->execute();
$Query->bind_result();
while ($Query->fetch()){ }
Basically, I want to select all the elements where type is equal to one and Ability is equal to 0, this is to be selected from the modifications table.
I further need to select all the IDs from wfabilities, but transform them into the names located in modifications where WFID is equal to the results from another query.
Here is my current semi-working code.
$Get_ID = $DB->prepare("SELECT ID FROM warframes WHERE Name=?");
$Get_ID->bind_param('s',$_GET['Frame']);
$Get_ID->execute();
$Get_ID->bind_result($FrameID);
$Get_ID->fetch();
$Get_ID->close();
echo $FrameID;
$WF_Abilties = $DB->prepare("SELECT ModID FROM `wfabilities` WHERE WFID=?");
$WF_Abilties->bind_param('i',$FrameID);
$WF_Abilties->execute();
$WF_Abilties->bind_result($ModID);
$Mod_IDArr = array();
while ($WF_Abilties->fetch()){
$Mod_IDArr[] = $ModID;
}
print_r($Mod_IDArr);
$Ability_Name = array();
foreach ($Mod_IDArr AS $AbilityMods){
$WF_AbName = $DB->prepare("SELECT `Name` FROM `modifications` WHERE ID=?");
$WF_AbName->bind_param('i',$AbilityMods);
$WF_AbName->execute();
$WF_AbName->bind_result($Mod_Name);
$WF_AbName->fetch();
$Ability_Name[] = $Mod_Name;
}
print_r($Ability_Name);
See below:
SELECT ModID,
ID,
Name
FROM modifications M
LEFT JOIN wfabilities WF
ON WF.ModID = M.ID
WHERE TYPE =1 & WFAbility = '0'
To do this, you need to join your tables, I'm not quite sure what you are trying to do so you might have to give me more info, but here is my guess.
SELECT ID, Name, ModID
FROM modifications
JOIN wfabilities
ON WFID = ID
WHERE TYPE = '1'
AND WFAbility = '0'
In this version I am connecting the tables when WFID is equal if ID. You will have to tell me exactly what is supposed to be hooking to what in your requirements.
To learn more about joins and what they do, check this page out: MySQL Join
Edit:
After looking at your larger structure, I can see that you can do this:
SELECT modifications.Name FROM modifications
JOIN wfabilities on wfabilities.ModID = modifications.ID
JOIN warframes on warframes.ID = wfabilities.WFID
WHERE warframes.Name = 'the name you want'
This query will get you an array of the ability_names from the warframes name.
This is the query:
"SELECT A.ID, A.Name,B.ModID,C.Name
FROM modifications as A
LEFT JOIN wfabilities as B ON A.ID = B.WFID
LEFT JOIN warframes as C ON C.ID = B.WFID
WHERE A.TYPE =1 AND A.WFAbility = '0' AND C.Name = ?"

Combining three queries into one

I have three pretty simple queries that I believe could be accomplished in one query, but I can't figure it out. Here are the queries:
$idSQL = "SELECT website_id FROM websites WHERE website_url = :webURL LIMIT 1
$featureSQL = "SELECT feature_id FROM feature_website WHERE website_id = :webID";
$sql = "SELECT feature_name, feature_start, feature_end, feature_headline, feature_text, feature_photoHor, feature_photoVert, feature_photoSquare FROM features WHERE feature_id = :featID";
This gives me the expected output:
So, I tried to combine the queries (probably using the incorrect Joins) into this:
$sql = "SELECT f.feature_name, f.feature_start, f.feature_end, f.feature_headline, f.feature_text, f.feature_photoHor, f.feature_photoVert, f.feature_photoSquare FROM features LEFT JOIN feature_website fw ON f.feature_id = fw.feature_id LEFT JOIN websites w ON fw.website_id = w.website_id AND w.website_url = :webURL";
What am I missing?
Try this query:
$sql = "
SELECT f.feature_name, f.feature_start, f.feature_end, f.feature_headline, f.feature_text, f.feature_photoHor, f.feature_photoVert, f.feature_photoSquare
FROM
websites w
LEFT JOIN feature_website fw ON fw.website_id = w.website_id
LEFT JOIN features f ON f.feature_id = fw.feature_id
WHERE
w.website_url = :webURL
";
Maybe you could use regular JOIN in case you don't want to show any result if there's no feature for your website.

MySQL use inner join in PHP

I have 2 tables, the unique id's in each table are the same in both tables.
How do I go about joining the data from both tables together in php?
When I normally pull data I do it like this:
$get_board_array = mysql_query("SELECT * FROM posts WHERE user_id_to = '$id' ");
while($posts = mysql_fetch_array($get_board_array))
{
$post_id = $get_post['post_id'];
$html_output .= "<p>".$post_id."</p>";
}
As for pulling the data from separate tables and not getting it all mixed up, I was thinking of doing something like this:
$get_arrayA = mysql_query("SELECT * FROM tableA WHERE age = '36' ");
while($dataA = mysql_fetch_array($get_arrayA))
{
$dataA_id = $dataA['id'];
$dataA_firstName = $dataA['FirstName'];
foreach($dataA_id)
{
$get_arrayB = mysql_query("SELECT * FROM tableB where id='".$dataA_id."'");
while($dataB = mysql_fetch_array($get_arrayB))
{
$dataB_lastName = $dataB['LastName'];
$html_output .= "<p>".$dataA_firstName.$dataB_lastName"</p>";
echo $html_output;
}
}
}
Or would this be just too weird of a thing?
I know how to do it within SQL using inner join but how do I do something like that in PHP and output html?
It is because of you misguide use of wildcard (*) characters. STOP THAT !! This is what happens when arrogant php developers imagine that they do not need to learn SQL. If you need something , then select it.
SELECT
foo.foo_id,
foo.data as paramX ,
bar.type,
bar.something_else
FROM foo
INNER JOIN bar USING (foo_id)
WHERE
foo.state = 'open'
AND bar.type = 3
This (I'm just guessing the schema and your needs) could be a better query for your situation:
SELECT a.id,a.FirstName,b.LastName
FROM tableA a
JOIN tableB b ON b.id = a.id
WHERE a.age = '36'

Categories