wordpress query custom fields and category - php

I have a query that creates a table view and then another that queries the view. The results are extremely slow.
Here is the code:
create or replace view $view_table_name as select * from wp_2_postmeta where post_id IN (
select ID FROM wp_2_posts wposts
LEFT JOIN wp_2_term_relationships ON (wposts.ID = wp_2_term_relationships.object_id)
LEFT JOIN wp_2_term_taxonomy ON (wp_2_term_relationships.term_taxonomy_id = wp_2_term_taxonomy.term_taxonomy_id)
WHERE wp_2_term_taxonomy.taxonomy = 'category'
AND wp_2_term_taxonomy.parent = $cat || wp_2_term_taxonomy.term_id = $cat
AND wposts.post_status = 'publish'
AND wposts.post_type = 'post')
The $values have been put it in for this example that queries the view table for the results.
select distinct(ID)
FROM $view_table_name wposts
LEFT JOIN wp_2_postmeta wpostmeta
ON wposts.ID = wpostmeta.post_id
WHERE post_status = 'publish'
AND ID NOT IN (SELECT post_id
FROM wp_2_postmeta
WHERE meta_key = '$var' && meta_value = '$value1')
AND ID NOT IN (SELECT post_id
FROM wp_2_postmeta
WHERE meta_key = '$var' && meta_value = '$value2')
AND ID NOT IN (SELECT post_id
FROM wp_2_postmeta
WHERE meta_key = '$var' && meta_value = '$value3')
AND postmeta.meta_key = 'pd_form'
ORDER BY CASE wpostmeta.meta_value
WHEN '$value5' THEN 1
WHEN '$value6' THEN 2
WHEN '$value7' THEN 3
WHEN '$value8' THEN 4
WHEN '$value9' THEN 5
WHEN '$value10' THEN 6
WHEN '$value11' THEN 7
WHEN '$value11' THEN 8
END;

The main problem here is a subquery in IN condition. Instead executing the subquery and then checking in the outer table for correspondences, MySQL is known to transform the query into a correlated subquery which is executed for each row in the outer table.
The usual solution is to get rid of the subquery in the IN in favour of a JOIN.
Another problem is that you use OUTER JOIN instead of inner JOIN though you do not actually need it (MySQL is usually smart enough to optimize it when it is trivial, but anyway you should express your intention more clearly).
And one more thing. Both queries seem to be dynamically generated. Beside optimizing the query itself, one should think how not to break the calling code. That may be tricky though.
Optimizing wordpress is always an interesting challenge.

Related

Moving the workload from PHP to MySQL

I currently have two queries that are executed in order. The first query pulls a few rows from a database and the 2nd query is executed using one value from those rows. I am only interested in the rows from the 1st query where I know the 2nd query is going to return a value higher than 0. I could potentially have thousands of rows in my first table, so I rather do the filtering in MySQL and not in PHP, I would assume it is faster.
The first query is as following
SELECT
*
FROM
wp_invoices
WHERE
deleted = 0
The 2nd query is as following:
SELECT COUNT(*) FROM wp_users A
INNER JOIN wp_usermeta B ON (A.ID = B.user_id)
LEFT JOIN wp_invoices_records C ON (A.ID = C.uid
AND C.invoice_id = %d
AND C.status != 100)
WHERE (B.meta_key = 'wp_capabilities' AND CAST(B.meta_value AS CHAR) LIKE '%%\"subscriber\"%%')
AND (C.uid IS NOT NULL)
Both queries work separately, this is not the problem. Currently, the %d in the 2nd query is replaced by PHP, which inserts the wp_invoices.id value for each row.
Ideally, I would like a query that returns only the rows from wp_invoices, where the 2nd query would return a value higher than 0. At this point I am not interested in the rows of the 2nd query, only the amount of rows found.
My table structures are as following: http://sqlfiddle.com/#!9/466cb
This is what I do in PHP, currently, to handle all this. Please keep in mind that this code was written to clarify this post, I am using deprecated MySQL functions because I don't write PHP that often anymore and haven't really looked into mysqli and I haven't checked if this code actually runs. It should give you a better indication of what I am currently doing.
<?php
/* Example code. I am not using mysql_ functions in my own solution, I just
know these functions by heart. Assume a database connection is already
established */
/* Refer to this as QUERY 1 */
$result = mysql_query("SELECT * FROM wp_invoices WHERE deleted = 0");
/* Loop over all invoices (this is not an invoice 'record'. These are
the invoice definitions, not the actual instances that people received */
while($row = mysql_fetch_assoc($result)) {
/* Refer to this as QUERY 2 */
$count_result = mysql_query(sprintf("
SELECT
COUNT(*) AS count
FROM wp_users A
INNER JOIN wp_usermeta B ON (A.ID = B.user_id)
LEFT JOIN wp_invoices_records C ON (A.ID = C.uid
AND C.invoice_id = %d
AND C.status != 100)
WHERE (B.meta_key = 'wp_capabilities'
AND CAST(B.meta_value AS CHAR) LIKE '%%\"subscriber\"%%')
AND (C.uid IS NOT NULL)", $row['id']));
if(mysql_result($count_result, 0) > 0) {
/* Here I would pull all data I would need for this invoice. $row
contains all the information about the invoice, $invoice_record
will contain all the information for this particular user about
this invoice*/
$invoice_records = mysql_query(sprintf("
SELECT
A.ID AS id,
A.display_name,
C.id AS received,
DATE_FORMAT(C.received_on, '%%d-%%m-%%Y') AS received_on,
C.status,
A.user_email,
C.price,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'first_name'
AND user_id = A.ID) AS first_name,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'tssnvgsl'
AND user_id = A.ID) AS insertion,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'last_name'
AND user_id = A.ID) AS last_name
FROM wp_users A
INNER JOIN wp_usermeta B ON (A.ID = B.user_id)
LEFT JOIN wp_invoices_records C ON (A.ID = C.uid
AND C.invoice_id = %d
AND C.status != 100)
WHERE (B.meta_key = 'wp_capabilities'
AND CAST(B.meta_value AS CHAR) LIKE '%%\"subscriber\"%%')
AND (C.uid IS NOT NULL)", $row['id']));
while($invoice_record = mysql_fetch_assoc($invoice_records)) {
/* Perform logic to see if this person should receive a reminder */
}
}
}
?>
What I attempt to achieve, though:
<?php
$result = mysql_query(/* here a query that pulls all rows from QUERY 1
where QUERY 2 would have returned a count higher than 2 */);
while($row = mysql_fetch_assoc($result)) {
/* Now here I should be 100% sure that every $row has at least one
"pending" invoice record, that has not yet been paid and has a
status of < 100 */
}
?>
Alright. I have been racking my brain staring at your process and I think it is starting to make sense. If I am not mistaken you should be able to eliminate both query1 and query2 and move the logic into the LEFT JOIN of your third query like such.
SELECT
A.ID AS id,
A.display_name,
C.id AS received,
DATE_FORMAT(C.received_on, '%%d-%%m-%%Y') AS received_on,
C.status,
A.user_email,
C.price,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'first_name'
AND user_id = A.ID) AS first_name,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'tssnvgsl'
AND user_id = A.ID) AS insertion,
(SELECT meta_value FROM wp_usermeta WHERE meta_key = 'last_name'
AND user_id = A.ID) AS last_name
FROM wp_users A
INNER JOIN wp_usermeta B ON (A.ID = B.user_id)
LEFT JOIN wp_invoices_records C ON (A.ID = C.uid
AND C.invoice_id IN (SELECT id FROM wp_invoices WHERE deleted = 0)
AND C.status != 100)
WHERE (B.meta_key = 'wp_capabilities'
AND CAST(B.meta_value AS CHAR) LIKE '%%\"subscriber\"%%')
AND (C.uid IS NOT NULL)"
This query, instead of having to be ran every time for every record and the $row['id'] added via php within your LEFT JOIN I am using MySQL IN functionality with a subselect. This gives you the ability to join only on records from wp_invoices which where deleted.

Mysql query ignoring the row if child row from left join table doesn't exists

Hi there so first of all , i'm not that expert in mysql queries. i have tow tables wp_posts and wp_wti_like_post and i'm doing LEFT JOIN on wp_wti_like_post ON wp_posts.ID = wp_wti_like_post.post_id and SUM(wp_wti_like_post.value) < 2 BUT if there is no rowin wp_wti_like_post with id of post from wp_posts then it doesn't show even the row from wp_posts and just ignore it , please help in this really need it.
query:
SELECT *
FROM wp_posts
LEFT JOIN wp_wti_like_post ON wp_posts.ID = wp_wti_like_post.post_id
WHERE wp_posts.post_status = 'publish'
GROUP BY wp_wti_like_post.post_id
HAVING SUM( wp_wti_like_post.value ) <2
OR SUM( wp_wti_like_post.value ) = NULL
LIMIT 0 , 200
table wp_wti_like_post
http://prntscr.com/6xixrd
table wp_posts
http://prntscr.com/6xixzp
You might need to restructure your query to as follows:
SELECT * FROM
(
SELECT WP.*, SUM(LP.value) AS `value`
FROM wp_posts WP
LEFT JOIN
(SELECT post_id FROM wp_wti_like_post WHERE post_status = 'publish') LP
ON WP.ID = LP.post_id
GROUP BY WP.ID
) T1
WHERE T1.value IS NULL
OR T1.value < 2;
The Inner-most query first fetches only wti_like_posts that have been published.
The left-join is then performed, which will give you the expected result of fetching all rows from wp_posts, even if they do not join with a record from the sub-query.
After that is performed, the GROUP statement performs the computation of SUMming the values.
The outer-most query enforces the requirement that the sum must either be less than 2 or be null.
So i sorted this out by my self and go it working in this way
at the place of
SUM( wp_wti_like_post.value ) = NULL
it should need to be
SUM( wp_wti_like_post.value ) IS NULL

Correct way to handle changeable queries in PDO?

I am currently building a news website that has features such as posting content, labeling the content into categories and voting.
I have gotten to the stage where I am making a home feed for my users, this will show the newest posts but I want to give my users the ability to sort by categories.
Here is my current query
SELECT p.*,
(SELECT Count(id)
FROM comments AS c
WHERE c.post = p.id) AS commentCount,
v.type AS vote_type,
(SELECT Count(id)
FROM votes AS vo
WHERE vo.post_id = p.id) AS totalVotes
FROM posts AS p
LEFT JOIN tagged AS t
ON p.id = t.post_id
LEFT JOIN votes AS v
ON v.post_id = p.id
WHERE (v.user_id = 1 OR v.user_id IS NULL)
AND t.tag_id = 7
ORDER BY timestamp DESC
LIMIT :limit OFFSET :offset
This query has the following parts of code that make it return posts that are only assigned (tagged) with the category id of 7 ( and without these two parts the query becomes the base query I normally use for newest posts)
LEFT JOIN tagged AS t
ON p.id = t.post_id
AND t.tag_id = 7
I was wondering if there is a way to have the base query without the category code and then if needed input the category code rather than have two querys? Is there a way to make a query 'dynamic'.
I want to implement other sorting features but feel creating a bunch of (slightly) different copies of each other would be inefficient.
Yes, people do this all the time. You can either dynamically set your parameters, such as in the first example below, or you can dynamically build your SQL statement. I would think best practice would be to: 1. Make sure it doesn't get too complicated. At the point that you have a query that is everything to everybody, look at building a query builder or multiple queries. 2. Combine dynamic SQL statements with dynamic parameters.
Prepare your statement:
SELECT p.*,
(SELECT Count(id)
FROM comments AS c
WHERE c.post = p.id) AS commentCount,
v.type AS vote_type,
(SELECT Count(id)
FROM votes AS vo
WHERE vo.post_id = p.id) AS totalVotes
FROM posts AS p
LEFT JOIN tagged AS t
ON p.id = t.post_id
LEFT JOIN votes AS v
ON v.post_id = p.id
WHERE (v.user_id = 1 OR v.user_id IS NULL)
AND t.tag_id = :tagid
ORDER BY timestamp DESC
LIMIT :limit OFFSET :offset
Now bind your element (pseudo code):
if( ! isset( $someValue ) ) $someValue = 7; // I am just giving it a default value for
// an example. You can do whatever.
pdo->bindParam( ':tagid', $someValue, parameterType such as PDO::PARAM_INT);
You can keep building like this, or you can break up your bigger SQL prepared statement. By way of example:
if( $categoryCodeNeeded === true ) // add another and statement to where portion of your sql statement
$select += " AND tag_id = 7 ";
Edit to answer the question in the comment:
The code that you provided: SELECT p.* is either assigned to a variable right now, or it is inside another piece of code that looks like this:
$statement = $dbHandler->prepare('SELECT p.* FROM posts p blah blah blah' );
If it's the second case, then you can change that line of code to read:
$statement = $dbHandler->prepare( $yourOwnSelectVariable );
Then, you can build your variable however you want.
$yourOwnSelectVariable = "SELECT p.* FROM posts p";
if( $categoryCodeNeeded )
$yourOwnSelectVariable += " WHERE tag_id = :tagid ";
$statement = $dbHandler->prepare( $yourOwnSelectVariable );
if( ! isset( $tagId ) ) $tagId = 7;
$statement->bindParam( ':tagid', $tagId, PDO::PARAM_INT);

SQL query with special conditions not working

please help me with my MySQL Query.
Basically I want to retrieve some users for a user-ranking-system. My project is based on a wordpress installation. But since my query is very specific, all wordpress methods and functions are useless.
I try to describe my query in words:
"Give me all Users, who have a minimum of three posts which have a minumum rating of 4 for each post. Furthermore each of the users post needs to have a minimum of 5 ratings."
(The date-conditions below in the query are unimportant, work perfectly)
For storing data, posts are saved as usual in wp_posts (special post_type). The amount of ratings for each post is stored with a meta value (key=rating_count). Each rating is stored in the table "wp_rating" (id, post_id, rating, user_id).
My Query looks like this:
SELECT SUM(r.rating) AS rating,
p.post_author AS user_id,
COUNT(r.rating) AS rating_count
FROM wp_rating r
INNER JOIN wp_posts p ON p.ID = r.post_id
INNER JOIN wp_postmeta meta ON meta.post_id = p.ID
WHERE p.post_status = 'publish'
AND (meta.meta_key = 'expiration_date'
AND meta.meta_value < DATE_FORMAT(NOW(), 'Ymd'))
AND (meta.meta_key = 'rating_count' AND meta.meta_value > 4)
AND MONTH(p.post_date) = 03
AND r.rating > 4
GROUP BY p.post_author
HAVING COUNT(p.ID) > 3
ORDER BY SUM(r.rating) DESC
The problem is the following line:
AND (meta.meta_key = 'rating_count' AND meta.meta_value > 4)
I dont get any results after firing this query. (I checked all posts twice for enough ratings)
I hope my description is okay for finding a solution.
Kind Regards,
Robin :-)
PS: Please help, Im very confused :(
At the same time column can be equal to one value meta.meta_key = 'expiration_date' or meta.meta_key = 'rating_count' how it is possible that at same time column can have these two values, i guess you have one to many relation between post and their meta you need another join to your wp_postmeta table to fullfill your second condition,and make sure you are comparing date to date type here meta.meta_value it smells like you have text type of meta.meta_value column
SELECT SUM(r.rating) AS rating,
p.post_author AS user_id,
COUNT(r.rating) AS rating_count
FROM wp_rating r
INNER JOIN wp_posts p ON p.ID = r.post_id
INNER JOIN wp_postmeta meta ON meta.post_id = p.ID
INNER JOIN wp_postmeta meta1 ON meta1.post_id = p.ID
WHERE p.post_status = 'publish'
AND (meta.meta_key = 'expiration_date'
AND meta.meta_value < DATE_FORMAT(NOW(), 'Ymd'))
AND (meta1.meta_key = 'rating_count' AND meta1.meta_value > 4)
AND MONTH(p.post_date) = 03
AND r.rating > 4
GROUP BY p.post_author
HAVING COUNT(p.ID) > 3
ORDER BY SUM(r.rating) DESC
i guess 'rating_count' you mean it the aliace you created before.
try that:
AND (meta.meta_key = COUNT(r.rating) AND meta.meta_value > 4)

Pull post from a specific Wordpress category

im looking to pull 3 posts from a specific Wordpress Category. At the minute I can pull 3 latest posts and display them in a flash banner using the code below.
SELECT yah_posts.*, yah_postmeta.*
FROM yah_posts
LEFT JOIN yah_postmeta ON yah_posts.ID = yah_postmeta.post_id
WHERE yah_postmeta.meta_key = 'largeimage' && yah_posts.post_status = 'publish'
ORDER BY post_date DESC LIMIT 3
I want to be able to pull 3 latest posts from a specific category instead of just 3 latest posts from every category.
I have put together this code below, but it doesn't seem to be working :(
$query = "SELECT yah_posts.*, yah_postmeta.*
FROM yah_posts
LEFT JOIN yah_postmeta ON yah_posts.ID = yah_postmeta.post_id
AND LEFT JOIN $yah_term_taxonomy ON($yah_term_relationships.term_taxonomy_id = $yah_term_taxonomy.term_taxonomy_id)
WHERE yah_postmeta.meta_key = 'largeimage' && yah_posts.post_status = 'publish'
AND $yah_term_taxonomy.term_id = '1'
AND $yah_term_taxonomy.taxonomy = 'category'
ORDER BY post_date DESC LIMIT 3";
There is actually a stack exchange site just focused on wordpress questions. Looking there and taking code from this question:
https://wordpress.stackexchange.com/questions/6533/modify-wordpress-sql-query-to-pull-from-within-a-category
You would need to add another JOIN:
LEFT JOIN $wpdb->term_taxonomy ON($wpdb->term_relationships.term_taxonomy_id = $wpdb->term_taxonomy.term_taxonomy_id)
And a couple more statements in your WHERE clause:
AND $wpdb->term_taxonomy.term_id = {term_id of the category you want}
AND $wpdb->term_taxonomy.taxonomy = 'category'
You can use wp_term_relationships in your query, take a look at wp_terms and wp_term_relationships tables in your database.

Categories