Getting number of purchased items using SQL - Woocommerce - mysql

I'm stuck in this query for while and any type of help would be appreciated. This is for a wordpress website which uses woocommerce.
Trying to get purchase details of customers and I need to get a result set which should contain following fields.
Email | First Name | Orders | Items Purchased | Order Total
I've written the following SQL for this purpose. achg8 is the table prefix. An order may have one or more items.
SELECT
pm1.meta_value as email ,
pm2.meta_value as first_name,
sum(pm3.meta_value) as total,
count(posts.ID) as orders ,
count(items.order_item_id) as items
from achg8_posts as posts
left join achg8_postmeta as pm1 on posts.ID = pm1.post_id
left join achg8_postmeta as pm2 on posts.ID = pm2.post_id
left join achg8_postmeta as pm3 on posts.ID = pm3.post_id
left join achg8_woocommerce_order_items as items on items.order_id = posts.ID
WHERE
posts.post_type = 'shop_order' and
posts.post_status = 'wc-processing' and
pm1.meta_key='_billing_email' and
pm2.meta_key='_billing_first_name' and
pm3.meta_key='_order_total'
GROUP by email
The problem is order number and item numbers are always equal. but that's not the case actually.
Need and insight on what i'am doing wrong.
UPDATE
edited the sql to the following as per #matigo's comment. The problem now is the order total gets added up multiple times. For an example if an order has two items (i.e two rows in the joined resultset) order-total gets added up twice.
SELECT
pm1.meta_value as email ,
pm2.meta_value as first_name,
sum(pm3.meta_value) as total,
count(DISTINCT posts.ID) as orders ,
count(items.order_item_id) as items
from achg8_posts as posts
left join achg8_postmeta as pm1 on posts.ID = pm1.post_id
left join achg8_postmeta as pm2 on posts.ID = pm2.post_id
left join achg8_postmeta as pm3 on posts.ID = pm3.post_id
left join achg8_woocommerce_order_items as items on items.order_id = posts.ID
WHERE
posts.post_type = 'shop_order' and
posts.post_status = 'wc-processing' and
pm1.meta_key='_billing_email' and
pm2.meta_key='_billing_first_name' and
pm3.meta_key='_order_total'
GROUP by email

Assuming that the same order number can appear in items multiple times, it looks like you need a DISTINCT in your COUNT.
Try this:
SELECT pm1.`meta_value` as `email`,
pm2.`meta_value` as `first_name`,
SUM(pm3.`meta_value`) as `total`,
COUNT(posts.`ID`) as `orders`,
(SELECT COUNT(z.`order_item_id`)
FROM `achg8_woocommerce_order_items` z
WHERE posts.`ID` = z.`order_id`) as `items`
FROM `achg8_posts` posts INNER JOIN `achg8_postmeta` pm1 ON posts.`ID` = pm1.`post_id`
INNER JOIN `achg8_postmeta` pm2 ON posts.`ID` = pm2.`post_id`
INNER JOIN `achg8_postmeta` pm3 ON posts.`ID` = pm3.`post_id`
WHERE posts.`post_type` = 'shop_order'
and posts.`post_status` = 'wc-processing'
and pm1.`meta_key` = '_billing_email'
and pm2.`meta_key` = '_billing_first_name'
and pm3.`meta_key` = '_order_total'
GROUP by `email`, `first_name`
This should give you exactly what you're looking for 👍🏻

Can you try this and see if it works.
Sorry, I removed that joins without considering the relation with meta_key, can you try this.
SELECT
pm1.`meta_value` as `email`,
pm2.`meta_value` as `first_name`,
SUM(pm3.`meta_value`) as `total`,
COUNT(distinct posts.`ID`) as `orders`,
COUNT(distinct items.order_item_id) as `items`
FROM `achg8_posts` posts
INNER JOIN `achg8_postmeta` pm1 ON posts.`ID` = pm1.`post_id`
INNER JOIN `achg8_postmeta` pm2 ON posts.`ID` = pm2.`post_id`
INNER JOIN `achg8_postmeta` pm3 ON posts.`ID` = pm3.`post_id`
INNER JOIN achg8_woocommerce_order_items as items on items.order_id = posts.ID
WHERE
posts.`post_type` = 'shop_order' and
posts.`post_status` = 'wc-processing' and
pm1.`meta_key` = '_billing_email' and
pm2.`meta_key` = '_billing_first_name' and
pm3.`meta_key` = '_order_total'
GROUP by `email`

Related

Woocommerce Mysql query to fetch products and varables

Trying to fetch products ID and the products variable ID in Mysql.
My category is 7006.
This query gives me only the general product, and not the product variables values.
SELECT post.ID, post.post_title, metavalue1.meta_value AS MetaValue1, metavalue2.meta_value AS MetaValue2
FROM wp_posts post
LEFT JOIN wp_postmeta metavalue1 ON post.ID = metavalue1.post_id
AND '_enable_colorlab' = metavalue1.meta_key
LEFT JOIN wp_postmeta metavalue2 ON post.ID = metavalue2.post_id
AND '_wcpa_product_meta' = metavalue2.meta_key
LEFT JOIN wp_term_relationships rs ON rs.object_id = post.ID
WHERE rs.term_taxonomy_id ='7006';
This query gives me all variables ID i need
SELECT post.ID, post.post_title FROM wp_posts post
INNER JOIN wp_postmeta pa ON pa.post_id = post.ID
INNER JOIN wp_term_relationships rs ON rs.object_id = post.ID
WHERE rs.term_taxonomy_id ='7006';
How can i get first query to include all product and variables values and not the general product value?
IT will return variation products with variant id.
SELECT wp.id AS `Product Id`, wpv.id AS `Variant Id`, wp.post_title, wpv.post_title, wpv.post_excerpt
FROM wp_posts wp
INNER JOIN wp_term_relationships r ON wp.ID = r.object_id
INNER JOIN wp_term_taxonomy tt ON r.term_taxonomy_id = 7006
INNER JOIN wp_terms t ON t.term_id = tt.term_id
INNER JOIN wp_posts wpv ON wp.id = wpv.post_parent
LEFT JOIN wp_postmeta wpm ON wp.ID = wpm.post_id
WHERE tt.taxonomy = 'product_type'
AND t.name = 'variable'
AND wpv.post_type != 'attachment'
AND wpm.meta_key = '_enable_colorlab'
You can try something like this:
select
p.id,
p.post_title,
group_concat(concat(m.meta_key, ':', m.meta_value))
from
wp_posts as p
left join wp_postmeta as m on m.post_id = p.id
left join wp_term_relationships rs on rs.object_id = p.id
where
rs.term_taxonomy_id = '7006'
group by
p.id,
p.post_title
In PHP you can explode concatenated string of values

My SQL query pulls inaccurate information about variable woocommerce products, but works fine for regular products. Why?

I have a rather long and complicated sql query that I use to pull product reports from Woocommerce (because the browser reports do not contain enough information for my superiors' liking) For the most part, it works wonderfully. But it gives me incorrect/unreliable data for variable products, and I am not sure why this is.
Query follows:
SELECT p.ID, b.meta_value, p.post_title, pv.meta_value, SUM( qty.meta_value ), var.meta_value, "CR [4/3]", SUM( tot.meta_value ), "Avg Prc [6/4] 3?", pr.meta_value, AVG(aic.meta_value) , "Grs Prft [(8-9)/8]", "GrAvPrft [(7-9)/7]", "Grs Markup [(8-9)/8]"
FROM wp_posts p INNER JOIN wp_woocommerce_order_itemmeta pid ON p.ID = pid.meta_value AND pid.meta_key = '_product_id'
INNER JOIN wp_woocommerce_order_itemmeta qty ON pid.order_item_id = qty.order_item_id AND qty.meta_key = '_qty'
INNER JOIN wp_woocommerce_order_itemmeta tot ON tot.order_item_id = qty.order_item_id AND tot.meta_key = '_line_total'
LEFT JOIN wp_woocommerce_order_itemmeta var ON var.order_item_id = qty.order_item_id AND var.meta_key = '_variation_id'
LEFT JOIN wp_woocommerce_order_itemmeta shp ON shp.order_item_id = qty.order_item_id AND shp.meta_key = '_per_product_shipping_cost'
LEFT JOIN wp_postmeta pr ON pr.post_id = p.ID AND pr.meta_key = '_price'
LEFT JOIN wp_postmeta b ON b.post_id = p.ID and b.meta_key = '_bucket'
LEFT JOIN wp_postmeta pv ON p.ID = pv.post_id and pv.meta_key = '_page_views'
LEFT JOIN wp_woocommerce_order_itemmeta aic ON aic.order_item_id = pid.order_item_id and aic.meta_key='_actual_item_cost'
INNER JOIN wp_woocommerce_order_items oim ON oim.order_item_id = pid.order_item_id
INNER JOIN wp_posts ord ON oim.order_id = ord.ID
WHERE p.post_type = 'product'
AND oim.order_item_type = 'line_item'
AND tot.meta_value > 0
AND ord.post_status IN ('wc-completed', 'wc-processing')
GROUP BY p.ID;
Some of the stuff in the select statement is just placeholders for post-query calculations, just ignore those. My main issue is that SUM( qty.meta_value ) and SUM( tot.meta_value ) give incorrect data for variable products.

Selecting posts together with optional taxonomy

I want to select all (WordPress) posts together with the eventual brand and brand_id (both from the same taxonomy). If there is no brand set, the post should be selected anyway with these two fields set as NULL.
This query does almost work, but it's excluding posts that are missing a brand:
SELECT
p.ID AS post_id,
p.post_title AS title,
p.post_content AS body,
brand.name AS brand,
brand.term_id AS brand_id,
UNIX_TIMESTAMP(p.post_date) AS date_added
FROM
wp_posts AS p
INNER JOIN wp_term_relationships AS brand_rel
ON brand_rel.object_id = p.ID
INNER JOIN wp_term_taxonomy AS brand_tax
ON brand_tax.term_taxonomy_id = brand_rel.term_taxonomy_id
AND brand_tax.taxonomy = "product_brand"
INNER JOIN wp_terms AS brand
ON brand.term_id = brand_tax.term_id
WHERE
p.post_type = 'product' AND
p.post_status = 'publish'
GROUP BY post_id;
Changing from INNER JOIN to LEFT JOIN, I get all the posts - but none of their brands (both brand and brand_id are NULL):
SELECT
p.ID AS post_id,
p.post_title AS title,
p.post_content AS body,
brand.name AS brand,
brand.term_id AS brand_id,
UNIX_TIMESTAMP(p.post_date) AS date_added
FROM
wp_posts AS p
LEFT JOIN wp_term_relationships AS brand_rel
ON brand_rel.object_id = p.ID
LEFT JOIN wp_term_taxonomy AS brand_tax
ON brand_tax.term_taxonomy_id = brand_rel.term_taxonomy_id
AND brand_tax.taxonomy = "product_brand"
LEFT JOIN wp_terms AS brand
ON brand.term_id = brand_tax.term_id
WHERE
p.post_type = 'product' AND
p.post_status = 'publish'
GROUP BY post_id;
If I however remove the GROUP BY statement during the LEFT JOIN I seem to get all the posts with and without a brand as I should, but also with alot of duplicates where most of the brand and brand_id fields are NULL.
All help appreciated.

mySql Distinct - group by issue

I'm trying to find out the most purchased products but to only count distinct users ids. Basically my client wants to stop duplicate purchases from the same user, so that they can't affect the chart/best sellers.
I need to count all order_items for that product, using only Distinct users ids. Currently the results are counting all order_items so the Distinct isn't working.
Any help and I would be grateful.
Thanks in advance
SELECT *
FROM
( SELECT DISTINCT
order_item_meta_3.meta_value as distinct_user_order_items_id,
order_item_meta_2.meta_value as product_id,
SUM( order_item_meta.meta_value ) as item_quantity
FROM
wp_woocommerce_order_items as order_items
LEFT JOIN wp_woocommerce_order_itemmeta as order_item_meta
ON order_items.order_item_id = order_item_meta.order_item_id
LEFT JOIN wp_woocommerce_order_itemmeta as order_item_meta_2
ON order_items.order_item_id = order_item_meta_2.order_item_id
LEFT JOIN wp_woocommerce_order_itemmeta as order_item_meta_3
ON order_items.order_item_id = order_item_meta_3.order_item_id
LEFT JOIN wp_posts AS posts
ON order_items.order_id = posts.ID
LEFT JOIN wp_term_relationships AS rel
ON posts.ID = rel.object_ID
LEFT JOIN wp_term_taxonomy AS tax
USING( term_taxonomy_id )
LEFT JOIN wp_terms AS term
USING( term_id )
WHERE
posts.post_type = 'shop_order'
AND posts.post_status = 'publish'
AND tax.taxonomy = 'shop_order_status'
AND term.slug IN ('completed','processing','on-hold')
AND order_items.order_item_type = 'line_item'
AND order_item_meta.meta_key = '_qty'
AND order_item_meta_2.meta_key = '_product_id'
AND order_item_meta_3.meta_key = '_user_id'
GROUP BY
order_item_meta_2.meta_value
ORDER BY
item_quantity DESC ) as order_table,
wp_posts
LEFT JOIN wp_postmeta as mk1
ON wp_posts.ID = mk1.post_id
LEFT JOIN wp_postmeta as mk2
ON wp_posts.ID = mk2.post_id
WHERE
order_table.product_id = wp_posts.ID
AND wp_posts.ID = mk1.post_id
AND mk1.meta_key = 'is_album'
AND mk1.meta_value = 0
AND mk2.meta_key = '_price'
AND mk2.meta_value = 0
Wouldn't you need to do the sum outside the group by. you have to materialize the distinct set first then aggregate it...
so change first few lines to...
SELECT distinct_user_order_items_Id, product_Id, sum(item_Quantity) as item_Quantity
FROM (
SELECT
order_item_meta_3.meta_value as distinct_user_order_items_id,
order_item_meta_2.meta_value as product_id,
order_item_meta.meta_value as item_quantity

WooCommerce query order totals grouped by users with specific role

I need to list all users with a specific role (custom role 'forhandler') and for each show their totals (spent) on published orders that have status 'completed', 'processing' and 'on-hold', along with a count of the number of orders per user (also published and status as above). The result must be sorted with the highest totals first.
I managed to write a SQL query that works (and that's the limit of my SQL knowledge), but it takes 20 seconds+ when running on production data (1200 orders and increasing). Can this query be rewritten to something more efficient (or simply better)?
SELECT users.ID, users.display_name,
(SELECT COUNT(posts.ID)
FROM $wpdb->posts AS posts
LEFT JOIN $wpdb->postmeta ON $wpdb->postmeta.post_id = posts.ID
LEFT JOIN $wpdb->term_relationships AS rel ON posts.ID=rel.object_ID
LEFT JOIN $wpdb->term_taxonomy AS tax USING( term_taxonomy_id )
LEFT JOIN $wpdb->terms AS term USING( term_id )
WHERE $wpdb->postmeta.meta_key = '_customer_user'
AND $wpdb->postmeta.meta_value = users.ID
AND posts.post_type = 'shop_order'
AND posts.post_status = 'publish'
AND tax.taxonomy = 'shop_order_status'
AND term.slug IN ('completed', 'processing', 'on-hold')
) AS numOrders,
(
SELECT SUM(postmeta.meta_value)
FROM $wpdb->postmeta AS postmeta
LEFT JOIN $wpdb->posts AS pp ON pp.ID = postmeta.post_id
LEFT JOIN $wpdb->postmeta AS pm2 ON pm2.post_id = postmeta.post_id
LEFT JOIN $wpdb->term_relationships AS rel ON pp.ID=rel.object_ID
LEFT JOIN $wpdb->term_taxonomy AS tax USING( term_taxonomy_id )
LEFT JOIN $wpdb->terms AS term USING( term_id )
WHERE postmeta.meta_key = '_order_total'
AND pm2.meta_key = '_customer_user'
AND pm2.meta_value = users.ID
AND tax.taxonomy = 'shop_order_status'
AND term.slug IN ('completed', 'processing', 'on-hold')
AND pp.post_type = 'shop_order'
AND pp.post_status = 'publish'
) AS totalsOfOrders
FROM wp_users AS users
INNER JOIN wp_usermeta ON users.ID = wp_usermeta.user_id
WHERE wp_usermeta.meta_key = 'wp_capabilities'
AND wp_usermeta.meta_value LIKE '%forhandler%'
ORDER BY totalsOfOrders DESC