I'm sure my inability to solve this problem steams from a lack of knowledge of some aspect of php but I've been trying to solve it for a month now with no luck. Here is a simplified version of the problem.
In my database I have a members table, a childrens table (the children of each member), and a friend requests table (this contains the friend requests children send to each other).
What I'm attempting to do is display the children of a particular parent using the following while loop....
$query = "SELECT * From children " . <br>
"WHERE parent_member_id = $member_id"; <br>
$result = mysql_query($query) <br>
or die(mysql_error());<br>
$num_children = mysql_num_rows($result);<br>
echo $num_children;<br>
while($row = mysql_fetch_array($result)){<br>
$first_name = $row['first_name'];<br>
$child_id = $row['child_id'];<br>
<div>echo $first_name<br>
}
This while loop works perfectly and displays something like this...
1) Kenneth
2) Larry
What I'm attempting to do though is also display the number of friend requests each child has next to their name...like this
Kenneth (2)
Larry (5)
To do this I attempted the following modification to my original while loop...
$query = "SELECT * From children " .<br>
"WHERE parent_member_id = $member_id";<br>
$result = mysql_query($query) <br>
or die(mysql_error());<br>
$num_movies = mysql_num_rows($result);<br>
echo $num_movies;<br>
while($row = mysql_fetch_array($result)){<br>
$first_name = $row['first_name'];<br>
$child_id = $row['child_id'];<br>
echo $first_name; include('counting_friend_requests.php') ;
}
In this version the included script looks like this...
$query = "SELECT <br>children.age,children.child_id,children.functioning_level,children.gender,children.parent_member_id,children.photo, children.first_name,friend_requests.request_id " .
"FROM children, friend_requests " .
"WHERE children.child_id = friend_requests.friend_two " .
"AND friend_requests.friend_one = $child_id"; <br>
$result = mysql_query($query)<br>
or die(mysql_error());<br>
$count = mysql_num_rows($result);<br>
if ($count==0)<br>
{<br>
$color = "";<br>
}<br>
else<br>
{<br>
$color = "red";<br>
}<br>
echo span style='color:$color' ;<br>
echo $count;<br>
echo /span;<br>
Again this while loop begins to work but the included file causes the loop to stop after the first record is returned and produces the following output...
Kenneth (2)
So my question is, is there a way to display my desired results without interrupting
the while loop? I'd appreciate it if anyone could even point me in the right direction!!
Avoid performing sub queries in code like the plague, because it will drag your database engine down as the number of records increase; think <members> + 1 queries.
You can create the query like so to directly get the result you need (untested):
SELECT child_id, first_name, COUNT(friend_two) AS nr_of_requests
From children
LEFT JOIN friend_requests ON friend_one = child_id OR friend_two = child_id
WHERE parent_member_id = $member_id
GROUP BY child_id, first_name;
It joins the children table records with friend_requests based on either friend column; it then groups based on the child_id to make the count() work.
You don't need to include the php file everytime you loop. Try creating a Person class that has a method getFriendRequestCount(). This method can all the database. This also means you can create methods like getGriendRequests() which could return an array of the friend requests, names etc. Then you could use count($myPerson->getFriendRequests()) to get the number. Thousands of options!
A great place to start, http://php.net/manual/en/language.oop5.php
Another example of a simple class, http://edrackham.com/php/php-class-tutorial/
Eg.
include ('class.Person.php');
while(loop through members)
$p = new Person(member_id)
echo $p->getName()
echo $p->getFriendRequestCount()
foreach($p->getFriendRequests as $fr)
echo $fr['Name']
In your Person class you want to have a constructor that grabs the member from the database and saves it into a private variable. That variable can then be accessed by your functions to proform SQL queries on that member.
Just to clarify whats happening here.
"include" processing is done when the script is parsed. Essentially its just copying the text from the include file into the current file. After this is done the logic is then parsed.
You should keep any include statements separate from you main logic. In most cases the "include"d code will contain definitions for one or more functions. You can then call these functions from the main body of your program at the appropriate place.
Related
I am trying to design an application and part of it is to show users new articles in different categories after the last visit of the user to the webapp. To this I use MySql and have a table that keeps track of last visits and I can query the table to get a php array like below:
$array =[[user1,category1, datetime1],[user1,category2, datetime2],[user1,category3,datetime3]];
Where user is the user id and datetime is the visited datetime and category is the article category.
Having the setup above, I am trying to get new articles from the article table where the publish date is after user last visited to categories.
I can achieve this by multiple OR in a query like below, however it is not really a good and nice looking query, and probable not scalable. Is there any other way of doing this which is simpler and faster?
$multiwhere=[];
foreach($array as $a){
$multiwhere[]="select article_id from articles where category=".$a[1]." and publish_date>".$a[2];
}
And the final query would be like this:
"Select * from articles where article_id in (".implode(" or ".$multiwhere.")";
I deeply appreciate any suggestion to improve the query above.
Your query is almost correct, apart from the fact that you first retrieve all the article_id you want, and then use them to query for those articles. You can do that in one step, like so:
$multiwhere = [];
foreach ($array as $a) {
$multiwhere[] = "(category = " . $a[1] . " AND publish_date >= " . $a[2] .")";
}
$query = "SELECT * FROM articles";
if (count($multiwhere) > 0) {
$query = " WHERE " . implode(" OR ", $multiwhere);
}
One query will do.
I kept the way you use the $array, but it looks weird to me. Especially around publish_date. I cannot change that because I don't know the type of the field. And, of course, $array is quite a bad name. It tells you what the type of the variable is, not what it contains, as it should. A better name would be: $lastCategoryVisits, or something like that. Your loop should look something like this:
foreach ($lastCategoryVisits as $lastCategoryVisit) {
$category = $lastCategoryVisit["category"];
$lastVisit = $lastCategoryVisit["lastVisit"];
$QueryConditions[] = "(category = '$category' AND publish_date >= '$lastVisit')";
}
Don't be afraid to write out what your code actually does. It might be a bit longer, but now you can see what is going on. This will not slow down the execution of your code at all.
Finally, it would be better to always use prepared statements to prevent the possibility of SQL-injection. If you get into the habit of always doing this you don't have to use excuses like: "It is not important in this project.", "I'll to it later when the code works." or "The data for this query doesn't come from an user.".
I've been writing some code that essentially collects information based on schools and the user search input. Once the information is pulled up, I also query a database containing users to show how many are signed up at each school, and then another database containing files showing how many files have been uploaded from each school.
I imagine this would require a three tiered loop? If I query the school database and then the student database in succession it works great (Every school will have the appropriate number of students signed up displayed). However the problem is with the files. If I add in the file query, it will only show the first two results of the schools.
This leads me to believe that the file database query isn't correct and after testing a two tiered loop (this time will files instead of students) it appears to be the case. So, what am I doing wrong with the file database code? I copied it directly from the student database code so I haven't a clue why this one won't work. Here is the code that works:
mysql_select_db($database_geographic, $geographic);
$query_school = "SELECT * FROM geographic.school WHERE countryid='$countryid' AND stateid='$stateid' ORDER BY school_name ASC";
$school = mysql_query($query_school, $geographic) or die(mysql_error());
$totalRows_schools = mysql_num_rows($schools);
while ($row_school = mysql_fetch_assoc($school)) {
echo $row_school['school_name'];
echo $row_school['city_name'];
echo $row_school['state_name'];
echo $row_school['schoolid'];
$schoolid = $row_school['schoolid'];
mysql_select_db($database_user_information, $user_information);
$query_users = "SELECT COUNT(*) AS studentcount FROM users WHERE school_name= '$schoolid'";
$users = mysql_query($query_users, $user_information) or die(mysql_error());
while ($row_users = mysql_fetch_assoc($users)) {
echo $row_users['studentcount']; }
But if I throw in this third files loop statement it will not work.
mysql_select_db($database_files, $files);
$query_files = "SELECT COUNT(*) AS filecount FROM file_data WHERE school_id= '$schoolid'";
$files = mysql_query($query_files, $files) or die(mysql_error());
while ($row_files = mysql_fetch_assoc($files)) {
echo $row_files['filecount']; }
}
If I use the file query in place of the student query it will not work either. The problem must be with the file query but I can't figure it out. Any help would be awesome! Thanks!
I have a question on how to go about the next phase of a project I am working on.
Phase I:
create a php script that scraped directory for all .txt file..
Open/parse each line, explode into array...
Loop through array picking out pieces of data that were needed and INSERTING everything into the database (120+ .txt files & 100k records inserted)..
this leads me to my next step,
Phase II:
I need to take a 'list' of several 10's of thousand of numbers..
loop through each one, using that piece of data (number) as the search term to QUERY the database.. if a match is found I need to grab a piece of data in a different column of the same record/row..
General thoughts/steps I plan to take
scrape directory to find 'source' text file.
open/parse 'source file'.... line by line...
explode each line by its delimiting character.. and grab the 'target search number'
dump each number into a 'master list' array...
loop through my 'master list' array.. using each number in my search (SELECT) statement..
if a match is found, grab a piece of data in another column in the matching/returned row (record)...
output this data.. either to screen or .txt file (havent decided on that step yet,..most likely text file through each returned number on a new line)
Specifics:
I am not sure how to go about doing a 'multiple' search/select statement like this?
How can I do multiple SELECT statements each with a unique search term? and also collect the returned column data?
is the DB fast enough to return the matching value/data in a loop like this? Do I need to wait/pause/delay somehow for the return data before iterating through the loop again?
thanks!
current function I am using/trying:
this is where I am currently:
$harNumArray2 = implode(',', $harNumArray);
//$harNumArray2 = '"' . implode('","', $harNumArray) . '"';
$query = "SELECT guar_nu FROM placements WHERE har_id IN ($harNumArray2)";
echo $query;
$match = mysql_query($query);
//$match = mysql_query('"' . $query . '"');
$results = $match;
echo("<BR><BR>");
print_r($results);
I get these outputs respectively:
Array ( [0] => sample_source.txt )
Total FILES TO GRAB HAR ID's FROM: 1
TOAL HARS FOUND IN ALL FILES: 5
SELECT guar_nu FROM placements WHERE har_id IN ("108383442","106620416","109570835","109700427","100022236")
&
Array ( [0] => sample_source.txt )
Total FILES TO GRAB HAR ID's FROM: 1
TOAL HARS FOUND IN ALL FILES: 5
SELECT guar_nu FROM placements WHERE har_id IN (108383442,106620416,109570835,109700427,100022236)
Where do I stick this to actually execute it now?
thanks!
update:
this code seems to be working 'ok'.. but I dont understand on how to handle the retirned data correctly.. I seem to only be outputting (printing) the last variable/rows data..instead of the entire list..
$harNumArray2 = implode(',', $harNumArray);
//$harNumArray2 = '"' . implode('","', $harNumArray) . '"';
//$query = "'SELECT guar_num FROM placements WHERE har_id IN ($harNumArray2)'";
$result = mysql_query("SELECT har_id, guar_num FROM placements WHERE har_id IN (" . $harNumArray2 . ")")
//$result = mysql_query("SELECT har_id, guar_num FROM placements WHERE har_id IN (0108383442,0106620416)")
or die(mysql_error());
// store the record of the "example" table into $row
$row = mysql_fetch_array($result);
$numRows = mysql_num_rows($result);
/*
while($row = #mysql_fetch_assoc($result) ){
// do something
echo("something <BR>");
}
*/
// Print out the contents of the entry
echo("TOTAL ROWS RETURNED : " . $numRows . "<BR>");
echo "HAR ID: ".$row['har_id'];
echo " GUAR ID: ".$row['guar_num'];
How do I handle this returned data properly?
thanks!
I don't know if this answers your question but I think you're asking about sub-queries. They're pretty straightforward and just look something like this
SELECT * FROM tbl1 WHERE id = (SELECT num FROM tbl2 WHERE id = 1);
That will only work if there is one unique value to that second subquery. If it returns multiple rows it will return a parse error. If you have to select multiple rows research JOIN statements. This can get you started
http://www.w3schools.com/sql/sql_join.asp
I am not sure how to go about doing a 'multiple' search/select statement like this?
With regards to a multiple select, (and I'll assume that you're using MySQL) you can perform that simply with the "IN" keyword:
for example:
SELECT *
FROM YOUR_TABLE
WHERE COLUMN_NAME IN (LIST, OF, SEARCH, VALUES, SEPARATED, BY COMMAS)
EDIT: following your updated code in the question.
just a point before we go on... you should try to avoid the mysql_ functions in PHP for new code, as they are about to be deprecated. Think about using the generic PHP DB handler PDO or the newer mysqli_ functions. More help on choosing the "right" API for you is here.
How do I handle this returned data properly?
For handling more than one row of data (which you are), you should use a loop. Something like the following should do it (and my example will use the mysqli_ functions - which are probably a little more similar to the API you've been using):
$mysqli = mysqli_connect("localhost", "user", "pass");
mysqli_select_db($mysqli, "YOUR_DB");
// make a comma separated list of the $ids.
$ids = join(", ", $id_list);
// note: you need to pass the db connection to many of these methods with the mysqli_ API
$results = mysqli_query($mysqli, "SELECT har_id, guar_num FROM placements WHERE har_id IN ($ids)");
$num_rows = mysqli_num_rows($results);
while ($row = mysqli_fetch_assoc($results)) {
echo "HAR_ID: ". $row["har_id"]. "\tGUAR_NUM: " . $row["guar_num"] . "\n";
}
Please be aware that this is very basic (and untested!) code, just to show the bare minimum of the steps. :)
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is it possible to query a tree structure table in MySQL in a single query, to any depth?
I have an admin area I created that pulls data from the mysql database using php and display the results in a table. Basically it shows a parent category, then the first sub category below it, then the third level sub category/subject.
It works perfectly but as I am new to mysql and php I am sure that it the code needs to be improved in order to save db resources as while building the table I use 3 while loops and in each loop make a mysql query which I am sure is the wrong way to do it.
Can somebody offer me some assistance for the best way of doing this?
Here is the code:
$query = mysql_query("SELECT * FROM categories WHERE
parent_id is null
order by cat_id asc;", $hd)
or die ("Unable to run query");
while ($row = mysql_fetch_assoc($query)) {
echo '<tr style="font-weight:bold;color:green;"><td>'. $row ['cat_id'].'</td><td>'.$row['cat_name'].'</td><td>'.$row ['parent_id'].'</td><td>'.$row['active'].'</td><td>'.$row ['url'].'</td><td>'.$row['date_updated'].'</td></tr>' ;
$query2 = mysql_query("SELECT * FROM categories WHERE
(active = 'true' AND parent_id = ".$row ['cat_id'].")
order by cat_id asc;", $hd)
or die ("Unable to run query");
while ($row2 = mysql_fetch_assoc($query2)) {
echo '<tr style="font-weight:bold;"><td>'. $row2['cat_id'].'</td><td>'.$row2 ['cat_name'].'</td><td>'.$row2['parent_id'].'</td><td>'.$row2 ['active'].'</td><td>'.$row2['url'].'</td><td>'.$row2 ['date_updated'].'</td></tr>' ;
$query3 = mysql_query("SELECT * FROM categories WHERE
(active = 'true' AND parent_id = ".$row2 ['cat_id'].")
order by cat_id asc;", $hd)
or die ("Unable to run query");
while ($row3 = mysql_fetch_assoc($query3)) {
echo '<tr><td>'. $row3['cat_id'].'</td><td>'.$row3['cat_name'].'</td><td>'.$row3 ['parent_id'].'</td><td>'.$row3['active'].'</td><td>'.$row3 ['url'].'</td><td>'.$row3['date_updated'].'</td></tr>' ;
}
}
}
EDIT
Ok so I did a bit of research and this is where I am:
Probably for a small database my approach is fine.
For a bigger database using an array to store the data would probably mean I need to use a recursive approach which might use up too much memory. Would love to hear what people think, would it still be better than looping db queries in the nested while loops?
I found the following thread where there is an answer to do this without reccursion and with only one query. Not sure if I need to add a position column to my current design:
How to build unlimited level of menu through PHP and mysql
If I rebuild the design using the nested sets model instead of adjacency model then the mysql query would return the results in the required order however maintaining the nested sets design is above my head and I think would be overkill.
That's it. If anyone has any input on top of that please add to the conversation. There must be a winning approach as this kind of requirement must be needed for loads of web applications.
I would think you could do something like this:
SELECT * FROM categories
WHERE active = 'true'
ORDER BY parent_id, cat_id
This would give you all your categories ordered by parent_id, then by cat_id. You would then take the result set and build a multi-dimensional array from it. You could then loop through this array much as you currently do in order to output the categories.
While this is better from a DB access standpoint, it would also consume more memory as you need to keep this larger array in memory. So it really is a trade-off that you need to consider.
There is a lot to fix there, but I'll just address your question about reducing queries. I suggest getting rid of the WHERE clauses all together and use if statements within the while loop. Use external variables to hold all the results that match a particular condition, then echo them all at once after the loop. Something like this (I put a bunch of your stuff in variables for brevity)
//before loop
$firstInfoSet = '';
$secondInfoSet = '';
$thirdInfoSet = '';
//in while loop
if($parentID == NULL)
{
$firstInfoSet.= $yourFirstLineOfHtml;
}
if($active && $parentID == $catID) // good for query 2 and 3 as they are identical
{
$secondInfoSet.= $yourSecondLineOfHtml;
$thirdInfoSet.= $yourThirdLineOfHtml;
}
//after loop
echo $firstInfoSet . $secondInfoSet . $thirdInfoSet;
You can now make whatever kinds of groupings you want, easily modify them if need be, and put the results wherever you want.
--EDIT--
After better understanding the question...
$query = mysql_query("SELECT * FROM categories order by cat_id asc;", $hd);
$while ($row = mysql_fetch_assoc($query)){
if($row['parent_id'] == NULL){
//echo out your desired html from your first query
}
if($row['active'] && $row['parent_id']== $row['cat_id']){
//echo out your desired html from your 2nd and 3rd queries
}
}
I have a problem when trying to populate an array in php. It seems that once I enter a while loop with a mysql_fetch_assoc method I cannot populate my array. I've included the code below.
$params = $_REQUEST['params'];
$arr["status"]="ok";
$projects=array();
$files=array();
$titles=array();
$query = 'SELECT p.id as pid, f.fname as name, f.title FROM proj p INNER JOIN pic f ON f.projid=p.id WHERE p.catid=\'' . $params['category'] . '\' ORDER BY p.ordr, f.ordr';
require("../php/connect.php");
//select all projects from chosen category and pics from selected projects
$proj_result = mysql_query($query) or die ("Select failed");
//populate from rows
while($row = mysql_fetch_assoc($proj_result)){
$projects[]=$row["pid"];
$files[]=$row["name"];
$titles[]=$row["title"];
}
$arr["projects"]=$projects;
$arr["files"]=$files;
$arr["titles"]=$titles;
echo json_encode($arr);
The result: {"status":"ok","projects":[],"files":[],"titles":[]}
Thank You.
A while loop doesn't create a new scope, as you can see here: http://codepad.org/H1U3wXZD
About the code itself, here's a few suggestions:
0) I would consider having a database abstraction layer (PDO would be good enough).
1) Learn how to use JOIN's. It looks like you could fetch all the necessary information with a single query, something like:
SELECT p.id, p.proj, c.id, c.fname, c.title
FROM proj p
INNER JOIN pic c ON c.projid=p.id
WHERE catid='<your category>'
ORDER BY p.ordr, c.ordr
2) You should separate the code that gets data from the db from the code that constructs the HTML (?). Perhaps you could put that in another method. Something like:
if ($cmd == 'catSelect') {
$data = getData($params['category']);
foreach ($data as $value) {
// process data here
}
}
3) I take it you are using the generated JSON to send it via AJAX to a client. In that case, I would totally cut the fat (eg: generated markup) and send only essentials (picture id, title, fname and whatever else is essential) and generate the code on the client side. This will make your page load faster and save you and your visitors bandwidth.
my jquery/ajax client side script was not sending in category properly and therefore was no selecting any rows.
The above code will work.
Within the loop try something like this :
while($row = mysql_fetch_assoc($proj_result)){
$projects[]=$row["pid"];
$files[]=$row["name"];
$titles[]=$row["title"];
echo $row["pid"]." -- ".$row["name"]." -- ".$row["title"]."\n";
}
Do you get anything? Once you have tried it we will take it from there. My guess is that you aren't getting any data from MySQL.