I wrote a PHP/MySQLi frontend, in which the user can enter SQL queries, and the server then returns the results in a table (or prints OK on INSERTs and UPDATEs)
As printing the results can take a very long time (e.g. SELECT * FROM movies) in a IMDb extract with about 1.6M movies, 1.9M actors and 3.2M keywords, I limited the output to 50 rows by cancelling the printing for-loop after 50 iterations.
However, the queries themselves also take quite some time, so I hoped that it might be possible to set a global maximum row return value, nevertheless whether the LIMIT keyword is used or not. I only intended to use the server for my own practice, but as some people in my class are struggling with the frontend provided by the teacher (Windows EXE, but half of the class uses Mac/Linux), I decided to make it accessible to them, too. But I want to keep my Debian VM from crashing because of - well, basically it would be a DDoS.
For clarification (examples with a global limit of 50):
SELECT * FROM movies;
> First 50 rows
SELECT * FROM movies LIMIT 10;
> First 10 rows
SELECT * FROM movies LIMIT 50,100;
> 50 rows (from 50 to 99)
Is there any possibility to limit the number of returned values using either PHP/MySQLi or the MySQL server itself? Or would I have to append/replace LIMIT to/in the queries?
You can use there queries and add "LIMIT 50" to it.
And if they added LIMIT by them self just filter it out with regex and still add your LIMIT.
I believe you have to build yourself a paginator anyway, avoiding to use LIMIT statement is not really possible i believe.
Here is what I would suggest for a Paginator:
if($_REQUEST['page'] == ""){
$page = 1;
}else{
$page = $_REQUEST['page']; // perhaps double check if numeric
}
$perpage = 50;
$start = ($page - 1) * $perpage;
$limit_string = " LIMIT ". $start . "," . $perpage ;
$query = "SELECT * FROM movies";
$query .= $limit_string;
Hope that helps
You can create a function.
https://dev.mysql.com/doc/refman/5.0/en/create-function.html
Let us know if this helps.
Related
I am using this script to cross reference data so that I can retrieve the right data from the database which requires 3 checks of which 2 are run within a for loop
$state = str_replace("<span>","",$state);
$state = str_replace("</span>","",$state);
$getStateGroups = $wpdb->get_results("SELECT * FROM wp_gmw_locations WHERE region_code='$state'");
$groupIdList = array();
for($i = 0; $i < count($getStateGroups); $i++){
array_push($groupIdList,$getStateGroups[$i]->object_id);
}
$groupMemberList = array();
for($i =0; $i < count($groupIdList);$i++){
$ggm = $wpdb->get_results("SELECT * FROM wp_bp_groups_members WHERE group_id=" . $groupIdList[$i]);
for($x = 0; $x < count($ggm); $i++){
array_push($groupMemberList,$ggm[$x]->user_id);
}
}
$whereList = implode(',',$groupMemberList);
$userGameData = $wpdb->get_results("SELECT * FROM user_game_data WHERE uid IN ($whereList) ORDER BY lifetime_keys_spent DESC");
return implode(',',$groupIdList);
using this I am running out of memory
I have tried increasing my memory limit from 256M to 3GB and it stills runs out of memory
I am using wordpress so $wpdb is the class that wordpress uses to interact with the database
I am required to cross reference the data pulled to get the information required for the functionality we want is there any way i can reduce the memory that this is trying to use or is there some bug that in the code that i am missing?
EDIT:
The script runs out of memory on the line array_push($groupMemberList,$ggm[$x]->user_id);
SELECT * FROM wp_gmw_locations WHERE region_code='$state'
if you only need the ids, why are you selecting everything? (not to mention that just interpreting $state in there makes you prone for SQL injection attacks, read up on prepared statements.)
your first improvement would be only selecting the id:
SELECT object_id FROM wp_gmw_locations WHERE region_code='$state'
next thing: you're only using the ids for further queries, you're not using them in your code otherwise, right?
then why not let the database do the heavy lifting, it's much better at it:
SELECT * FROM wp_bp_groups_members WHERE group_id IN
(SELECT object_id FROM wp_gmw_locations WHERE region_code='$state')
which not just gets rid of all that memory overhead, but also turns n+1 queries to the database into one.
again, you only need the id? further refine your select then!
SELECT user_id FROM wp_bp_groups_members WHERE group_id IN
(SELECT object_id FROM wp_gmw_locations WHERE region_code='$state')
that should drastically reduce your memory footprint and your execution time. you should be able to take it from here and improve the rest of your code. i recommend spinning up a SQL-console and just playing around a bit so you get a feel for what the database can do for you - it's quite a lot! i haven't encountered any data retrieval task in the past few years i couldn't solve with just one query.
by the way: take a look at foreach-loops, they make code quite elegant compared to for-loops.
I have the following query for a type-ahead search (as you type into the form it displays matches in a drop down). This query worked well until I switched to a database with about a million records. Now it takes 15 seconds for the match to be displayed.
Because search hits are displayed as you type, the query is inside a loop. Is there anything about this query that can be changed to speed it up?
$diagnosis = isset($_GET['diagnosis']) ? $_GET['diagnosis'] : '';
$data = array();
if ($diagnosis) {
$query = explode(' ', $diagnosis);
for ($i = 0, $c = count($query); $i < $c; $i ++) {
$query[$i] = '+' . mysql_real_escape_string($query[$i]) . '*';
}
$query = implode(' ', $query);
$sql = "SELECT diagnosis, icd9, MATCH(diagnosis) AGAINST('$query' IN BOOLEAN MODE) AS relevance
FROM icd10 WHERE MATCH(diagnosis) AGAINST('$query' IN BOOLEAN MODE) HAVING relevance > 0 ORDER BY relevance ";
$r = mysql_query($sql);
while ($row = mysql_fetch_array($r)) {
$data[] = $row;
}
}
echo json_encode($data);
exit;
You can try some stuff:
First, make sure you have a fulltext index for diagnosis. Second, make sure you have a fulltext index for diagnosis! A million rows isn't that much (depending on the number of words in diagnosis of course), so that just already might be the problem.
Then try the following code:
SELECT diagnosis, icd9, MATCH(diagnosis) AGAINST('$query' IN BOOLEAN MODE) AS relevance
FROM icd10 ORDER BY relevance desc limit 30
(It might not be obvious that this is faster, and it might not be, so just try it).
If you need to support short words, e.g. if 3 digit icd9-codes are entered often, you should check your ft_min_word_len / innodb_ft_min_token_size-values (depending on your database) to make sure they are included in the index - but be aware it will increase your index size. Maybe check the stopwords.
You didn't specify your setup; you can often improve general database performance by e.g. changing settings, hdds or ram. Especially ram.
Some general ideas: You might want to call the function asynchronously (the user should be able to type while the query runs). As soon as you hit less than 30 results (or whatever limit you set), you can just filter the remaining results on the fly in php (as long as the query gets longer/no words are removed) - it's the closest you get to a cache. Or set the limit to 1000 and filter manually afterwards, php regex is fast too, you just need a score-function.
Depending on your data, you might want to not run the query when you just add a single letter to the query (every text will contain a word beginning with an "a", so you might not get a better result - that might not be the case for "q" though). That won't reduce runtime of the query, but you can just save one execution.
I am pulling some data from mysql database using PHP, now I would like to place that data in a html table which I have completed, how can I make the table pageable if it goes over say 10 records? Is there a tutorial I can look into or any information where I can get this? Maybe a tool I can implement easily? I just haven't found anything online about this topic but perhaps anyone here can lead me in the correct direction
I am currently using just a simple <table></table> html
You can achieve paging with MySQL's LIMIT keyword.
You can then use a query string to tell the website which page to get.
First we need to set a default page number and define how many results we want to display in the page:
$items_per_page = 10;
$page = 1;
if(isset($_GET['page'])) {
$page = (int)$_GET['page'];
}
The LIMIT keyword works by providing an offset and the number of rows you want to limit to. So now we need to figure out the offset:
$offset = ($page - 1) * $items_per_page;
Now we have all of the information we need to limit the results correctly based on the page number in our query string:
$query = "SELECT column_1, column_2 FROM your_table LIMIT {$offset}, {$items_per_page};";
$result = mysql_query($query) or die('Error, query failed');
while($row = mysql_fetch_assoc($result)) {
echo $row['column_1'] . '<br />';
}
Now to show your different pages you just add the query string to the end of your page URI.
For example my_page.php?page=1 or my_page.php?page=2
Perhaps you could try to figure out how to create the paging links by yourself and post more if you can't get it to work.
You just need to find out the total rows in your query with COUNT in MySQL and you can do all of the maths from there ;)
You'll need some javascript in order to do paging..
Take a look at http://flexigrid.info/
In summary, what you should do is have your php script return the tabular data as JSON or XML, and then feed it to flexgrid.
You can also have flexgrid request records when they're needed using additional requests.
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.
Given 5,000 IDs of records fetch in the database, which query , in your opinion is faster?
Loop through 5000 IDs using php and perform a SELECT query for each one,
foreach($ids as $id){
// do the query
$r = mysql_query("SELECT * FROM TABLE WHERE ID = {$id}");
}
Or collect all ids in an array, and use SELECT * FROM TABLE WHERE ID IN (1 up to 5000)
//assuming $ids = array(1,2 ---- up to 5000);
$r = mysql_query("SELECT * FROM TABLE WHERE ID IN (".join(",",$ids).")");
Without a shadow of a doubt, loading them all in one go will be faster. Running 5,000 queries is going to be a lot slower as each query will carry a certain amount of overhead.
Also, to speed it up even more, DON'T use the * operator! Select the fields you are going to use, if you only need the ID column, specify this! If you want all the columns, specify them all, because you may later add fields in and you do not need to retrieve this new field.
option 2 is definitely going to be faster. 5000 separate db queries are going to have huge network connection overhead.
The fastest way is not to request 5000 rows at all.
You barely need 100 to display them on one page. 5000 is way overkill
Sure measure it, but I'd certainly recommend letting the database doing the job.
All depends, I hope you're not creating a connection for each call though.
Loop is faster if you use a Query Statement using bind variables. Declare the Statement off the loop; then inside the loop bind the variable per each id.
Do not underestimate the time going into SQL parsing; especially on these long winded things.
Option 2 is faster. With option 1 you do a full roundtrim to the server for each iteration.
I'd point out that in this case you might consider using paging to display the data.
Hint: Measure, Measure, Measure. With a code worth 10 minutes of your time you will have the answer right away.
Which is faster for many queries?
Try measure it for example like this:
<?php
$start = getmicrotime();
for ($i=0;$i<100000;$i++)
{
foreach($ids as $id){
// do the query
$r = mysql_query("SELECT * FROM TABLE WHERE ID = {$id}");
}
}
$end = getmicrotime();
echo 'Time (1): '.($end- $start).' sec';
$start = getmicrotime();
for ($i=0;$i<100000;$i++)
{
//assuming $ids = array(1,2 ---- up to 5000);
$r = mysql_query("SELECT * FROM TABLE WHERE ID IN (".join(",",$ids).")");
}
$end = getmicrotime();
echo 'Time (2): '.($end- $start).' sec';
?>