Is there a more efficient solution for this MySQL Query? - mysql

I am working on extracting and displaying data from a Wordpress DB to a mobile app for a customer and I am having a little trouble refining this query to be most efficient.
In wordpress, there are three tables that link the data I need to access
1. wp_posts - in this table there is the main post title, it's published status and the post type.
2. wp_postmeta - this table has all supplemental info related to the post id in the above table.
3. wp_p2p - this table has links to all the parent-child posts and their relationship.
Because of the volume of data in these tables, the query I currently have takes about 13 seconds to run, could you please take a look at this sqlfiddle and let me know what I could look at to improve it? The query in it's current form is not the end result, but improving it will improve my end result. I also need to add a search field on the "name" in the wp_postmeta table.
http://sqlfiddle.com/#!2/0e9e0/1
Any direction is appreciated, thank you!

If I understand correctly, you're looking for only child posts, in which case, the query below should be much faster:
SELECT wp_posts.id, post_title, post_status, post_type
FROM wp_posts
JOIN wp_postmeta ON (wp_posts.id = wp_postmeta.post_id)
LEFT JOIN wp_p2p ON (wp_posts.id = wp_p2p.p2p_from)
WHERE `post_status`='publish' AND `post_type`='merchant'
AND wp_p2p.p2p_from IS NULL
GROUP BY wp_posts.id
This query will be optimized to find where a match doesn't exist in the p2p table so that part will be much faster than how you're currently doing it. It looks like you can also remove the JOIN on wp_postmeta since you don't use it at all. Removing that JOIN would also make the GROUP BY redundant and removing it could help the performance a little. Removing the GROUP BY would also be a good practice since strictly you can't select non-aggregate fields that aren't in the GROUP BY clause, but MySQL provides for this functionality so the query will still work either way.

To begin with, you should join tables using INNER or OUTER JOIN syntax. So:
FROM wp_posts INNER JOIN wp_postmeta ON wp_posts.id = wp_postmeta.post_id
or perhaps you want an OUTER JOIN here?
Can you explain why you're doing a GROUP BY on wp_posts.id. Does this column not have unique values?

Related

How to Split Categories and Tags into Two Different Columns when Querying Wordpress Posts.?

I am trying to split the results I get from querying wp_terms.name into two columns, one containing the "Category" entries and the other the "Tags" to go along with the rest of the query I got out of creating an editable table with WPDATATABLES´s query generator (wordpress plugin for displaying front-end editable databases: https://wpdatatables.com)
Here's the code I managed to come up with:
SELECT wp_posts.`ID`,
wp_posts.`guid`,
wp_posts.`post_title`,
wp_posts.`post_content`,
wp_postmeta.`meta_value`,
wp_users.`display_name`,
wp_terms.`name`
FROM wp_users
INNER JOIN wp_posts
ON wp_posts.`post_type` = 'post'
AND wp_posts.`post_status` = 'publish'
AND wp_users.`ID` = wp_posts.`post_author`
INNER JOIN wp_postmeta
ON wp_postmeta.`meta_key` = 'user_submit_url'
AND wp_posts.`ID` = wp_postmeta.`post_id`
INNER JOIN wp_term_relationships
ON wp_postmeta.`post_id` = wp_term_relationships.`object_id`
INNER JOIN wp_term_taxonomy
ON wp_term_relationships.`term_taxonomy_id` = wp_term_taxonomy.`term_taxonomy_id`
INNER JOIN wp_terms
ON wp_term_taxonomy.`term_taxonomy_id` = wp_terms.`term_id`
Everything is looking good except for the splitting of categories and post_tags into two different columns.
I have tried to implement the solution found here:
Wordpress SQL: get post category and tags
But obviously to no success (also because I am forced to use WPDATATABLES´ query generator which is very picky when I make big changes to the code.)
I appreciate any kind suggestion whatsoever and thanks in advance for the patience (I'm a total newbie with some small, but very specific needs I am trying to figure out.)

Remove woocommerce products only from certain category parent ID

I am in need of some help writing a Mysql Query to remove Woocommerce products only from a certain parent category. The database contains over 20.000 products.
I found following query:
DELETE relations.*, taxes.*, terms.*
FROM wp_term_relationships AS relations
INNER JOIN wp_term_taxonomy AS taxes
ON relations.term_taxonomy_id=taxes.term_taxonomy_id
INNER JOIN wp_terms AS terms
ON taxes.term_id=terms.term_id
WHERE object_id IN (SELECT ID FROM wp_posts WHERE post_type='product')
I need to link this with wp_term_taxonomy parent id "7447" so only products and custom fields matching this parent id will be removed.The structure has 3 levels of subcategories, is this even possible in mysql? Because I can't find any way to relate the subcategories directly to the parent category.
Forgive me for asking but i am not really good in MYSQL Queries. There is nothing online about the requirement. Besides I think it will be good to know for future developers facing this issue.
Many thx!
Verify the result first by using a SELECT statement like
SELECT relations.*, taxes.*, terms.*
FROM wp_term_relationships AS relations
INNER JOIN wp_term_taxonomy AS taxes
ON relations.term_taxonomy_id=taxes.term_taxonomy_id
INNER JOIN wp_terms AS terms
ON taxes.term_id=terms.term_id
WHERE object_id IN (SELECT ID FROM wp_posts WHERE post_type='product')
AND taxes.parent = 7447 <-- Add this extra condition
The query did not remove products, only category information.. I ended up using:
https://wordpress.org/plugins/wp-e-commerce-store-toolkit/
Wich does the job.... Will take like 24 hours but it does what it should, removing products and leaving category structure untouched!
Thx for the help Rahul!

MySQL - how to speed up or change this query

I did not write this query. I am working on someone else's old code. I am looking into changing what is needed for this query but if I could simply speed up this query that would solve my problem temporarily. I am looking at adding indexes. when I did a show indexes there are so many indexes on the table orders can that also slow down a query?
I am no database expert. I guess I will learn more from this effort. :)
SELECT
orders.ORD_ID,
orders.ORD_TotalAmt,
orders.PAYMETH_ID,
orders.SCHOOL_ID,
orders.ORD_AddedOn,
orders.AMAZON_PurchaseDate,
orders.ORDSTATUS_ID,
orders.ORD_InvoiceNumber,
orders.ORD_CustFirstName,
orders.ORD_CustLastName,
orders.AMAZON_ORD_ID,
orders.ORD_TrackingNumber,
orders.ORD_SHIPPINGCNTRY_ID,
orders.AMAZON_IsExpedited,
orders.ORD_ShippingStreet1,
orders.ORD_ShippingStreet2,
orders.ORD_ShippingCity,
orders.ORD_ShippingStateProv,
orders.ORD_ShippingZipPostalCode,
orders.CUST_ID,
orders.ORD_ShippingName,
orders.AMAZON_ShipOption,
orders.ORD_ShipLabelGenOn,
orders.ORD_SHIPLABELGEN,
orders.ORD_AddressVerified,
orders.ORD_IsResidential,
orderstatuses.ORDSTATUS_Name,
paymentmethods.PAYMETH_Name,
shippingoptions.SHIPOPT_Name,
SUM(orderitems.ORDITEM_Qty) AS ORD_ItemCnt,
SUM(orderitems.ORDITEM_Weight * orderitems.ORDITEM_Qty) AS ORD_ItemTotalWeight
FROM
orders
LEFT JOIN orderstatuses ON
orders.ORDSTATUS_ID = orderstatuses.ORDSTATUS_ID
LEFT JOIN orderitems ON
orders.ORD_ID = orderitems.ORD_ID
LEFT JOIN paymentmethods ON
orders.PAYMETH_ID = paymentmethods.PAYMETH_ID
LEFT JOIN shippingoptions ON
orders.SHIPOPT_ID = shippingoptions.SHIPOPT_ID
WHERE
(orders.AMAZON_ORD_ID IS NOT NULL AND (orders.ORD_SHIPLABELGEN IS NULL OR orders.ORD_SHIPLABELGEN = '') AND orderstatuses.ORDSTATUS_ID <> 101 AND orderstatuses.ORDSTATUS_ID <> 40)
GROUP BY
orders.ORD_ID,
orders.ORD_TotalAmt,
orders.PAYMETH_ID,
orders.SCHOOL_ID,
orders.ORD_AddedOn,
orders.ORDSTATUS_ID,
orders.ORD_InvoiceNumber,
orders.ORD_CustFirstName,
orders.ORD_CustLastName,
orderstatuses.ORDSTATUS_Name,
paymentmethods.PAYMETH_Name,
shippingoptions.SHIPOPT_Name
ORDER BY
orders.ORD_ID
One simple thing you should consider is whether you really need to use left joins or you would be satisfied using inner joins for some of the joins. the new query would not be the same as the original query, so you would need to think carefully about what you really want back. If your foreign key relationships are indexed correctly, this could help substantially, especially between ORDERS and ORDERITEMS, because I would imagine these are your largest tables. The following post has a good explanation: INNER JOIN vs LEFT JOIN performance in SQL Server. There are lots of other things that can be done, but you will need to post the query plan so people can dive deeper.
It looks like just adding the index was all that was needed.
create index orderitems_ORD_ID_index on orderitems(ORD_ID);

Populate 'temporary' columns with corresponding values during MySQL join query, also limit

I'm doing several MySQL joins to get template variables (i.e. custom fields) and their values (in MODX Evo but it's irrelevant - this is a general MySQL query).
I'm looking ideally to be able to create 2 temporary columns in order to use SORT BY in the query, or something to this effect. I'd like to populate the values for 'event_date' and 'event_featured' for their corresponding id's in these new columns - then I could then sort the results by these columns.
On a very related note I would like to limit the results to 20 for each unique id, not for each row as would happen if I added LIMIT- it would crop the below result to the . Can this be accomplished at the same time?
Anybody know how / if these are possible? Many thanks in advance.
Code and image of the results below:
SELECT DISTINCT
content.id, content.pagetitle, content.template , content.published,
templates.templatename,
tv_props.name,
tv_values.value
FROM `modx_site_content` AS `content`
LEFT JOIN `modx_site_templates` AS `templates` ON content.template=templates.id
LEFT JOIN `modx_site_tmplvar_templates` AS `template_tvs` ON templates.id=template_tvs.templateid
LEFT JOIN `modx_site_tmplvars` AS `tv_props` ON template_tvs.tmplvarid=tv_props.id
LEFT JOIN `modx_site_tmplvar_contentvalues` AS `tv_values` ON template_tvs.tmplvarid=tv_values.tmplvarid
WHERE templates.id=89
AND (
tv_props.name='event_featured'
OR tv_props.name='event_link_through'
OR tv_props.name='event_title'
OR tv_props.name='event_date'
OR tv_props.name='event_date_text'
OR tv_props.name='event_short_description'
OR tv_props.name='event_list_image'
);
Link to full-size image
You're going to need a couple of virtual tables, also known as subqueries, to retrieve these two properties of events from your name/value table. The generic name for this kind of query is a "pivot," for your information.
The mental knack is to think of the subquery as a virtual table which you can use in a surrounding query. The subquery for event_date looks like this, I believe.
SELECT content.id AS id,
tv_values.value AS event_date
FROM modx_site_content AS content
LEFT JOIN modx_site_templates AS templates
ON content.template=templates.id
LEFT JOIN modx_site_tmplvar_templates AS template_tvs
ON templates.id=template_tvs.templateid
LEFT JOIN modx_site_tmplvars AS tv_props
ON template_tvs.tmplvarid=tv_props.id
LEFT JOIN modx_site_tmplvar_contentvalues AS tv_values
ON template_tvs.tmplvarid=tv_values.tmplvarid
WHERE tv_props.name = 'event_date'
This little query produces a resultset that's a table relating content id to event date. I honestly don't understand your schema well enough to know if there's just one event date for each content id, so you might need to adjust this query to SELECT more columns. As you debug this, you should try out the subquery and make sure it's giving the results you hope for.
Then, when you're sure the subquery is OK, you join that subquery into your overall query, generically like so.
SELECT DISTINCT
content.id, event_date.event_date, templates.column,
table.column, table.colum, etc, etc
FROM modx_site_content AS content
LEFT JOIN table ON condition
LEFT JOIN (
SELECT content.id AS id,
tv_values.value AS event_date
FROM modx_site_content AS content
LEFT JOIN modx_site_templates AS templates
ON content.template=templates.id
LEFT JOIN modx_site_tmplvar_templates AS template_tvs
ON templates.id=template_tvs.templateid
LEFT JOIN modx_site_tmplvars AS tv_props
ON template_tvs.tmplvarid=tv_props.id
LEFT JOIN modx_site_tmplvar_contentvalues AS tv_values
ON template_tvs.tmplvarid=tv_values.tmplvarid
WHERE tv_props.name = 'event_date'
) AS event_date ON event_date.id = content.id
LEFT JOIN etc, etc, etc.
WHERE etc etc etc
Do you see how that goes? You can use tablename AS table or (some query) AS table interchangeably. You can also define a VIEW in your schema that provides the same data, and name it in your query. That's a handy way to make your queries less hairy.
By the way, you'll boost performance if you change
AND (
tv_props.name='event_featured'
OR tv_props.name='event_link_through'
OR tv_props.name='event_title' etc )
to
AND tv.props.name IN ('event_featured',
'event_link_through',
'event_title', etc)
You've probably noticed I'm a bit of a stickler for indentation in SQL queries. I find this helpful; I often find mistakes while I'm fixing up the indentation. Your practice may vary.

SQL joining wordpress tables

I am struggling with an SQL query to join 3 tables to return specific results.
Anyone familiar with wordpress may be able to assist as I am using word press to power post interactions with the db behind the scenes but am building a custom UI. The three tables are: -
posts (**ID**, post_title, post_content, post_modified_gmt)
term_relationships (**object_id**, term_taxonomy)
terms (**term_id**, name, slug)
I have got as far as..
SELECT posts.post_title FROM posts
INNER JOIN term_relationships
ON posts.ID = term_relationships.object_id
Which returns a list of all the post titles that have a matching ID number in term_relationships. However term names are in the table 'terms' and the ID's don't match the other two tables. In the terms table the term_id refers to the name of the term, e.g
term_id = 2, name = blog
Basically I am trying to achieve a query whereby if I set the term_id = 2 it returns all the rows from the table posts that have the term relationship to blog, meaning the query returns all blog posts and I am completely lost!
Can anyone give me a few pointers? my mind is boggled.
I managed to achieve the result I was looking for with the following query, but would still appreciate some pointers should anyone stumble upon this and have the inclination :)
SELECT * FROM posts
INNER JOIN term_relationships
ON posts.ID = term_relationships.object_id
WHERE term_relationships.term_taxonomy_id = 2
AND posts.post_type = 'post'
AND posts.post_status = 'publish'
ORDER BY posts.post_modified_gmt ASC
LIMIT 0,5