MySQL - Operand should contain 1 column(s) with opencart - php

While working on a system I'm creating, I attempted to use the following query in my project:
SELECT o.order_id,
(SELECT op.price, op.quantity, op.name FROM oc_order_product op
RIGHT JOIN oc_order o ON op.order_id=o.order_id ) AS product,
CONCAT(o.firstname, ' ', o.lastname) AS customer,
(SELECT os.name FROM oc_order_status os
WHERE os.order_status_id = o.order_status_id AND os.language_id = '1') AS status,
o.total, o.currency_code, o.currency_value, o.date_added, o.date_modified
FROM `oc_order` o WHERE o.order_status_id > '0' ORDER BY o.order_id DESC LIMIT 0,20
but it warnings: Operand should contain 1 column(s), pls help me

A subquery in the SELECT list must return exactly one expression. A subquery that returns more than one expression is not valid.
The problem is the second line, that's a subquery in the SELECT list of the outer query. And there is more than one expression in the SELECT list of the subquery.
You also need to insure that any subquery in the SELECT list will return at most one row.
I suggest you consider a JOIN operation, rather than a correlated subquery, something like this:
SELECT o.order_id
, op.price AS product_price
, op.quantity AS product_quantity
, op.name AS product_name
, CONCAT(o.firstname, ' ', o.lastname) AS customer
, ( SELECT os.name
FROM oc_order_status os
WHERE os.order_status_id = o.order_status_id
AND os.language_id = '1'
LIMIT 1
) AS status
, o.total
, o.currency_code
, o.currency_value
, o.date_added
, o.date_modified
FROM oc_order o
LEFT
JOIN oc_order_product op
ON op.order_id=o.order_id
WHERE o.order_status_id > '0'
ORDER BY o.order_id DESC
LIMIT 0,20
I left the subquery to return status in the query, as a demonstration of a subquery that is valid in terms of SQL syntax. I added the LIMIT 1 clause, to insure the query doesn't throw an error at execution time, if that subquery happens to return more than one row.
(It's not clear what problem you were trying to solve by using a subquery in the SELECT list.)

Related

MySQL query (28K rows) overloads the server

We are running OpenCart store and now we have to export order data for 6 months (around 17K orders).
Opencart has a builtin solution, when you select the orders you need and export, but it works fine with less than 500 orders.
I've decided to make a standalone script based on the query it uses for its original export.
I'm not publishing the export part, since my dedicated server and the store freezes when I run the following query and consequent loop for mysql_fetch_assoc.
$query = "SELECT o.*, op.name, op.model,op.order_product_id, op.product_id, op.quantity, op.price, op.total AS ptotal, op.tax,
(SELECT ot.value FROM order_total ot WHERE ot.order_id = o.order_id AND ot.code = 'sub_total') AS sub_total,
(SELECT ot.value FROM order_total ot WHERE ot.order_id = o.order_id AND ot.code = 'credit') AS store_credit,
(SELECT `name` FROM order_history oh INNER JOIN `order_status` os on oh.order_status_id=os.order_status_id WHERE oh.order_id = o.order_id and os.language_id='2'
ORDER BY order_history_id DESC LIMIT 1) as order_status
FROM `order` o inner join `order_product` op on o.order_id=op.order_id WHERE o.date_added BETWEEN '2014-01-01 00:00:00' AND '2014-06-30 03:59:59'";
$sql = mysql_query($query);
do {
echo $o['model']."<br/>"; // here we will have a part using PEAR Excel basically
} while ($o=mysql_fetch_assoc($sql));
$endtime = microtime();
echo $endtime-$starttime;
Indexes are set. Additional index was set on oh.order_id. No luck. The same request for 1 particular order id runs in 0.003ms.
My process list shows "Sending data" for the above shown query and "Waiting for table level lock" for anothers.
Could you kindly assist?
I think you could optimize your SQL a bit to prevent locks. The following might help, but recognize that I don't have your schema or data to test this on, so you may need to tweak.
SELECT o.*, op.name, op.model,op.order_product_id, op.product_id,
op.quantity, op.price, op.total AS ptotal, op.tax,
ot.sub_total, ot.store_credit, os.os_name
FROM `order` o inner join `order_product` op on o.order_id=op.order_id
INNER JOIN (SELECT t.order_id,
MAX(case when t.code = 'sub_total' t.value else 0 end case) as sub_total,
MAX(case when t.code = 'credit' t.value else 0 end case) as store_credit
FROM order_total t GROUP BY t.order_id) ot ON ot.order_id = o.order_id
INNER JOIN (SELECT * FROM (SELECT oh.order_id, `name` AS os_name
FROM order_history oh
INNER JOIN `order_status` s on oh.order_status_id=s.order_status_id
WHERE s.language_id='2' ORDER BY order_history_id DESC) t1
GROUP BY t1.order_id) as os ON os.order_id = o.order_id
WHERE o.date_added BETWEEN '2014-01-01 00:00:00' AND '2014-06-30 03:59:59'
I would also probably do a create temp table temp_order_history from select ... and then select * from temp_order_history Also, you may need to do outer join instead of inner join if either of those sub selects have missing data. In those cases, you'd just get NULLs for those columns.

MySQL query, add new alias

I have a mysql query using for search script and i need to add another field (like SPV.term alias) This code is tell me an Error Number: 1054 Unknown column 'S.id' in 'on clause'.
$query = "
SELECT
SP.url,
SP.id,
S.name,
SP.hit,
SP.hot,
SP.action,
SP.id,
SP.smallimage,
SP.mainmodimage,
(SELECT
stock
FROM shop_product_variants
WHERE shop_product_variants.product_id = S.id
AND stock > 0
LIMIT 1) as stock,
(SELECT
price
FROM shop_product_variants
WHERE shop_product_variants.product_id = S.id
OR shop_product_variants.product_id = S.id
AND stock = 0
LIMIT 1) as price,
(SELECT
id
FROM shop_product_variants
WHERE shop_product_variants.product_id = S.id
OR shop_product_variants.product_id = S.id
AND stock = 0
LIMIT 1) as v_id,
(SELECT
old_price
FROM shop_product_variants
WHERE shop_product_variants.product_id = S.id
AND stock > 0
LIMIT 1) as old_price
FROM shop_products_i18n S,
shop_product_variants SPV
INNER JOIN shop_products SP
ON SP.id = S.id
WHERE SP.active = 1
AND S.name LIKE '%" . $get . "%'
OR SP.url LIKE '%" . $get . "%'
OR SPV.term LIKE '%" . $get . "%'
GROUP BY S.id
ORDER BY stock DESC
";
This is your from clause:
FROM shop_products_i18n S,
shop_product_variants SPV INNER JOIN
shop_products SP
ON SP.id = S.id
The problem is that you are mixing old-style joins and new-style joins. A simple rule: Never use a commas in the from clause.
I think you really mean:
FROM shop_products_i18n S INNER JOIN
shop_products SP
ON SP.id = S.id
Because you don't use SPV anywhere else in the outer query, except for the where clause. That condition should probably go into each of the subqueries.
By the way, you can fix the original problem by replacing the , with cross join:
FROM shop_products_i18n S CROSS JOIN
shop_product_variants SPV INNER JOIN
shop_products SP
ON SP.id = S.id
Although , and cross join both perform cartesian products, they behave differently in the FROM clause in terms of precedence. The problem that you have with the , is that the precedence rules say the following INNER JOIN is parsed first -- so the columns in S are not available.

How to combine two Post/Category tables MYSQL SELECT queries into one

I have two MySQL queries:
1) "SELECT ID,post_title,post_category,post_perma FROM ".TBL_POSTS."
WHERE published='1' AND page='0' ORDER BY ID DESC LIMIT 10"
2) "SELECT p.cat_ID,p.cat_nicename FROM ".TBL_CATEGORIES." n, ".TBL_CATEGORIES." p
WHERE n.lft BETWEEN p.lft AND p.rgt AND n.cat_ID='".post_category."' ORDER BY p.lft
First query selects posts and then second select the Path of the category by post_category please note that post_category will be taken from first query means post_category is common in both table.. in first table it is named as post_category and in second it is cat_ID
Right now I am running it in foreach loop which is not good. Also one thing to be noticed that second query will also return Array and that one array should correspond to post_category
Can any SQL expert help me?
Many Thanx
Please try this it might be helpful to you.
SELECT a.ID, a.post_title,a.post_category,a.post_perma, b.cat_ID, b.cat_nickname
FROM (SELECT ID,post_title,post_category,post_perma FROM ".TBL_POSTS." WHERE published='1' AND page='0' ORDER BY ID DESC LIMIT 10) a
LEFT JOIN (SELECT p.cat_ID as cat_ID,p.cat_nicename as cat_nickname FROM " . TBL_CATEGORIES . " n, " . TBL_CATEGORIES . " p WHERE n.lft BETWEEN p.lft AND p.rgt AND n.cat_ID = '" .$post_category. "' ORDER BY p.lft) b ON a.ID = b.cat_ID
I would use LEFT JOIN
Like this:
$sql = "SELECT `p`.`ID`,`p`.`post_title`,`p`.`post_category`,`p`.`post_perma`,`c`.`cat_ID`,`c`.`cat_nicename` FROM `".TBL_POSTS."` AS `p` ";
$sql .= "LEFT JOIN `".TBL_CATEGORIES."` AS `c` ON `c`.`cat_ID`=`p`.`post_category` WHERE `p`.`published`='1' AND `p`.`page`='0' ORDER BY `p`.`ID` DESC LIMIT 10";
You may need to tweak the WHERE clause to suite your needs more..
Please Note: This is one string, i have just split them onto two lines so it is easier to read. .= is to append the current string.

Showing COUNT in LEFT JOIN subquery?

I have a query that selects order info between a selected time period. I want to include a where clause that limits the order info to all orders that have only 1 order total(through out all time).
Here is what I have so far:
SELECT o.orders_id, o.customers_id, o.customers_name, o.payment_method, o.date_purchased,o.orders_status, o.shipping_status, ot.value
FROM orders as o
LEFT JOIN orders_total as ot ON o.orders_id = ot.orders_id
WHERE date_purchased between '2011-07-30' AND '2011-08-30 23:59:59'
AND ot.class = 'ot_total'
AND o.customer_service_id = ''
OR o.customer_service_id IS NULL
ORDER BY orders_id DESC
This query gives me all orders in the specified time period. I need to include a subquery(or something similar) that counts all previous(through out all time) orders(order_count) BY customers_id. Then include a 'HAVING order_count < 2' in the where clause.
Is this possible? Does this make sense?
Just add this in you where close:
AND (
SELECT COUNT(o.id)
FROM orders o2
WHERE o2.customers_id = o.customers_id
) < 2
Or if you want to return the orders count, add it in your SELECT clause, and add a HAVING clause:
SELECT o.orders_id, ..., (
SELECT COUNT(o.id)
FROM orders o2
WHERE o2.customers_id = o.customers_id
) as orders_count
...
HAVING orders_count < 2

How can I combine 2 SQL queries on the same column in the same table?

I need to combine 2 queries into one. Both query the same table and same column name. I need the results in one query so that I can repeat region the results. I have tried UNION but this doesn't run the second half correctly. Query 1 is as follows:
SELECT o.value AS sdate
FROM order_option o
INNER JOIN order_product p ON o.order_product_id = p.order_product_id
WHERE
p.product_id = '$_GET[u]'
AND o.name = 'Start Date'
And query 2 is as follows...
SELECT o2.value AS sday
FROM order_option o2
INNER JOIN order_product p ON o2.order_product_id = p.order_product_id
WHERE
p.product_id = '$_GET[u]'
AND o2.name = 'Number of Days'
UNION should be correct. Does the second query run correctly as shown? if so, it should run the same way in a UNION. You may try UNION ALL and see if that 'corrects' the query.
You can use a combination of MAX and CASE like this
SELECT
MAX(CASE WHEN o.name = 'Start Date' THEN o.value END) AS sdate,
MAX(CASE WHEN o.name = 'Number of Days' THEN o.value END) AS sday
FROM order_option o
INNER JOIN order_product p USING (order_product_id)
WHERE
p.product_id = '$_GET[u]'
;

Categories