Print data from a DB grouped under the starting letter using PHP? - php

I'm trying to set up a for loop to print all data from a database table that starts with a certain letter. For example, let's say from all the usernames in the database, I only want to print every username starting with the letter "b". My end result I want to achieve is something along the lines of this:
A
adam
angel
apple
B
ball
bear
blue
C
car
cell
chris
#
0wen
1uis
3than
.,_
.apple.
,car,
_jeff_
I want to be able to print all usernames under the corresponding character in which they start with. I have the starting characters under heading tags, so all I really need is to print the usernames under them. I figured running a simple for loop under each heading tag that filters that data would do the trick, but I for the life of me can't figure out how to go about doing it. This my code so far (I know this will print every user in the table):
require_once 'important/connect.php';
$query = $link->prepare('select distinct usr from info order by usr');
$query->execute();
$users = $query->fetchAll(PDO::FETCH_OBJ);
foreach ($users as $user)
{
print "<center>{$user->usr}</center>";
}
This code above is merely to show what I'm working with. I'm shooting to keep each username to print as a url as well, so when their username is clicked, it will display more information in a seperate pop up window, however I already have that working. Anyway, how would I implement this or is the way I wanna go about it not possible?

For the loop you can use this PHP syntax:
foreach(range('A','Z') as $letter) {
//your own code
}
range will make you iterate through the letters as you do usually with foreach.
The point about your question is about performance.
If you have a small amount of data to return you can return the whole array and then search with php functions to manipulate arrays only the items that begins with $letter.
If you have many items it is better (IMHO) to run a query for each letter using mysql instruction LIKE (cause this will improve performance):
"SELECT usr FROM info WHERE usr LIKE '$letter%' ORDER BY usr GROUP BY usr"
LIKE will match only those usr that will start with $letter and will have anything else following. % is the wildcard to be used.
Also note that in the query I have used GROUP BY usr instead of SELECT DISTINCT usr to get the same result as this is the right way to get an unique list of users.

As Lelio Faieta pointed out, looping through the user list over and over again might be bad for performance. However, querying the database over and over again might also be bad.
So I would suggest getting the users just once, and getting them in the right order:
SELECT usr FROM info ORDER BY usr GROUP BY usr
Then loop through them, and keep track of what starting letter you're on:
$oldLetter = '';
$newLetter = '';
foreach ($users as $user)
{
$newLetter = strtoupper(substr($user->usr, 0, 1));
if($oldLetter != $newLetter)
{
//We are on a new letter.
//Print a heading for all letters between the old one and the new one.
foreach(range(++$oldLetter, $newLetter) as $letter)
print '<h2>' . $letter . '</h2>';
$oldLetter = $newLetter;
}
//Print the user, as before.
print "<center>{$user->usr}</center>";
}
This will not take care of the last group (titled # in your example) for you. To do that, you will need to check whether the first character is a letter in the SQL and sort on that somehow.
Please note that this code is not copy-paste-ready, you will need to work some on it. For instance if the old letter is Z there might be some problems. I have not tested this, so you should before you put it into production.

Pseudo code for a nasty solution:
$alphabetArray = array("A","B", etc);
foreach($alphabetArray as $letter) {
echo '<h2>'.$letter.'</h2>';
$sql = "SELECT * FROM `usertable` WHERE `username` = '".$letter."%' ORDER BY `username` ASC;"
// execute sql, loop through and output results
}

Related

Multiple MYSQL Statements executed as a single statement

I've got a bit of a problem with my code. I'm sure that it is something simple, but I just can't figure it out! I have been on tons of forums and have read several books... but every answer that I have worked to has failed. I almost guarantee that it's the way that I am using my syntax (and yes I know... procedural PHP is not really used anymore) but I am really a bit of a newbie to this and I am just trying to pick up the basics before moving onto OOP and PDO connections.
Could you please help me? At the moment I can get the user to select their date from the date picker and the results specifically from that date only will return... only problem is that the event is displaying the event_id as opposed to the name of the event that it relates to (1 = 5km run) for example.
Somehow I need to access the events table and pull the row that relates to that specific event_id.
I have normalized my database, and according to my tutor it looks ok. To give you an idea what it looks like - logins table (all user logins details), results table (a history of submitted events) events table (the events themselves).
On the results table the foreign keys are logins_id and the event_id. The primary key is the results_id in the results table and the only data stored here is the time and data (individual columns).
<?php // -----Stage 1. On submission of the form run the following -----//
if (isset($_POST['submit_d'])) {
$mydate = $_POST ['MyDate'];
$my = preg_replace('/[^a-zA-Z0-9]+/', ' ', $mydate);
if ($mydate) {
$result = mysql_query("SELECT * FROM logins WHERE username = '$username' LIMIT 1");
//This function will take the above query and create an array...
while($row = mysql_fetch_array($result))
{
//With the array created above, I can create variables (left) with the outputted array (right)
$logins_id3 = $row['logins_id'];
}
$sql = "SELECT * FROM results where $logins_id3 AND date = $mydate ";
/* ----- Here is the code that I want to use in conjunction with the above statement --->
$query = "SELECT logins.username,events.event,results.time,results.date,logins.age,logins.gender
FROM logins INNER JOIN results ON logins.logins_id=results.logins_id INNER JOIN events ON results.event_id=events.event_id
ORDER BY time ASC LIMIT 10";
*/
$resultz = mysql_query($sql);
if( mysql_num_rows($resultz) ) {
while ($row = mysql_fetch_array($resultz)) {
echo "<table><tr><th>Username</th><th>Event</th><th>Time (HH:MM:SS)</th><th>Date (YY/MM/DD)</th><th>Age</th><th>Gender</th>
</tr><tr><td>".$username."</td>"
."<td>".$row['event_id']."</td>"."<td>".$row['time']."</td>"." <td>".$row['date']."</td>"."<td>".$row['age']."</td>".
"<td>" $row['gender']."</td></tr></table>";
}
}
}
}
?>
The other thing I would like to do.. although this is not crucial, is to strip special characters from the input. Basically I'm using a jquery calendar picker and I want the user to be able to select their date in the 2014-05-26 format and the php to remove the - before it is submitted to the database, that way it doesn't effect the users experience but it will work with my current code.
Anyways sorry to waffle on, any help on either of these matters would be much appreciated!
Yours Sincerely:
Peter Scales.
You can use a join to get the data that relates to the event ID:
SELECT * FROM results r LEFT JOIN events e ON r.event_id = e.event_id WHERE ...
You can then select where "e.event_id = $event_id"; and the rest of your query logic.
You can also filter out any unwanted characters by using preg_replace: http://ar2.php.net/preg_replace

Is there a better way to create an alphabetic pagination index in PHP/MySQL?

One of my standard behaviors for pagination within my CMSs is to show an alphabetic quickbar when sorting by an alpha column. For example, if the results are being sorted by Last Name, under the pagination I output a series of links, A to Z, to take you directly to the page for a particular first character.
Example:
Currently I'm doing this by getting all the results for that column, sorted alphabetically, and then looping through them all in PHP and recording what page the record appears on. This works fine when you're only dealing with a few hundred results, but I'm now working on a project that could potentially have several hundred thousand rows and it simply isn't a viable option.
Is there a more efficient method to produce this kind of index? Note that it also needs to handle more than just A-Z, since rows may begin with numbers or punctuation.
Edit for clarification:
I'm not looking for a simple list of all the first characters, that's easy. I need to calculate what page of the total results the field starting with that character would be on. So say we're looking for someone named Walter, and I have 1000 rows, I need to know where in that 1-1000 range the W's start at.
I presume it's a varchar field, so have you considered the following:
SELECT DISTINCT SUBSTRING(lastname FROM 1 FOR 1) FROM mytable;
This will get you a distinct list of the first letters of the last name.
You can also use UPPER() to ensure you just get upper case characters. LEFT() will also achieve something similar, so you should experiment to see which performs quickest on your dataset.
Edit: If you also want counts:
SELECT DISTINCT SUBSTRING(lastname FROM 1 FOR 1) AS firstletter, COUNT(*) AS counter FROM mytable GROUP BY firstletter;
No need to do a second query for each letter.
$sql = "SELECT left(name, 1) AS firstchar FROM mytable ORDER BY name";
$result = mysql_query($sql) or die(mysql_error());
$letters = array();
$row = 0;
while($row = mysql_fetch_assoc($result)) {
$row++;
if (!isset($letters[$row['firstchar']])) {
$letters[$row['firstchar']] = $row;
}
}
This would give you an array keyed by the first letters, and the row number they first appeared on for the value:
a => 1,
b => 50,
c => 51,
etc...
There's probably some way of doing it purely in SQL, but MySQL itself doesn't have any 'row number' support built in, so it'd be a highly ugly query.
Just like on standrd pagination is just a matter of fetching and ordering - simply add WHERE with A% (dont forget to create index on this column)
<?php
$result1 = mysql_query("SELECT LEFT(name, 1) AS fl FROM comics GROUP BY fl");
while ($row = mysql_fetch_array($result1))
{
$result11 = mysql_query("SELECT * FROM comics WHERE name LIKE '".$row['fl']."%'");
$countresult11 = mysql_num_rows($result11);
?>
<?php echo $row['fl']; ?>
<?php } ?>
might be kinda what you are looking for if you replace my variables/table names with yours.
that will check the table, pull the first letter from each, group by that letter and output it as
1 3 7 9 A B R W X Y Z
depending on what you have in the table

Extremely slow search page load (MySQL and PHP)

I made a simple search box on a page, where a user can type in keywords to look for photos of certain items, using PHP. I'm using an MySQL database. I trim the result and show only 10 to make the loading quicker, but certain set of keywords causes the browser to hang on both IE and Firefox. When this happens on IE, I can see outlines of photos (just the silhouette) beyond the 10 results with an "X" mark at the top right corner, similar to when you load a photo and the photo doesn't exist on a webpage, even though I wrote the code to show only 10 results. The database has over 10,000 entries, and I'm thinking maybe it's trying to display the entire set of photos in the database. Here are some code that I'm using.
I'm using the function below to create the query. $keyword is an array of the keywords that the user has typed in.
function create_multiword_query($keywords) {
// Creates multi-word text search query
$q = 'SELECT * FROM catalog WHERE ';
$num = 0;
foreach($keywords as $val) { // Multi-word search
$num++;
if ($num == 1) {
$q = $q . "name LIKE '%$val%'"; }
else {
$q = $q . " AND name LIKE '%$val%'";}
}
$q = $q . ' ORDER BY name';
return $q;
//$q = "SELECT * FROM catalog WHERE name LIKE \"%$trimmed%\" ORDER BY name";
}
And display the result. MAX_DISPLAY_NUM is 10.
$num = 0;
while (($row = mysqli_fetch_assoc($r)) && ($num < MAX_DISPLAY_NUM)) { // add max search result!
$num++;
print_images($row['img_url'], '/_', '.jpg'); // just prints photos
}
I'm very much a novice with PHP, but I can't seem to find anything wrong with my code. Maybe the way I wrote these algorithms are not quite right for PHP or MySQL? Can you guys help me out with this? I can post more code as necessary. TIA!!
Don't limit your search results in PHP, limit them in the SQL query with the LIMIT keyword.
As in:
select * form yourtable where ... order by ... limit 10;
BTW, those LIKE '%something%' can be expensive. Maybe you should look at Full text indexing and searching.
If you want to show a More... link or something like that, one way of doing it would be to limit your query to 11 and only show the first ten.
Apart from the LIMIT in your query, I would check out mysql full text search (if your tables have the MyISAM format).
Why don't use use MySQL to limit the number of search results returned?
http://dev.mysql.com/doc/refman/5.0/en/select.html
add LIMIT to your query.
you are retrieving all rows from DB (lot of bytes traveling from DB to server) and then you are filtering the first 10 rows.
try
$q = $q . ' ORDER BY name LIMIT 10';
LIKE is slow also according to Flickr(slides 24-26). You should first try to use FULL TEXT indexes instead. If your site still seems slow there are also some other really fast(er)/popular alternatives available:
sphinx
elasticsearch
solr
The only thing that is a little bit annoying that you need to learn/install these technologies, but are well worth the investment when needed.

While loop for mysql database with php?

I am developing a mysql database.
I "need" a unique id for each user but it must not auto increment! It is vital it is not auto increment.
So I was thinking of inserting a random number something like mt_rand(5000, 1000000) into my mysql table when a user signs up for my web site to be. This is where I am stuck?!
The id is a unique key on my mysql table specific to each user, as I can not 100% guarantee that inserting mt_rand(5000, 1000000) for the user id will not incoherently clash with another user's id.
Is there a way in which I can use mt_rand(5000, 1000000) and scan the mysql database, and if it returns true that it is unique, then insert it as the user's new ID, upon returning false (somebody already has that id) generate a new id until it becomes unique and then insert it into the mysql database.
I know this is possible I have seen it many times, I have tried with while loops and all sorts, so this place is my last resort.
Thanks
You're better off using this: http://dev.mysql.com/doc/refman/5.0/en/miscellaneous-functions.html#function_uuid
Or using this: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
But if you actually want to do what you are saying, you can just do something like:
$x;
do {
$x = random_number();
"SELECT count(*) FROM table WHERE id = $x"
} while (count != 0);
// $x is now a value that's not in the db
You could use a guid. That's what I've seen done when you can't use an auto number.
http://php.net/manual/en/function.com-create-guid.php
Doesn't this function do what you want (without verification): http://www.php.net/manual/en/function.uniqid.php?
I think you need to approach the problem from a different direction, specifically why a sequence of incrementing numbers is not desired.
If it needs to be an 'opaque' identifier, you can do something like start with a simple incrementing number and then add something around it to make it look like it's not, such as three random numbers on the end. You could go further than that and put some generated letters in front (either random or based on some other algorithm, such as the day of the month they first registered, or which server they hit), then do a simple checksuming algorithm to make another letter for the end. Now someone can't easily guess an ID and you have a way of rejecting one sort of ID before it hits the database. You will need to store the additional data around the ID somewhere, too.
If it needs to be a number that is random and unique, then you need to check the database with the generated ID before you tell the new user. This is where you will run into problems of scale as too small a number space and you will get too many collisions before the check lucks upon an unallocated one. If that is likely, then you will need to divide your ID generation into two parts: the first part is going to be used to find all IDs with that prefix, then you can generate a new one that doesn't exist in the set you got from the DB.
Random string generation... letters, numbers, there are 218 340 105 584 896 combinations for 8 chars.
function randr($j = 8){
$string = "";
for($i=0;$i < $j;$i++){
srand((double)microtime()*1234567);
$x = mt_rand(0,2);
switch($x){
case 0:$string.= chr(mt_rand(97,122));break;
case 1:$string.= chr(mt_rand(65,90));break;
case 2:$string.= chr(mt_rand(48,57));break;
}
}
return $string;
}
Loop...
do{
$id = randr();
$sql = mysql_query("SELECT COUNT(0) FROM table WHERE id = '$id'");
$sql = mysql_fetch_array($sql);
$count = $sql[0];
}while($count != 0);
For starters I always prefer to do all the randomization in php.
function gencode(){
$tempid=mt_rand(5000, 1000000);
$check=mysql_fetch_assoc(mysql_query("SELECT FROM users WHERE id =$tempid",$link));
if($check)gencode();
$reg=mysql_query("INSERT INTO users id VALUES ('$tempid')",$link);
//of course u can check for if $reg then insert successfull

Numbers and calculations in php/sql

ok im new on the scene with php and sql, i read the basics and was able to produce a DB driven shoutbox.
This is my website http://www.teamdelta.byethost12.com/
What i want to do next im not sure what i should be using java? php? sql? i have no idea...
but i assume php and sql if sql can do what i think it can..
What i want to do next is to A extract data from data at certain positions for example
data in the format "DAB:CD:EF:GH" eg "D03:57:16:23" to be turned into AB, DF, CE. Eg "3","76","51".
then i want to store these Numbers (Not VARCHARS) in the database.
that is part 1.
the sceond part that i want to make sure is possible before i put to much effort in is to do calculations on all the entries in the database with respect to a 3rd peice of data and display all the entries in a decending ordered list ordered by the output of the calculation..
i think calculations can be added to querys...but as i said im new and the tutorial i read only coved reading values out of the db..
and just if it helps to clarify what im trying to do even though this is not part of the question... here is what im trying to do
* set up a database and entry system that records the player name, base name, location,econ, and comment and stored this as a entry in the database..(i have done this)
*on entry i wish to extract numeric values(AB,DF,CE) from a text string(location) (dont know how)
*then i wish to display the list(i have done this)
*a new column should be added on display containing the resuly of a calculation involving the numberic values from each entry with a global value that is entered on page(dont know how)
*then the list should be sorted in decending order of the output of the calculation.
*lastly i wish to be able to remove items from the list with a click.
thats all :) ,, seeking part advice and guidence
here is the page its php but i renamed it as text so its readble.
cant past is as code as it has escape chars in it
http://www.teamdelta.byethost12.com/trade/postroute3.txt
currently trying to use
$values = array();
$string = $location;
$values[0]= $string[1].$string[2];
$values[1]= $string[5].$string[8];
$values[2]= $string[4].$string[7];
$x = intval($values[1], 10);
$y = intval($values[2], 10);
$g = intval($values[0], 10);
print_r($x);
echo(' : '); print_r($y); echo(' : ');
print_r($g);
ok.. for the first part.
$values = array();
$values = split(":", 'DAB:CD:EF:GH');
$int_values = array();
foreach($values as $v) {
$int_values[] = intval($v, 10);
}
// values in base 10
print_r($int_values);

Categories