I am not a programmer or good at programming, just laying this out. I had a site coded for me, but the coder is not interested in further development. I need to rank images in the database by losses (kinda like votes) to views of an image. Originally, here is what was written for the code:
// get images
$result = $this->db->query("SELECT * FROM `ylyl_images` ORDER BY `loses` DESC LIMIT $start, 12");
That basically orders the images on the site by loses, and to paginate, it has the $start variable which is incremented by 12 for every "next page" click.
$result = $this->db->query("SELECT (`loses`/`views`) AS `ratio` FROM `ylyl_images` WHERE `views` > 150 ORDER BY `ratio` DESC LIMIT $start, 12");
That is what I came up with to order images based on ratio of loses to views, but the query doesn't return anything. I tried replacing the $start variable with just 0, but it still doesn't work.
Are you returning the right fields? In the code above, you Changed:
SELECT * FROM...
To...
SELECT loses/views AS ratio FROM...
Perhaps you need to return more fields. While I do not recommend 'SELECT *', you could do the following:
SELECT *, loses/views AS ratio FROM...
Related
I've got a products table that I'm trying to get to work. The query brings back results; however, it isn't actually using the ORDER BY FIELD to sort the results. It's skipping it somehow. I even tried ORDER BY FIELD(sc.id,'4','5','6'), and that didn't work either.
Is it even possible to use table_name.column in an ORDER BY FIELD()? Is there an alternative or a better method of doing this query?
$product = $db1q->query("
SELECT p.id, p.name, p.image, p.url,p.subcat as subcat, sc.id as scid,sc.name as scname
FROM Product as p
JOIN Product_Sub_Category as sc ON p.subcat = sc.id
WHERE p.visibility='1' AND find_in_set(p.id,'". $sidr['products'] ."')
ORDER BY FIELD(p.subcat,'4','5','6'), sc.sort_order ASC, p.sort_order ASC")
or die ('Unable to execute query. '. mysqli_error($db1q));
I just dumbed the query down to the basic level....
$product = $db1q->query("
SELECT id, name, image, url,subcat
FROM Product WHERE visibility='1' AND id IN ({$sidr['products']}) ORDER BY FIELD(subcat,'5','4','6','22')") or die ('Unable to execute query. '. mysqli_error($db1q));
and for some reason the order of my subcats are as follows....
3,12,23,5,5,4,4,4,4,4,22
Why wouldn't they begin with 5, 4, 6(doesn't exist), and 22? Then display 3,12, and 23 after those are first....
Simple Rextester Demo
When datatype is numeric don't compare to 'string' values
eg visibility = '1' if visibility is numeric you really shouldn't have the apostrophes around it. same in the field function given subcat.
$product = $db1q->query("SELECT id, name, image, url,subcat
FROM Product
WHERE visibility='1'
AND id IN ({$sidr['products']})
ORDER BY case when subcat in (5,4,6,22) then 0 else 1 end,
FIELD(subcat,5,4,6,22)
") or die ('Unable to execute query. '. mysqli_error($db1q));
or something like:
order by case when field(sort,'5','4','22') = 0 then (select max(sort)+1+sort from Product)
else field(sort,'5','4','22') end;
The issue with the 2nd approach is that it has to run a subquery for every record. In addition if the size of subcat/sort exceed or approach the max of int we'll run into a problem adding the values together. This problem is negated by using the 2 column sort approach in the first method.
Again, my gut feeling is that the first approach with 2 sort columns would be faster; and in my opinion easier to follow/maintain. The downfall is if the sort order defined changes then we have to change code. So... why have the order defined here... what isn't the order defined in a table; or is the order passed in as a parameter by user?
I have a query that I wrote a couple of years ago for a membership website. I'm not extremely well versed in using $wpdb (or MYSQL code in general) to write custom queries, and the site has grown quite a bit. There are about 150k rows in the wp_usermeta table now, and the page where the query runs hangs for a couple of seconds now before loading. I expect that this will get worse as time goes on and the site gains more users.
Any help in figuring out how to speed this query up would be greatly appreciated.
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$limit = 15;
$offset = ($paged - 1) * $limit;
$key = 'first_name';
$sql = "SELECT SQL_CALC_FOUND_ROWS {$wpdb->users}.* FROM {$wpdb->users}
INNER JOIN {$wpdb->usermeta} wp_usermeta ON ({$wpdb->users}.ID = wp_usermeta.user_id)
WHERE 1=1
AND wp_usermeta.meta_key = '$key'
AND wp_usermeta.meta_value <> ''
AND wp_users.user_registered < '2014-01-30'
ORDER BY wp_usermeta.meta_value ASC
LIMIT $offset, $limit";
$members = $wpdb->get_results($sql);
$found_rows = $wpdb->get_var("SELECT FOUND_ROWS();");
foreach ($members as $member) { // display member info here }
*Note: I am paginating...not displaying all results on one page.
A couple of things here.
First, you seem to be ordering by user first name, but not retrieving or displaying it. That's a bit odd. You could order by wp_users.user_nicename, and omit the join to wp_usermeta. That will save some time in joining. That column's value is set by the user in her profile, and reflects the identity by which she wants to be known. So, it's an appropriate ordering column, most likely.
Second, you're retrieving all the columns (*) in wp_users. Can you avoid retrieving them all, and rather enumerate the one you actually need? That will save time in sorting.
Your query would become this.
$sql = "SELECT SQL_CALC_FOUND_ROWS
id, user_login, user_nicename, etc etc
FROM {$wpdb->users}
WHERE user_registered < '2014-01-30'
ORDER BY user_nicename ASC
LIMIT $offset, $limit";
You are stuck, as you are paginating a large number of users, with some inescapable inefficiency. But this should be better than what you have.
Hi I need to get the results and apply the order by only in the limited section. You know, when you apply order by you are ordering all the rows, what I want is to sort only the limited section, here is an example:
// all rows
SELECT * FROM users ORDER BY name
// partial 40 rows ordered "globally"
SELECT * FROM users ORDER BY name LIMIT 200,40
The solution is:
// partial 40 rows ordered "locally"
SELECT * FROM (SELECT * FROM users LIMIT 200,40) AS T ORDER BY name
This solution works well but there is a problem: I'm working with a Listview component that needs the TOTAL rows count in the table (using SQL_CALC_FOUND_ROWS). If I use this solution I cannot get this total count, I will get the limited section count (40).
I hope you will give me solution based on the query, for example something like: "ORDER BY LOCALLY"
Since you're using PHP, might as well make things simple, right? It is possible to do this in MySQL only, but why complicate things? (Also, placing less load on the MySQL server is always a good idea)
$result = db_query_function("SELECT SQL_CALC_FOUND_ROWS * FROM `users` LIMIT 200,40");
$users = array();
while($row = db_fetch_function($result)) $users[] = $row;
usort($users,function($a,$b) {return strnatcasecmp($a['name'],$b['name']);});
$totalcount = db_fetch_function(db_query_function("SELECT FOUND_ROWS() AS `count`"));
$totalcount = $totalcount['count'];
Note that I used made-up function names, to show that this is library-agnostic ;) Sub in your chosen functions.
Is there a better way to check if there are at least two rows in a table for a given condition?
Please look at this PHP code:
$result = mysql_query("SELECT COUNT(*) FROM table WHERE .......");
$has_2_rows = (mysql_result($result, 0) >= 2);
The reason I dislike this, is because I assume MySQL will get and count ALL the matching rows, which can be slow for large results.
I would like to know if there is a way that MySQL will stop and return "1" or true when two rows are found. Would a LIMIT 2 at the end help?
Thank you.
This is a good question, and yes there is a way to make this very efficient even for large tables.
Use this query:
SELECT count(*) FROM (
SELECT 1
FROM table
WHERE .......
LIMIT 2
) x
All the work is done by the inner query, which stops when it gets 2 rows. Also note the select 1, which gives you a tiny bit more efficiency, since it doesn't have to retrieve any values from columns - just the constant 1.
The outer count(*) will count at most 2 rows.
Note: Since this is an SQL question, I've omitted PHP code from the answer.
The query below will only inspect two rows as you request. mysql_num_rows can check how many rows are returned without any looping.
$result = mysql_query("SELECT col1 FROM t1 WHERE ... LIMIT 2");
if (mysql_num_rows($result) == 2) {
Please avoid using ext/mysql and switch to PDO or mysqli if you can.
$result = mysql_query("SELECT COUNT(*) FROM table WHERE .......");
if(mysql_num_rows($result) > 1)
{
echo 'at least 2 rows';
}
else
{
echo 'less than 2 rows';
}
my $sth = $dbh->prepare("SELECT id
FROM user
WHERE group == '1'
ORDER BY id DESC
LIMIT 1");
I was trying to get the id of the last row in a table without reading the whole table.
I am already accessing via:
my $sth = $dbh->prepare("SELECT name,
group
FROM user
WHERE group == '1'
LIMIT $from, $thismany");
$sth->execute();
while(my ($name,$group) = $sth->fetchrow_array()) {
...and setting up a little pagination query as you can see.
But, I am trying to figure out how to detect when I am on the last (<= 500) rows so I can turn off my "next 500" link. Everything else is working fine. I figured out how to turn off the "previous 500" link when on first 500 page all by myself!
I thought I would set up a "switch" in the while loop so if ($id = $last_id) I can set the "switches" var.
Like:
if ($id = $last_id) {
$lastpage = 1; #the switch
}
So I can turn off next 500 link if ($lastpage == 1).
I am really new to this and keep getting stuck on these types of things.
Thanks for any assistance.
Try to grab an extra row and see how many rows you really got. Something like this:
my #results = ( );
my $total = 0;
my $sth = $dbh->prepare(qq{
SELECT name, group
FROM user
WHERE group = ?
LIMIT ?, ?
});
$sth->execute(1, $from, $thismany + 1);
while(my ($name, $group) = $sth->fetchrow_array()) {
push(#results, [$name, $group]); # Or something more interesting.
++$total;
}
$sth->finish();
my $has_next = 0;
if($total == $thismany + 1) {
pop(#results);
$has_next = 1;
}
And BTW, please use placeholders in all of your SQL, interpolation is fraught with danger.
Always asking for one more row than you are going to show, as suggested by mu is too short, is a good way.
But if you want to take the other suggested approach of doing two separate queries, one to get the desired rows, and one to get the total count if there had not been a limit clause, MySQL provides an easy way to do that while combining as much of the work as possible:
SELECT SQL_CALC_FOUND_ROWS name, group FROM user WHERE group = '1' LIMIT ..., ...;
then:
SELECT FOUND_ROWS();
The SQL_CALC_FOUND_ROWS qualifier changes what a following FOUND_ROWS() returns without requiring you to do a whole separate SELECT COUNT(*) from user WHERE group = '1' query.
SELECT COUNT(*) from tablename will give you the number of rows, so if you keep a running count of how many rows you have read so far, you'll know when you're on the last page of results.
You could generate that query with (untested; away from a good workstation at the moment):
my $sth = $dbh->prepare("select COUNT(*) FROM user WHERE group == '1'");
my #data = $sth->fetchrow_array;
my $count = $data->[0];
(PS. you should be aware of SQL injection issues -- see here for why.)
As Ether mentioned in the comments, pagination usually requires two queries. One to return your paged set, the other to return the total number of records (using a COUNT query).
Knowing the total number of records, your current offset and the number of records in each page is enough data to work out how many pages there are in total and how many before and after the current page.
Although your initial suggestion of SELECT id FROM table WHERE ... ORDER BY id DESC LIMIT 1 should work for finding the highest matching ID, the standard way of doing this is SELECT max(id) FROM table WHERE ...