Need help with a mysql query - mysql

I've developed a mysql query that tells me how many times each product was purchased over a specified timeframe. I'm trying to figure out how to join this with my pageview tracking table so I can calculate the conversion rate for each product. Here is the original query:
SELECT SUM( op.products_quantity ) AS num_sold, SUM( op.final_price * op.products_quantity ) AS total_sales, p.products_model, pd.products_name, p.products_id
FROM orders_products AS op
JOIN products AS p
JOIN products_description AS pd
JOIN orders as o
WHERE p.products_id = op.products_id
AND p.products_id = pd.products_id
AND op.orders_id = o.orders_id
AND o.date_purchased BETWEEN '2011-01-15' AND '2011-04-15"'
GROUP BY p.products_id
ORDER BY total_sales DESC
I have another query that gives me the page views per product:
SELECT pv.products_id, count( pv.timestamp )
FROM products_visits AS pv
WHERE pv.timestamp
BETWEEN '2011-01-15'
AND '2011-04-17'
GROUP BY products_id
The caveat is that the views data has just started being collected, so we want return results even if a given product_id is not in the views table, but is in the purchases table.
How do I combine those queries into a single query?

You need a LEFT OUTER JOIN against a subquery. I think this will do the job:
SELECT
SUM( op.products_quantity ) AS num_sold,
SUM( op.final_price * op.products_quantity ) AS total_sales,
p.products_model, pd.products_name,
p.products_id,
visits.visits
FROM orders_products AS op
JOIN products AS p
JOIN products_description AS pd
JOIN orders as o
LEFT OUTER JOIN (
SELECT pv.products_id, count( pv.timestamp ) AS visits
FROM products_visits AS pv
WHERE pv.timestamp BETWEEN '2011-01-15' AND '2011-04-17'
GROUP BY products_id
) visits ON p.products_id = visits.products_id
WHERE p.products_id = op.products_id
AND p.products_id = pd.products_id
AND op.orders_id = o.orders_id
AND o.date_purchased BETWEEN '2011-01-15' AND '2011-04-15"'
GROUP BY p.products_id
ORDER BY total_sales DESC

You will need to use a LEFT JOIN: "first" you get all products and their sales data, and then you join the views data, if it exists.
try this:
SELECT p.products_id,
count( pv.timestamp ) AS views,
p.products_model AS model,
SUM( op.products_quantity ) AS num_sold,
SUM( op.final_price * op.products_quantity ) AS total_sales,
pd.products_name
FROM products AS p
LEFT JOIN products_visits AS pv
ON pv.products_id = p.products_id
AND pv.timestamp BETWEEN '2011-01-15' AND '2011-04-17'
JOIN orders_products AS op
ON p.products_id = op.products_id
JOIN products_description AS pd
ON p.products_id = pd.products_id
JOIN orders as o
ON op.orders_id = o.orders_id
AND o.date_purchased BETWEEN '2011-01-15' AND '2011-04-17'
GROUP BY p.products_id
ORDER BY total_sales DESC

Related

OR clause slowing down SQL query

I need your help about the query below which takes more than 2 min to return a result:
SELECT
p.weight,
o.login,
o.date,
o.s_address,
o.s_city,
o.s_county,
o.s_state,
o.s_country,
o.s_zipcode,
o.phone,
c.categoryid,
c.category,
o.orderid,
p.product product_name,
p.productcode sku,
d.amount,
v.value emplacement,
( SELECT ev.value FROM xcart_extra_field_values ev LEFT JOIN xcart_extra_fields ef ON ef.fieldid=ev.fieldid WHERE ev.productid = d.productid AND ef.field = 'a_type' LIMIT 1 ) type,
o.customer_notes,
o.membership,
o.s_firstname,
o.s_lastname,
o.phone,
d.price,
o.email
FROM `xcart_orders` o
LEFT JOIN `xcart_shipping` s ON s.shippingid=o.shippingid
LEFT JOIN `xcart_order_details` d ON d.orderid=o.orderid
LEFT JOIN `xcart_products` p ON p.productid=d.productid
LEFT JOIN `xcart_products_categories` pc ON pc.productid=p.productid
LEFT JOIN `xcart_categories` c ON c.categoryid=pc.categoryid
LEFT JOIN `xcart_extra_field_values` v ON v.productid=p.productid
LEFT JOIN `xcart_extra_fields` f ON f.fieldid=v.fieldid
WHERE o.shippingid IN ( SELECT DISTINCT shippingid FROM `xcart_rafale_shipping` WHERE rafale='1' )
AND (
SELECT COUNT(*)
FROM `xcart_order_details` d2
LEFT JOIN `xcart_products_categories` pc2 ON pc2.productid=d2.productid
WHERE d2.orderid=o.orderid
AND pc2.categoryid NOT IN ( SELECT DISTINCT ac2.categoryid FROM `xcart_rafale_aggregation_categories` ac2 WHERE ac2.aggregationid='12' )
) = 0
AND (
( o.date BETWEEN '1386802800' AND '1386889199' )
OR (o.orderid IN ('44', '55', '66'))
)
AND o.orderid NOT IN ('11', '22', '33', '123', '458')
AND o.paid = 'Y'
AND o.status <> 'F'
AND o.status <> 'Q'
AND o.status <> 'I'
AND f.field = 'emplacement'
AND pc.main = 'Y'
ORDER BY v.value ASC, p.productcode ASC
LIMIT 100
The problem may come from the following clause
AND (
( o.date BETWEEN '1386802800' AND '1386889199' )
OR (o.orderid IN ('44', '55', '66'))
)
because the query executes faster when I remove the OR (o.orderid IN ('44', '55', '66'))
There are indexes on o.date and o.orderid columns
I used the query below inspired by reply from #Clockwork-Muse:
SELECT
p.weight,
o.login,
o.date,
o.s_address,
o.s_city,
o.s_county,
o.s_state,
o.s_country,
o.s_zipcode,
o.phone,
c.categoryid,
c.category,
o.orderid,
p.product product_name,
p.productcode sku,
d.amount,
v.value emplacement,
( SELECT ev.value FROM xcart_extra_field_values ev LEFT JOIN xcart_extra_fields ef ON ef.fieldid=ev.fieldid WHERE ev.productid = d.productid AND ef.field = 'a_type' LIMIT 1 ) type,
o.customer_notes,
o.membership,
o.s_firstname,
o.s_lastname,
o.phone,
d.price,
o.email
FROM `xcart_order_details` d
INNER JOIN (SELECT *
FROM `xcart_orders`
WHERE (
orderid IN ('44', '55', '66')
OR (`date` >= '1386802800' AND `date` <= '1386889199')
)
) o
ON o.orderid = d.orderid
INNER JOIN (SELECT DISTINCT shippingid
FROM `xcart_rafale_shipping`
WHERE rafale = '1') rf
ON rf.shippingid = o.shippingid
INNER JOIN `xcart_shipping` s ON s.shippingid=o.shippingid
INNER JOIN `xcart_products` p ON p.productid=d.productid
INNER JOIN (SELECT *
FROM `xcart_products_categories`
WHERE main = 'Y') pc
ON pc.productid=p.productid
LEFT JOIN `xcart_categories` c ON c.categoryid=pc.categoryid
LEFT JOIN `xcart_extra_field_values` v ON v.productid=p.productid
LEFT JOIN `xcart_extra_fields` f ON f.fieldid=v.fieldid
WHERE NOT EXISTS (
SELECT d2.orderid
FROM `xcart_order_details` d2
LEFT JOIN `xcart_products_categories` pc2 ON pc2.productid=d2.productid
WHERE d2.orderid=o.orderid
AND pc2.categoryid NOT IN ( SELECT DISTINCT ac2.categoryid FROM `xcart_rafale_aggregation_categories` ac2 WHERE ac2.aggregationid='12' )
)
AND o.orderid NOT IN ('11', '22', '33', '123', '458')
AND o.paid = 'Y'
AND o.status NOT IN ('F', 'Q', 'I')
AND f.field = 'emplacement'
Besides any other problems your query is having, or indices it may need, it's doing more work than it needs to; here's a slightly tweaked version which might run faster:
SELECT
p.weight,
o.login,
o.date,
o.s_address,
o.s_city,
o.s_county,
o.s_state,
o.s_country,
o.s_zipcode,
o.phone,
c.categoryid,
c.category,
o.orderid,
p.product product_name,
p.productcode sku,
d.amount,
v.value emplacement,
(SELECT ev.value
FROM xcart_extra_field_values ev
INNER JOIN xcart_extra_fields ef
ON ef.fieldid = ev.fieldid
AND ef.field = 'a_type'
WHERE ev.productid = d.productid) type,
o.customer_notes,
o.membership,
o.s_firstname,
o.s_lastname,
o.phone,
d.price,
o.email
FROM `xcart_orders` o
INNER JOIN (SELECT DISTINCT shippingid
FROM `xcart_rafale_shipping`
WHERE rafale = '1') rf
ON rf.shippingid = o.shippingid
LEFT JOIN `xcart_shipping` s
ON s.shippingid = o.shippingid
LEFT JOIN `xcart_order_details` d
ON d.orderid = o.orderid
LEFT JOIN `xcart_products` p
ON p.productid = d.productid
LEFT JOIN `xcart_products_categories` pc
ON pc.productid = p.productid
AND pc.main = 'Y'
LEFT JOIN `xcart_categories` c
ON c.categoryid = pc.categoryid
LEFT JOIN `xcart_extra_field_values` v
ON v.productid = p.productid
LEFT JOIN `xcart_extra_fields` f
ON f.fieldid = v.fieldid
AND f.field = 'emplacement'
WHERE NOT EXISTS (SELECT *
FROM `xcart_products_categories` pc2
LEFT JOIN `xcart_rafale_aggregation_categories` ac2
ON ac2.categoryid = pc2.categoryid
AND ac2.aggregationid = '12'
WHERE pc2.productid = d.productid
AND ac2.categoryid IS NULL)
AND ((o.date >= '1386802800' AND o.date <'1386889200')
OR o.orderid IN ('44', '55', '66'))
AND o.orderid NOT IN ('11', '22', '33', '123', '458')
AND o.paid = 'Y'
AND o.status NOT IN ('F', 'Q', 'I')
ORDER BY v.value ASC, p.productcode ASC
LIMIT 100
A couple of other things -
You have LEFT JOINs with a condition in the WHERE clause - this actually turns them into INNER JOINs. I've moved the relevant conditions into the join, which will likely change your results. If you wanted an actual INNER JOIN, just change/remove the word. This is why it's best to put ALL conditions in a join, when possible.
Date/time/timestamps (even if not stored as that type) are a "measurement" - all measurements logically have some imprecision in the recording; to reflect this please use "lower-bound inclusive, upper-bound exclusive" (a >= x < b, needs to be flipped for negative values) for comparisons. I also recommend this for integer counts, for the sake of consistency.
Without an ORDER BY clause, any use of LIMIT (or similar statements) returns essentially uncontrollable results. If you want exactly one value, you must do one of the following - 1) use an aggregate (MAX(), etc), 2) write your query/structure your db such that only one value will meet the criteria, 3) provide a relevant ORDER BY for the use of "select position x" type constructs. Failure to do so will cause your query to return unexpected results when you least expect it (and without throwing a warning, either). In this case I find it extremely unlikely that there is more than one instance of a value in a EAV table (essentially, case #2).
Your original query contains a rather obfuscated double negative (SELECT COUNT(*) ... = 0)). Unfortunately, without knowing more about the nature of your data/table schema, I can't really eliminate the double negative (although I can make it more obvious. For the sake of future maintainers, please avoid double negatives whenever possible. In this case, it's because of your (perhaps overly) liberal use of LEFT-joins - Are you sure that information isn't required?
Most likely thing to help you would be to make sure you have an index on xcart_orders.orderid if you are sure that part of the query is making it slower.
Try this query, I tried all subquerys put in joins:
SELECT
p.weight,
o.login,
o.date,
o.s_address,
o.s_city,
o.s_county,
o.s_state,
o.s_country,
o.s_zipcode,
o.phone,
c.categoryid,
c.category,
o.orderid,
p.product product_name,
p.productcode sku,
d.amount,
v.value emplacement,
evv.Value AS type,
o.customer_notes,
o.membership,
o.s_firstname,
o.s_lastname,
o.phone,
d.price,
o.email
FROM `xcart_orders` o
LEFT JOIN `xcart_shipping` s ON s.shippingid=o.shippingid
LEFT JOIN `xcart_order_details` d ON d.orderid=o.orderid
LEFT JOIN `xcart_products` p ON p.productid=d.productid
LEFT JOIN `xcart_products_categories` pc ON pc.productid=p.productid
LEFT JOIN `xcart_categories` c ON c.categoryid=pc.categoryid
LEFT JOIN `xcart_extra_field_values` v ON v.productid=p.productid
LEFT JOIN `xcart_extra_fields` f ON f.fieldid=v.fieldid
JOIN (SELECT DISTINCT shippingid
FROM `xcart_rafale_shipping`
WHERE rafale='1') rs
ON rs.shippingid = o.shippingid
JOIN (SELECT d2.orderid, COUNT(*) as cnt
FROM `xcart_order_details` d2
LEFT JOIN `xcart_products_categories` pc2 ON pc2.productid=d2.productid
LEFT JOIN `xcart_rafale_aggregation_categories` ac2
ON ac2.aggregationid='12'
AND ac2.categoryid = pc2.categoryid
WHERE ac2.categoryid is null
GROUP BY d2.orderid
) dd2
ON dd2.orderid=o.orderid
AND dd2.cnt = 0
LEFT JOIN (SELECT ev.productid, MAX(ev.value) as 'Value'
FROM xcart_extra_field_values ev
LEFT JOIN xcart_extra_fields ef ON ef.fieldid=ev.fieldid
WHERE ef.field = 'a_type'
GROUP BY ev.productid) evv
ON evv.productid = d.productid
WHERE (( o.date BETWEEN '1386802800' AND '1386889199' )
OR (o.orderid IN ('44', '55', '66'))
)
AND o.orderid NOT IN ('11', '22', '33', '123', '458')
AND o.paid = 'Y'
AND o.status NOT IN ('F', 'Q', 'I')
AND f.field = 'emplacement'
AND pc.main = 'Y'
ORDER BY v.value ASC, p.productcode ASC
LIMIT 100

what's wrong with my sql query

Id like it to return records where products_extrafield_id is not 14 , but it still RETURNS it in my results .. I'm using joins
SELECT op. *
FROM
products_to_products_extra_fields AS p
INNER JOIN orders_products AS op ON p.products_id = op.products_id
INNER JOIN orders AS o ON op.orders_id = o.orders_id
WHERE NOT
EXISTS (
SELECT *
FROM products_to_products_extra_fields
WHERE
p.products_id = op.products_id
AND p.products_extra_fields_id = 14
)
AND o.date_purchased BETWEEN '2013-11-29' AND '2013-12-03 23:59:59'
AND o.payment_method = 'Institutional Billing'
AND o.orders_status <100001
GROUP BY o.orders_id
ORDER BY DECODE( o.cc_type, 'oFsAfHr7' ) ASC
LIMIT 0 , 30
If I am reading your SQL correctly, then you don't need a NOT EXISTS clause to do this. Just replace the NOT EXISTS clause with the following statement: p.products_extra_fields_id != 14
SELECT
op. *
FROM
products_to_products_extra_fields AS p
INNER JOIN orders_products AS op
ON p.products_id = op.products_id
INNER JOIN orders AS o
ON op.orders_id = o.orders_id
WHERE
p.products_extra_fields_id != 14
AND o.date_purchased BETWEEN '2013-11-29' AND '2013-12-03 23:59:59'
AND o.payment_method = 'Institutional Billing'
AND o.orders_status <100001
GROUP BY o.orders_id
ORDER BY DECODE( o.cc_type, 'oFsAfHr7' ) ASC
LIMIT 0 , 30
Why are you GROUPing without using aggregate functions? Also, why would try to GROUP BY a field that is not in your SELECT? I added the DISTINCT operator, along with moving your logic around. You subquery was using criteria from the main/ outer query.
SELECT DISTINCT o.cc_type, op.*
FROM orders_products AS op
INNER JOIN orders AS o
ON op.orders_id = o.orders_id
WHERE o.date_purchased BETWEEN '2013-11-29' AND '2013-12-03 23:59:59'
AND o.payment_method = 'Institutional Billing'
AND o.orders_status <100001
AND NOT EXISTS (SELECT *
FROM products_to_products_extra_fields AS p
WHERE p.products_id = op.products_id
AND p.products_extra_fields_id = 14)
ORDER BY DECODE( o.cc_type, 'oFsAfHr7' ) ASC
LIMIT 0 , 30;

Use limit in sub query

I have 4 tables
product
status
orders
orders_products.
Using following query I get the proper result but it is not in correct order i.e. result gets by sub query.I want result order as sub query result.
SELECT
p.products_id, p.products_image, p.products_tax_class_id,
pd.products_name, if(s.status, s.specials_new_products_price,
p.products_price) as products_price
FROM
products p LEFT JOIN specials s
ON p.products_id = s.products_id, products_description pd
WHERE
p.products_status = '1'
AND p.products_id = pd.products_id
AND pd.language_id = '2'
AND p.products_id in (
SELECT * from (
SELECT
distinct(op.products_id)
FROM
orders_products op,orders o
WHERE
op.orders_id=o.orders_id
AND o.customers_id='27'
ORDER BY
o.date_purchased
DESC LIMIT 0,10
) AS temptable
)
The outer SELECT has an ORDER BY pd.products_name. That's what should apply, and only that.
The sub-query ORDER BY shouldn't actually matter. You should get the same result removing it.
If you want a different sort order from pd.products_name, you have to ORDER BY the column required. In your case, you need to move orders.date_purchased to the main part of the query so you can use it to sort. Inverting the query, i.e. starting with what you want and relating everything else:
SELECT MAX(o.date_purchased) AS date_purchased, DISTINCT (op.products_id) AS products_id
p.products_id, p.products_image, p.products_tax_class_id, p.products_price
pd.products_name,
IF (s.status, s.specials_new_product_price, p.products_price) AS products_price
FROM orders o
LEFT JOIN orders_products op USING (orders_id)
LEFT JOIN products p USING (products_id)
LEFT JOIN products_description pd USING (products_id)
LEFT JOIN specials s USING (products_id)
WHERE o.customers_id = '27'
AND p.products_status = 1
AND pd.language_id = 2

Multiple inner joins to get a complex report, not working

For the schema below, I need to get this report
This is what I have
select c.name, sr.name, count(o.order_id)
from contact c
INNER JOIN accounts a
ON c.account_id=a.account_id
INNER JOIN sales_reps sr
ON a.sales_rep_id =sr.sales_rep_id
INNER JOIN orders o
ON a.account_id =o.account_id
where o.order_id in (
select SUM(oi.quantity*p.price) from
order_items oi INNER JOIN parts p
on oi.part_id =p.part_id
)
group by a.account_id, c.name
But this does not give any results.
Please help.
Your where condition is not right, how should be a order_id equal a sum?
Try the below:
select
c.name, sr.name, COUNT(o.order_id), SUM(op.order_total)
FROM
contact c
INNER JOIN
accounts a ON c.account_id = a.account_id
INNER JOIN
sales_reps sr ON a.sales_rep_id = sr.sales_rep_id
INNER JOIN
orders o ON a.account_id = o.account_id
INNER JOIN
(SELECT
oi.order_id, SUM(oi.quantity * p.price) AS order_total
FROM
order_items oi
INNER JOIN
parts p ON oi.part_id = p.part_id
GROUP BY
oi.order_id
) op ON o.order_id = op.order_id
WHERE o.delivery_data >= CURDATE()
GROUP by c.contact_id
It won't give results as your WHERE ... IN SELECT is based on a query returning a sum() value which will not equal a key (most likely), or incorrect at best... and since you are dealing with a quantity and price which will have decimal precision (typically), you won't even get that to match even LESS likely...
I would swap the query around to pre-qualify the orders within a given date in question and sum that... THEN join to rest...
select
c.name,
sr.name,
PreQualified.NumberOrders,
PreQualified.OrderTotal
from
( select
o.Account_ID,
count( distinct o.order_id ) as NumberOrders,
sum( oi.quantity * p.price ) as OrderTotal
from
orders o
join order_items oi
on o.order_id = oi.order_id
join parts p
on oi.part_id = p.part_id
where
o.Delivery_Date >= CURDATE()
group by
o.Account_ID ) as PreQualified
JOIN Accounts a
on PreQualified.Account_ID = a.Account_ID
Join Contact C
on a.Account_ID = c.Account_ID
JOIN Sales_Reps sr
ON a.sales_rep_id = sr.sales_rep_id
If you want to count records use
count(*)
Instead of
count(o.order_id)

Need help with a mysql query

I have a mysql query that is trying to combine the following tables:
TABLE: orders_products
products_id
orders_id
products_quantity
products_price
TABLE: products
products_id
products_model
I want a query that returns the total number of sales, the total sales value for each product_id, and the product_model for each product in the orders_products table. This is the best I've come up with so far and it just adds everything into the same bucket:
SELECT
SUM( op.products_quantity ) AS num_sold
,SUM( op.final_price * op.products_quantity )
,p.products_model
FROM orders_products AS op
JOIN products AS p
WHERE p.products_id = op.products_id
Add a group by p.products_id after your where and you will be good
SELECT p.products_id, p.products_model,
SUM( op.products_quantity ) AS num_sold
,SUM( op.final_price * op.products_quantity ) AS sales
FROM orders_products AS op
JOIN products AS p
WHERE p.products_id = op.products_id
GROUP BY p.products_id, p.products_model
You need to use GROUP BY. Check this link: http://www.w3schools.com/sql/sql_groupby.asp