MySQL Select ordered rows, then randomize the result - mysql

I'm trying to fetch rows, ordered by ID ASC but I also want that result to be randomized afterwards. So basically, if I want to fetch the first 15 rows, I want them to come out randomized but as the first 15 rows.
Basically, I have a frame that loads the first 15 clients, and a button "load more" to load 15 more clients. I simply want the same 15 clients to come out by order ID, but have their positions randomized.
This is what I have thus far, but the RAND() at the end is not having any impact:
SELECT wp_posts.ID, wp_posts.post_title,
wp_postmeta.meta_value,
axess_clients.client_nom,
axess_clients.site
FROM wp_posts
LEFT JOIN axess_clients ON axess_clients.client_id = wp_posts.ID
LEFT JOIN wp_postmeta ON wp_postmeta.post_id = wp_posts.ID
WHERE wp_posts.post_type='clients' AND wp_posts.post_status='publish'
AND wp_postmeta.meta_key='_thumbnail_id'
ORDER BY wp_posts.ID ASC, RAND() LIMIT 15 OFFSET ".$_POST['data']
Is there a way to do this via MySQL or do I really have to pull out PHP for this?

When you have multiple expressions in ORDER BY, the second expression is used to order within a group where the first expression is equal. It can't reorder rows that are already ordered by the first expression.
To reorder something, you need to put the first ordering in a subquery.
SELECT *
FROM (
SELECT wp_posts.ID, wp_posts.post_title,
wp_postmeta.meta_value,
axess_clients.client_nom,
axess_clients.site
FROM wp_posts
LEFT JOIN axess_clients ON axess_clients.client_id = wp_posts.ID
LEFT JOIN wp_postmeta ON wp_postmeta.post_id = wp_posts.ID
WHERE wp_posts.post_type='clients' AND wp_posts.post_status='publish'
AND wp_postmeta.meta_key='_thumbnail_id'
ORDER BY wp_posts.ID ASC
LIMIT 15 OFFSET " . $_POST['data']) AS x
ORDER BY RAND()

Related

Where to start to optimize slow WP SQL query?

I have the following query, which currently takes about 0.3s to load, causing a heavy load on my Wordpress site.
SELECT SQL_CALC_FOUND_ROWS wp11_posts.ID
FROM wp11_posts
WHERE 1=1
AND ( wp11_posts.ID NOT IN (
SELECT object_id
FROM wp11_term_relationships
WHERE term_taxonomy_id IN (137,141) )
AND (
SELECT COUNT(1)
FROM wp11_term_relationships
WHERE term_taxonomy_id IN (53)
AND object_id = wp11_posts.ID ) = 1 )
AND wp11_posts.post_type = 'post'
AND ((wp11_posts.post_status = 'publish'))
GROUP BY wp11_posts.ID
ORDER BY wp11_posts.post_date DESC
LIMIT 0, 5
Where should I start to make it execute faster? Is there an apparent mistake standing out, that should definitely had been done differently?
You have a so-called dependent subquery (a/k/a correlated subquery) in your example. It's a performance killer.
WHERE (
SELECT COUNT(1)
FROM wp_term_relationships
WHERE term_taxonomy_id IN (53)
AND object_id = wp_posts.ID
) = 1
Refactoring it to an independent subquery looks like this:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID
FROM wp_posts
JOIN (
SELECT object_id
FROM wp_term_relationships
WHERE term_taxonomy_id IN (53)
GROUP BY object_id
HAVING COUNT(*) = 1
) justone ON wp_posts.ID = justone.object_id
... WHERE ...
See how this works? It needs to scan term_relationships just one time looking for object_ids meeting your criterion (just one). Then the ordinary inner JOIN excludes posts rows that don't meet that criterion. (The dependent subquery loops to scan the table multiple times, while we wait.)
The SQL_FOUND_ROWS thing: WordPress puts it there to help with "pagination" -- it lets WordPress figure out how many pages (in your case of five items) there are to display. It provides data to the familiar
987 Items << < 2 of [ 20 ] > >>
page-selection interface you see in many parts of WordPress: it counts all the items matched by your query (987 in this example), not just one pageload of them.
If you don't need that pagination you can turn it off by giving a 'nopagination' => true element to WP_Query(). But if your query only yields a small number of items without the LIMIT clause, this probably doesn't matter much. If you wrote the query yourself, just leave it out along with the ORDER BY and LIMIT clauses.
So, leaving in the pagination stuff, a better query is
ANALYZE FORMAT=JSON SELECT wp_posts.ID
FROM wp_posts
JOIN (
SELECT object_id
FROM wp_term_relationships
WHERE term_taxonomy_id IN (53)
GROUP BY object_id
HAVING COUNT(*) = 1
) justone ON wp_posts.ID = justone.object_id
WHERE 1 = 1
AND (
wp_posts.ID NOT IN (
SELECT object_id
FROM wp_term_relationships
WHERE term_taxonomy_id IN (137,141)
)
AND wp_posts.post_type = 'post'
AND wp_posts.post_status = 'publish')
GROUP BY wp_posts.ID
ORDER BY wp_posts.post_date DESC LIMIT 0, 5
You also have an unnecessary GROUP BY near the end of your query. It doesn't hurt performance: MySQL can tell it's not needed in this case and doesn't do anything with it. But it is extra stuff. If you wrote the query yourself leave it out.
SQL_CALC_FOUND_ROWS requires doing nearly as much work as the same query without the LIMIT. [However, removing it without doing most of the following things probably won't help much.]
Do you already have this plugin installed? https://wordpress.org/plugins/index-wp-mysql-for-speed/ If not, that may be a good starting point.
WP is not designed to handle millions of posts/attributes/terms; you may have move on beyond WP.
Using JOIN or LEFT JOIN or [NOT] EXISTS ( SELECT 1 ... ) may be more efficient than IN ( SELECT ... ), especially in older versions of MySQL.
Is your SELECT COUNT(1) attempting to demand exactly 1? That is, 2 would be disallowed? If you really wanted to know if any exist, then use
AND EXISTS( SELECT 1 FROM wp11_term_relationships
WHERE term_taxonomy_id IN (53)
AND object_id = wp11_posts.ID )`
A better index for wp11_posts [I don't know whether your WP or the Plugin has this already]:
INDEX(post_status, post_type, -- first, either order is OK
post_date, ID) -- last, in this order
Having the GROUP BY and ORDER BY the 'same' may eliminate a sort. The following change will probably give you the same results, but faster.
GROUP BY wp11_posts.ID
ORDER BY wp11_posts.post_date DESC
-->
GROUP BY wp11_posts.post_date, wp11_posts.ID
ORDER BY wp11_posts.post_date DESC, wp11_posts.ID DESC

Is there a faster way to select records from a table and query its sub tables

He everyone,
I would like to get all records from a table that are changed, but also if one of its child tables has changed.
I have a working query that does this using subqueries but it takes very long (2 minutes) to complete.
Is there any other way to accomplish this that runs faster?
Thanks!
Wouter
SELECT
wp_posts.id AS order_id,
br_order_archive.id AS archive_id,
wp_posts.post_date_gmt AS date,
wp_posts.post_modified_gmt AS lastmod
FROM
wp_posts
LEFT JOIN
br_order_archive ON br_order_archive.order_id = wp_posts.id
WHERE
wp_posts.post_type = "shop_order"
AND
wp_posts.post_status = "wc-completed"
AND (
br_order_archive.order_id IS NULL
OR wp_posts.post_modified_gmt != br_order_archive.lastmod
OR br_order_archive.lastmod_licenses != (
select max(wp_woocommerce_software_licenses.created)
from wp_woocommerce_software_licenses
where wp_posts.id = wp_woocommerce_software_licenses.order_id
)
OR br_order_archive.lastmod_activations != (
select max(wp_woocommerce_software_activations.activation_time)
from wp_woocommerce_software_activations
LEFT JOIN wp_woocommerce_software_licenses ON wp_woocommerce_software_activations.key_id = wp_woocommerce_software_licenses.key_id
where wp_posts.id = wp_woocommerce_software_licenses.order_id
)
)
ORDER BY
wp_posts.ID desc
In the absence of the necessary debugging information, let's start with this bit....
select max(a.activation_time)
from wp_woocommerce_software_activations a
LEFT
JOIN wp_woocommerce_software_licenses l
ON a.key_id = l.key_id
where p.id = l.order_id
A LEFT JOIN on a table from which you select no columns is a strange thing, as is the correlation between the LEFT JOINed table and the posts table (p). Normally, a LEFT JOINed table would not have an equals operator in a WHERE clause. For my own sanity, I would start by rewriting this as an uncorrelated subquery, regardless of the impact on performance.

Wordpress MySQL - custom meta key order by key and date

I have a meta key which is set by a select drop down so a user can select an option between 1 and 14 and then save their post. I want the posts to display on the page from 1 to 14 ordered by date but if the user creates a new set of posts the next day I also want this to happen so you have posts 1 to 14 each day displaying in that order.. the SQL i have so far is as follows
SELECT SQL_CALC_FOUND_ROWS
wp_postmeta.meta_key,
wp_postmeta.meta_value,
wp_posts.*
FROM wp_posts
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
WHERE 1=1
AND wp_posts.post_type = 'projectgallery'
AND ( wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'private')
AND (wp_postmeta.meta_key = 'gallery_area' )
GROUP BY wp_posts.post_date asc
ORDER BY CAST(wp_postmeta.meta_value AS UNSIGNED) DESC,
DATE(wp_posts.post_date) desc;
Which gives me the following output noticte thatthe posts entered at different dates with either 1 or 3 show up in sequence, ideally i want the latest ones to display directly after 14 so it starts over again. the number 14 should not be static either as if someone adds another option to the select then it will increase and decrease if an option is removed.
GROUP BY is confusingly named. It only makes sense when there's a SUM() or COUNT() or some such function in the SELECT clause. It's not useful here.
The canonical way of getting a post_meta.value into a result set of post items is this. You're close but this makes it easier to read.
SELECT SQL_CALC_FOUND_ROWS
ga.meta_value gallery_area,
p.*
FROM wp_posts p
LEFT JOIN wp_postmeta ga ON p.ID = ga.post_id AND ga.meta_key = 'gallery_area'
WHERE 1=1
AND p.post_status IN ('publish', 'private')
AND p.post_type = 'projectgallery'
Notice the two parts of the ON clause in the JOIN. That way of doing the SQL gets you just the meta_key value you want cleanly.
So, that's your result set. You'll get a row for every post. If the metadata is missing, you'll get a NULL value for gallery_area.
Then you have to order the result set the way you want. First order by date, then order by gallery_area, like so:
ORDER BY DATE(p.post_date) DESC,
0+gallery_area ASC
The 0+value trick is sql shorthand for casting the value as an integer.
Edit. Things can get fouled up if the meta_value items contain extraneous characters like leading spaces. Try diagnosing with these changes. Put
DATE(p.post_date) pdate,
0+ga.meta_value numga,
ga.meta_value gallery_area
in your SELECT clause. If some of the numga items come up zero, this is your problem.
Also try
ORDER BY DATE(p.post_date) DESC,
0+TRIM(gallery_area) ASC
in an attempt to get rid of the spaces. But they might not be spaces.

ORDER BY mysql comma separated values

Hi i am trying to order my database by comma separated value but it doesn't seem to be working correctly for me. here is my mysql query.
SELECT *
FROM wp_posts
LEFT JOIN wp_postmeta ON wp_posts.ID=wp_postmeta.post_id
LEFT JOIN wp_term_relationships ON wp_posts.ID=wp_term_relationships.object_id
LEFT JOIN wp_term_taxonomy ON wp_term_relationships.term_taxonomy_id=wp_term_taxonomy.term_id
LEFT JOIN wp_terms ON wp_term_taxonomy.term_id=wp_terms.term_id
WHERE wp_terms.name = 'Dimmers'
AND meta_key = 'feature_number_of_channels'
GROUP BY wp_posts.ID
ORDER BY meta_value ASC
And here is a screenshot.
As you can see from the screenshot they dont seem to be working correctly any help please on how to order these ASC correctly???
Correct order i am looking for is so the highest out of them all then ASC like this
24,48 - 4,6,12,24 - 12 - 6 - 6 - 3
Change your ORDER BY to look at the integer value of the string :
ORDER BY CONVERT(SUBSTRING_INDEX(meta_value, ',', -1), SIGNED) DESC
Here's an example:
SELECT "24,38" AS meta_value UNION
SELECT 1 AS meta_value UNION
SELECT 30 AS meta_value
ORDER BY CONVERT(SUBSTRING_INDEX(meta_value, ',', -1), SIGNED) DESC
returns:
Rows = 3
meta_value
24,38
30
3
Good luck!
Using order table.field ASC might help you as there are too many tables joined. That way the server would take the exact order from one single table.

Inefficient SQL

I'm no MySQL expert, but I've managed until now to hack together something that works. Unfortunately, my latest bodged attempt results in the server dying, so obviously I'm doing something that is massively inefficient. Can anyone give me a hint as to where the problem is and how I might get the same results without bringing the whole site down everytime?
$sqlbest = "SELECT
wp_postmeta.meta_value
, wp_posts.post_title
, wp_posts.ID
, (TO_DAYS(CURDATE())- TO_DAYS(wp_posts.post_date))+1 AS days
FROM `wp_postmeta` , `wp_posts`
WHERE `wp_postmeta`.`post_id` = `wp_posts`.`ID`
AND `wp_posts`.`post_date` >= DATE_SUB( CURDATE( ) , INTERVAL 1 WEEK)
AND `wp_postmeta`.`meta_key` = 'views'
AND `wp_posts`.`post_status` = 'publish'
AND wp_posts.ID != '".$currentPostID."'
GROUP BY `wp_postmeta`.`post_id`
ORDER BY (CAST( `wp_postmeta`.`meta_value` AS UNSIGNED ) / days) DESC
LIMIT 0 , 4";
$results = $wpdb->get_results($sqlbest);
It uses a post views count to calculate views/day for posts published in the last, then orders them by that number, and grabs the top 4.
I think I see that it's inefficient in that it has to calculate that views/day everytime for a few thousand posts, but I don't know how to do it any better.
Thanks in advance.
You could eliminate the need to call those date functions every time by either passing them statically into the query from your PHP server (which may not be synced with your database) or you can instead write a stored procedure and save the results of those date functions to variables that will then be used in the query.
SELECT
wp_postmeta.meta_value
, wp_posts.post_title
, wp_posts.ID
, DATEDIFF(CURDATE(),wp_posts.post_date)+1 AS days <<--1: DATEDIFF
FROM wp_postmeta
INNER JOIN wp_posts ON (wp_postmeta.post_id = wp_posts.ID) <<--2: explicit join
WHERE wp_posts.post_date >= DATE_SUB( CURDATE( ) , INTERVAL 1 WEEK)
AND wp_postmeta.meta_key = 'views'
AND wp_posts.post_status = 'publish'
AND wp_posts.ID != '".$currentPostID."'
AND wp_postmeta.meta_value > 1 <<-- 3: extra filter
/*GROUP BY wp_postmeta.post_id */ <<-- 4: group by not needed
ORDER BY (CAST( wp_postmeta.meta_value AS UNSIGNED ) / days) DESC
LIMIT 0 , 4;
I've tried to make a few changes.
Replaced the two calls to TO_DAYS with one call to DATEDIFF.
Replaced the ugly implicit where join with an explicit inner join this does not do anything, just makes things clearer. One thing it shows, if wp_postmeta.post_id is unique, then you do not need the group by, because the inner join will only give one row per wp_postmeta.post_id.
Added an extra filter to filter out the posts with a low view count, this limits the amount of rows MySQL has to sort.
Eliminated group by this is only right if wp_postmeta.post_id is unique!