PHP & MySQL Query results are confusing - php

Working on a quick script to display recent activity of a customer in my database - I have the script outputting results but what it's out putting is confusing me.
<?php
//Search for customer recent history
$q = "SELECT * FROM txn_log WHERE customer_no = $customer_no ORDER BY datetime DESC LIMIT 3";
$r = mysql_query($q) or die(mysql_error());
while($row = mySQL_fetch_array($r)) {
$recent_history = '';
$str .= '<a href="#" class="list-group-item">';
$str .= ' <span class="badge">' . gmdate("Y-m-d\TH:i:s\Z", $row['datetime']) . '</span>';
$str .= ' <i class="fa fa-check"></i> ' . $row['txn_id'] . ': ' . $row['txn_type'] . ' ' . $row['amount_dif'];
$str .= '</a>';
echo $str;
}
?>
In my database I have one test customer with three records associated with them, starting at ID 2.
My query above SHOULD be outputting only the three records in order of the UNIX timestamp used,
which should produce the three records in the following order => ID2, ID3, ID4
What it IS doing is outputting the following => ID2, ID2, ID3, ID2, ID3, ID4
I don't understand what I did wrong to get it to produce the first three (ID2, ID2, ID3) in addition to the correct results. Each time I run the query, the results are the same.

You are using $str.=
so on the
first iteration of your loop, the $str being echoed is ID2
second iteration, $str being echoed is ID2 + ID3
third iteration, $str being echoed is ID2 + ID3 + ID4
In the end, it will look like ID2, ID2, ID3, ID2, ID3, ID4
Hope that helps!
To Fix this, try declaring the variable as $str = "" at the start of your loop, and then your echo will only echo each ID once.

Reset $str for each loop iteration.
Add:
$str="";
After $recent_history = '';

You need to use mysql_fetch_assoc, and echo outside the loop
Few things to keep in mind,
security has to be your top priory, check for SQL injection characters and always enclose your variables with single quotes in the query. see below
<?php
//Search for customer recent history
$customer_no = str_replace("'", '', $customer_no);
$q = "SELECT * FROM txn_log WHERE customer_no = '$customer_no' ORDER BY datetime DESC LIMIT 3";
$r = mysql_query($q) or die(mysql_error());
while($row = mySQL_fetch_assoc($r)) {
$recent_history = '';
$str .= '<a href="#" class="list-group-item">';
$str .= ' <span class="badge">' . gmdate("Y-m-d\TH:i:s\Z", $row['datetime']) . '</span>';
$str .= ' <i class="fa fa-check"></i> ' . $row['txn_id'] . ': ' . $row['txn_type'] . ' ' . $row['amount_dif'];
$str .= '</a>';
}
echo $str;
?>

Woops... found it..
Derp in my While()
I was clearing the wrong string!
$recent_history = ''; should have been $str = '';
EDIT:
Thanks all, I didn't see you guys posting until I submitted my correction.
I'm definitely going to be securing the script, I'm just making sure that i can do what I want it to do :)

Related

Why doesn't my query work for retrieving substrings?

Below is my query to show user comments. It works well:
enter
$query='SELECT
customers.cust_id,
customers.f_name,
customers.l_name,
user_comments.comment_txt,
clubs.name_club
from
customers
inner join
user_comments
on customers.cust_id = user_comments.cust_id
inner join
clubs
on user_comments.cust_id = clubs.cust_id
ORDER BY RAND() LIMIT 1,1
';
$result = mysql_query($query);
while($row = mysql_fetch_array($result)){
$nameclub =$row['name_club'];
$comment =$row['comment_txt'];
echo '<div><img src="images/arrow.gif"> Name : '.$nameclub.'</div>';
echo '<div>'.$comment.'</div><br>';
echo '<div>'.$row['f_name'].' '.$row['l_name'].'</div>';
}
echo '<p style="text-align: left"> Show all comments</ ></p> '; ?>
I now want to show just about 100 characters of a user comment. So I changed my query accordingly, but it does not work:
enter $query='SELECT
customers.cust_id,
customers.f_name,
customers.l_name,
user_comments.comment_txt.substr(comment_txt,1,100) as comment_txt,
clubs.name_club
from
customers
inner join
user_comments
on customers.cust_id = user_comments.cust_id
inner join
clubs
on user_comments.cust_id = clubs.cust_id
ORDER BY RAND() LIMIT 1,1
';
$result = mysql_query($query);
while($row = mysql_fetch_array($result)){
$nameclub =$row['name_club'];
$comment =$row['comment_txt'];
echo '<div><img src="images/arrow.gif"> Name : '.$nameclub.'</div>';
echo '<div>'.$comment.'</div><br>';
echo '<div>'.$row['f_name'].' '.$row['l_name'].'</div>';
}
echo '<p style="text-align: left"> Show all comments</ ></p> '; ?>
How can I fix my query to just get 100 characters of a comment?
You're using SUBSTR() incorrectly. Replace this:
user_comments.comment_txt.substr(comment_txt,1,100) as comment_txt,
With this:
SUBSTR(user_comments.comment_txt,1,100) as comment_txt,
Edit per comment: to add ... if the text continues past 100 characters, use
CONCAT(SUBSTR(user_comments.comment_txt,1,100), IF(LEN(user_comments.comment_txt) > 100, '...', '')) as comment_txt,
Try this
Replace this
user_comments.comment_txt.substr(comment_txt,1,100) as comment_txt,
To
SUBSTRING(user_comments.comment_txt,1,100) as comment_txt,
Mysql function_substring
Another solution is to use a php function that cuts a string into a number of words:
<?php
function cut_string($string_to_cut, $number_words, $append = "...") {
$string = array();
$string_return = "";
$return_string = "";
$string = explode(" ", $string_to_cut);
if(count($string) < $number_words) {
for($x=0; $x < count($string); $x++) {
$string_return .= $string[$x] . " ";
}
$return_string = substr($string_return, 0, -1);
}
else {
for($x=0; $x < $number_words; $x++) {
$string_return .= $string[$x] . " ";
}
$string = substr($string_return, 0, -1);
$return_string = $string . $append;
}
return($return_string);
}
?>
Note: This is word count based, not character count based.

Preg_match with different combinations of words

I've a littile question about preg_matches, these regex things are really hard to understand and I hope someone can give the right awnser!
I have the following text:
0A-24-423
But this can also be:
0A-242-2
or
0A-2-423
How can I use preg_matches to filter these? I was using
substr($something, 0,2)
so that it captures 0A and
substr($seat, 4,5)
This will capture 24 but when you get 242 it wont capture the last 2....
Hope someone can help creating this in preg_match!
to make it more clear what I have now:
foreach($_POST['seats'] AS $seat) {
if ($count > 0) {
$selectQuery .= " || ";
}
$selectQuery .= " ( rowId = '" . substr($seat, 0,2) . "'";
$selectQuery .= " and `order` = " . substr($seat, 3,5) . " ";
$selectQuery .= " and columnId = " . substr($seat, 6) . " ) ";
$count++;
and $seat had the following format XXXXXX and using substr I can get the right things (for example: 0J3017)
Something Like this should do it:
$selectQuery = "SELECT * from seats where ";
$count = 0;
$pattern = "I DON'T KNOW :( ";
foreach($_POST['seats'] AS $seat) {
if ($count > 0) {
$selectQuery .= " || ";
}
preg_match($pattern, $seats, $matches);
$selectQuery .= " ( rowId = '" . $matches[0] . "'";
$selectQuery .= " and `order` = " . $matches[1] . " ";
$selectQuery .= " and columnId = " . $matches[2] . " ) ";
$count++;
and $seats is explained in the beginning of the post (it has a format of XX-XXX-XXX
where the first 2 XX are 0[A-Z] (yes the 0 is correct)
where the 3 first XXX are [0-9]
Where the last 3 XXX are [0-9]
EDIT:
there are 2 ways to solve this.
Option 1:
$pattern = "/(.*)-(.*)-(.*)/";
or use explode() function.
It does not look like you need to be using regular expressions. Here's an example using explode() and list():
list($row_id, $order, $column_id) = explode('-', $seat, 3);
You could then use those three new variables in your $selectQuery.
EDIT : Since the OP has stated his requirement as a comment to my answer I have updated my answer accordingly.
You can try this:
$pattern = "/[A-Z\d]{1,2}-[A-Z\d]{1,3}-[A-Z\d]{1,3}/";
$matched = preg_match($pattern, $something);
if ($matched === 0) {
die('regex did not match');
}
$matched will give you 1 for a matched string and 0 if not matched.

order by alphabet PHP, MySql but list the letter before each group

Say I had a table with :
First | Last
-------------------
Bob Dylan
Ashley Scott
Lol Cats
black cake
It would order them but do
A:
Ashley Scott
B:
Bob Dylan
Black Cake
L:
lol cats
I know how to list by abc from MySql. but i want to have it put the letter before each group of names. So i can make a nice looking interface, and at the top of the page have A B C D letters at the top that went contacts#letterL
I know how to do the #links in a url. Its just the div Id. I know how to grab from MySql. The part i'm not sure on is echoing the letter before the group of names that start with that letter.
I was thinking of getting the first letter of the name of saving it in a variable.
if ($lastletter == $currentleter) {
echo name
} else {
$lastletter = $currentleter;
echo '<div id="Letter ' . $currentleter . '" class="letterheader">' . $currentleter . '</div>';
}
Not sure if this idea would be a efficient idea. Would this be the right away of doing this?
Untested code but something like this should work:
$currentleter = substr($name , 0 , 1);
if ($lastletter != $currentleter){
echo '<div id="Letter' . $currentleter . '" class="letterheader">' . $currentleter . '</div>';
$lastletter = $currentleter;
}
echo $name;
You can select the records with order by first name.
$sql = "select * from tblname order by firstname";
$lastletter = "A";
echo $lastLetter;
if ($lastLetter != substr($firstname, 0,1))
echo $lastLetter;
echo $firstname;
This looks like a pretty good solution except IDs must not have spaces in them so you wanna change
<div id="Letter ' . $currentleter . '" class="letterheader"> to
<div id="Letter_' . $currentleter . '" class="letterheader">.

PHP/MySQL news archive

I'm a bit stuck trying to get my code to output correctly, see below. It all works ok, but rather than displaying all news items, it only shows one for each month. What I need to do is group all news for a selected month with the month/year heading for that month. Hope this makes sense.
Any help greatly appreciated.
SS
$theQuery="Select * from isnews WHERE active = '1' GROUP BY YEAR(date) DESC, MONTH(date) ORDER BY YEAR(date) DESC, MONTH(date) DESC";
$newsQuery=mysql_query($theQuery);
if(mysql_num_rows($newsQuery)>0) {
while ($newsResult=mysql_fetch_array($newsQuery)) {
$newDate = $newsResult['date'] ;
echo '<div class="date">' . date('F Y ',strtotime($newDate)) . '</div>';
echo '<ul class="press">';
echo '<li>
<img src="'.$wwwUrl.'images/news/'.$newsResult['image'].'" width="'.$newsResult['tnWidth'].'" height="'.$newsResult['tnHeight'].'" title="'.$newsResult['title'].'" alt="'.$newsResult['title'].'" />
<h3>'.$newsResult["title"].'</h3>
'.substr($newsResult['descrip'],0,100).'
<p>Read more</p>
</li>';
}
echo '</ul>';
} else {
echo 'We currently have no press releases available';
}
There are two problems that I can see. First of all, GROUP BY is an aggregate function, so it is used to combine multiple rows into one row in your result (for instance, if you wanted to see how many news items were written for a given month and year). Secondly, even if you were getting multiple records per time period, you are outputting a date header for every record that you pull from the database (ie. you would get duplicate headers if you have multiple news items from the same month and year).
A better solution would be to collect all your active news items (without the GROUP BY clause), and then build an array which you can then iterate over to output your page:
$query = "SELECT *
FROM isnews
WHERE active = '1'
ORDER BY YEAR(date) DESC, MONTH(date) DESC";
$resultSet = mysql_query($query);
if (mysql_num_rows($resultSet))
{
$newsArray = array();
while ($newsResult = mysql_fetch_array($resultSet))
{
// The time period is what you will output as your header
$timePeriod = intval(date("F Y", $newsResult['date']));
if (!isset($newsArray[$timePeriod]))
{
// Create a subarray if needed
$newsArray[$timePeriod] = array();
}
$newsArray[$timePeriod][] = $newsResult;
}
foreach ($newsArray as $timePeriod => $newsItems)
{
echo '<div class="date">' . $timePeriod . '</div>';
echo '<ul class="press">';
foreach ($newsItems as $item)
{
echo '<li>';
// ... do your news item outputting
echo '</li>';
}
echo '</li>';
echo '</div>';
}
}
else
{
echo 'We currently have no press releases available';
}
Many thanks for the help.
I've tried adapting the code from Daniel a little in order to create a sidebar-type archive list showing years, then the months and their corresponding news results. The end result being an accordion type menu where the years dropdown to show the months, then the months dropdown to display the news items. I can get it to work as far as the years go but can't seem to get the months working properly.
Any pointers/help more than greatly appreciated. (code below)
SS
$query = "SELECT * FROM isnews WHERE active = '1' ORDER BY YEAR(date) DESC, MONTH(date) DESC";
$resultSet = mysql_query($query);
if (mysql_num_rows($resultSet))
{
$newsArray = array();
echo '<ul>' . PHP_EOL;
echo '<li><strong>Press releases:</strong></li>' . PHP_EOL;
while ($newsResult = mysql_fetch_array($resultSet))
{
$newDate = $newsResult['date'] ;
$timePeriod = date('F Y ',strtotime($newDate));
$timePeriodY = date('Y',strtotime($timePeriod));
$timePeriodM = date('F',strtotime($timePeriod));
if (!isset($newsArray[$timePeriodY]))
{
$newsArray[$timePeriodY] = array();
}
$newsArray[$timePeriodY][] = $newsResult;
}
foreach ($newsArray as $timePeriodY => $newsItems)
{
echo '<li><em>' . $timePeriodY . '</em>' . PHP_EOL;
echo '<ul>' . PHP_EOL;
foreach ($newsItems as $item)
{
echo '<li>';
echo ''.$item["title"].'';
echo '</li>' . PHP_EOL;
}
echo '</ul>' . PHP_EOL;
echo '</li>' . PHP_EOL;
}
echo '</ul>' . PHP_EOL;
}
else
{
echo 'We currently have no press releases available';
}
it only shows one for each month
That's correct, your GROUP BY isn't correct. All columns (see the * ) should be in your GROUP BY because all of them are in the SELECT and you don't use any aggregate function. MySQL has very strange behaviour and now only returns the first record it can find.
If you want all records, just drop the entire GROUP BY, and ORDER BY the month and year to get the correct sort order. In youw PHP you can make some groups, but that has nothing to do with SQL.
You might consider ONLY_FULL_GROUP_BY, this helps to prevent strange/false results.

Problems using GROUP BY in MySQL in a query that does a JOIN on two tables

I have two MySQL tables, $database1 and $database2. Both have a field in them called ID. I am passing the name of a town to the file using GET (i.e. it's in the URL of the PHP file that holds this code).
I can run this query...
$PlaceName = $_GET['townName'];
$PlaceName = mysql_real_escape_string($PlaceName);
$sql="SELECT * from $database1 LEFT JOIN $database2 on $database1.ID = $database2.ID WHERE PlaceName='$PlaceName'";
$query = mysql_query($sql);
echo '<h1>People who are searching for '.$PlaceName.':</h1>';
echo '<ul>';
while ($row = mysql_fetch_array($query)) {
echo "<li>ID #",$row['ID'],": ",$row['MemberPersonalName']," ",$row['MemberSurname']," -- searching for ",$row['SurnameBeingSearched'],"</li>";
}
echo '</ul>';
...and it works and all is well. Right now the output looks like this...
People who are searching for Hogwarts:
ID #137: Hermione Granger -- searching for Stern
ID #137: Hermione Granger -- searching for Engelberg
ID #503: Harry Potter -- searching for Kreindler
ID #549: Ron Weasley -- searching for Kreindler
ID #1062: Draco Malfoy -- searching for Engelberg
ID #1155: Ginny Weasley -- searching for Kreindler
ID #1155: Ginny Weasley -- searching for Streisand
But the output needs tweaking, and I'm having trouble writing my SQL query statement to reflect the changes. What I really want is for the output to look like this...
People who are searching for Hogwarts:
Engelberg is being searched by Hermione Granger (id #137) and Draco Malfoy (id #1062)
Kreindler is being searched by Harry Potter (id #503), Ron Weasley (id #549), and Ginny Weasley (id #1155)
Stern is being searched by Hermione Granger (id #137)
Streisand is being searched by Ginny Weasley (id #1155)
In other words, I need to group the output together by the field 'SurnameBeingSearched', I need to list the names of the people doing the searching in an "X, Y, and Z" output format (where it knows where to add a comma, if necessary, depending on the number of results), and I need to order the results by the 'SurnameBeingSearched' field.
Help? Thanks!
You need to list the names so this isn't an aggregation (in the SQL sense) problem. Keep your current query. You're going to have to do the grouping in code.
So something like:
$rows = array();
$last = '';
while ($row = mysql_fetch_array($query)) {
$surname = $row['SurnameBeingSearched'];
$id = $row['ID'];
$name = $row['MemberPersonalName'];
if ($last != $surname) {
$last = $surname;
$rows[] = array();
}
$rows[count($rows)-1][$id] = $name;
}
foreach ($rows as $row) {
// now display each group of names
}
You might also be able to use the MySQL GROUP_CONCAT() function.
It would look something like this...
SELECT places_tbl.name, GROUP_CONCAT(people_tbl.name)
FROM places_tbl
LEFT JOIN people_tbl ON (places_tbl.id = people_tbl.id)
GROUP BY places_tbl.id
GROUP_CONCAT() by default returns the values as comma delimited. You can probably split them up to get the formatting as you need it or use the SEPARATOR keyword. GROUP_CONCAT(fieldname SEPARATOR '-')
$PlaceName = $_GET['townName'];
$PlaceName = mysql_real_escape_string($PlaceName);
// note - added order to the query
$sql="SELECT * from $database1 LEFT JOIN $database2 on $database1.ID = $database2.ID WHERE PlaceName='$PlaceName'
ORDER BY SurnameBeingSearched, MemberSurname, MemberPersonalName";
$query = mysql_query($sql);
echo '<h1>People who are searching for '.$PlaceName.':</h1>';
echo '<ul>';
$cntr = mysql_num_rows($query);
if ($cntr > 0) {
$i = 0;
$srchd = mysql_result($query, $i, 'SurnameBeingSearched');
$mbr = mysql_result($query, $i, 'MemberPersonalName');
$mbr = $mbr . " " . mysql_result($query, $i, 'MemberSurname');
$mbr = $mbr . " (id #" . mysql_result($query, $i, 'ID') . ")";
$lin = $srchd . " is being searched by " . $mbr;
$prev = $srchd;
if ($cntr == 1) {
echo "<li>" . $lin . "</li>";
} else {
for ($i = 1; $i< $cntr; $i++) {
$srchd = mysql_result($query, $i, 'SurnameBeingSearched');
$mbr = mysql_result($query, $i, 'MemberPersonalName');
$mbr = $mbr . " " . mysql_result($query, $i, 'MemberSurname');
$mbr = $mbr . " (id #" . mysql_result($query, $i, 'ID') . ")";
if ($srchd == $prev) { // common search
$j = $i + 1;
if ($j < $cntr) { // still have data
$nxt = mysql_result($query, $j, 'SurnameBeingSearched');
if ($prev == $nxt) { // another one coming -- use the comma
$lin = $lin . ", " . $mbr;
} else {
$lin = $lin . ", and " . $mbr; // last member add the 'and' - line is done
echo "<li>" . $lin . "</li>";
}
$prev = $srchd;
} else { // ran out of data - need to finish the line
$lin = $lin . ", and " . $mbr; // last member add the 'and' - line is done
echo "<li>" . $lin . "</li>";
} else { // new search - need to print this line and start a new one
echo "<li>" . $lin . "</li>";
$lin = $srchd . " is being searched by " . $mbr;
$prev = $srchd;
} // test searched = previous
} // next i
} // only one row
} // cntr > 0
echo '</ul>';
/* note: this is not tested
I would recommend using table1 and table2 instead of database1 and database2
or better give the tables meaningful names
I would use active voice instead of passive voice
*/

Categories