couchbase Expression must depend only on group keys or aggregates - couchbase

why I get this error message on my query ?
"Expression (`L`.`product_id`) must depend only on group keys or aggregates.
Query:
SELECT
P.product_id as PID,
L.product_id as LikedPID,
COUNT(L.likedAt) as likes
FROM products P
INNER JOIN products_likes L
ON P.product_id = L.product_id
WHERE P.product_id='3221132' GROUP BY P.product_id;

The reason for this error message is because you are using an aggregate (COUNT), but not everything else you are projecting in your SELECT is aggregated or included in the GROUP BY. (This is true of every SQL I've used, by the way, not just Couchbase's N1QL). If you want to group by both product IDs, you'll need to change your GROUP BY:
GROUP BY P.product_id, L.product_id
Or you could omit the L.product_id as LikedPID, from your SELECT.

Related

MySQL aliasing not working when subjoining

I have two main tables: orders and PayPal transactions. I'm trying to get only the distinct values from my PayPal transactions table. Since there is no unique identifier in my transactions table I have tried to use a subquery to retrieve them.
The problem with my query is that MySQL doesn't recognize my aliases. Therefore, it gives me an Unknown column error.
/* SQL Error (1054): Unknown column 'pp.Date' in 'field list' */
SELECT
pp.Date
FROM hub.orders o
LEFT JOIN
(SELECT p.transaction_of_interest AS ppID
FROM financial.paypal AS p
GROUP BY p.transaction_of_interest
) AS pp ON pp.ppID = o.ex_trans_id
You are not getting DATE column from PP sub-query. If you include that column it will work as you would expect. If your result set multiplying because of TRANSACTION_OF_INTEREST values are not distinct then you should use a function on P.DATE like MAX to singularize yor TRANSACTION_OF_INTEREST values.
Which PP.DATE values you are need ? Is there any condition like last date or something ?
SELECT PP.DATE
FROM HUB.ORDERS O
LEFT JOIN (SELECT P.TRANSACTION_OF_INTEREST AS PPID,P.DATE
FROM FINANCIAL.PAYPAL AS P
GROUP BY P.TRANSACTION_OF_INTEREST,P.DATE
) AS PP ON PP.PPID = O.EX_TRANS_ID
You can only refer to those fields via the derived table's alias that you included in the select list for the derived table. Since you did not include the date field in the select list, you cannot reference it.
You need to add the ¬Date¬ field to the select list in the subquery and to the group by clause as well.
SELECT
pp.Date
FROM hub.orders o
LEFT JOIN
(SELECT p.transaction_of_interest AS ppID, p.Date
FROM financial.paypal AS p
GROUP BY p.transaction_of_interest, p.Date
) AS pp ON pp.ppID = o.ex_trans_id
My best interpretation of your question is that you want distinct dates of PayPal transactions.
If you only want dates from the paypal table, doesn't this do what you want?
SELECT DISTINCT p.DATE
FROM financial.paypal p;
If the dates come from the orders table, but you only want them for PayPal transactions, then LEFT JOIN is not appropriate:
SELECT DISTINCT o.Date
FROM hub.orders o JOIN
financial.paypal p
ON pp.ppID = o.ex_trans_id

MYSQL Subquery and count number of rows containing a value

I have two tables oc_order o and oc_order_total ot
oc_order o has fields o.customer_id, o.date_added, o.email,o.total
And oc_order_total ot has fields ot.code and ot.value
I want to show results only if customer orders more than 3 times that is if customer_id repeats thrice or more in the result and show ot.value where ot.code = 'shipping'
I am trying to do following
SELECT COUNT(o.customer_id) AS 'Orders Count', o.date_added, o.email,o.total, ot.value
FROM oc_order o
Inner join oc_order_total ot ON ot.order_id = o.order_id
WHERE count(o.customer_id) > 3 AND ot.value = (select value from oc_order_total where code = 'shipping' )
GROUP BY o.customer_id
I am getting Invalid use of group error and I think I am not using subquery correctly in where clause.
You can not use SUM/COUNT in WHERE statement. You need to use for this HAVING operator. Try this query:
SELECT COUNT(o.customer_id) AS 'Orders Count', o.date_added, o.email,o.total, ot.value
FROM oc_order o
INNER JOIN oc_order_total ot ON ot.order_id = o.order_id AND
WHERE ot.code IN (select value from oc_order_total where code = 'shipping' )
GROUP BY o.customer_id
HAVING count(o.customer_id) > 3
EDIT: Adding example with ot.value not affected by GROUP BY.
SELECT o.order_id, q.orders_count AS 'Orders Count', o.date_added, o.email,o.total, ot.value
FROM oc_order o
INNER JOIN oc_order_total ot ON ot.order_id = o.order_id
INNER JOIN (SELECT o.customer_id, COUNT(o.customer_id) AS orders_count
FROM oc_order o
INNER JOIN oc_order_total ot ON ot.order_id = o.order_id
WHERE ot.code IN (select value from oc_order_total where code = 'shipping' )
GROUP BY o.customer_id
HAVING count(o.customer_id) > 3) AS q ON q.customer_id = o.customer_id
Basically what happens here is that you pre-filter customers with previous query and then use these pre-filtered list to get individual orders for these customers meeting your criteria. On this individual orders_id you can perform any operation without grouping cause you already eliminated customers not meeting your needs. Hope it helps.
NO, you are getting error cause your GROUP BY doesn't contains all the column listed in SELECT list and you can't use COUNT() aggregate function like that in WHERE condition; it's only allowed in HAVING clause. You can modify your query like
SELECT o.date_added
, o.email
, o.total
, ot.value
FROM oc_order o
JOIN oc_order_total ot
ON ot.order_id = o.order_id
JOIN
( SELECT COUNT(customer_id) AS 'Orders Count'
, customer_id
FROM oc_order
GROUP
BY customer_id
) xxx
ON xxx .customer_id = o.customer_id
WHERE xxx.`Orders Count` > 3
AND ot.code = 'shipping';
For one thing, aggregates cannot be referenced in the WHERE clause.
The predicates in the WHERE clause are evaluated when the rows are accessed. At the time the rows are being retrieved, MySQL doesn't have any information about the value returned by aggregate functions (e.g. COUNT()).
The COUNT() aggregate will be evaluated after the rows are accessed, and after the GROUP BY operation.
An aggregate can be referenced in a HAVING clause, following the GROUP BY clause.
GROUP BY ...
HAVING COUNT(...) > 3
Note that the HAVING clause is evaluated after the GROUP BY operation, and after the values of the aggregate expressions are evaluated. Much different than the WHERE clause.
Also, the subquery is a bit odd. Because it's being referenced in an equality comparison, the subquery can return at most one row. We don't see anything in the query that would prevent that, unless code is guaranteed to be unique in `ot_.
If code is unique, then we wouldn't need a subquery at all, we could just test for code.
WHERE ot.code = 'shipping'
If there are multiple rows with "shipping" in the code column, we don't see any guarantee that the value column on those rows will be the same. To test for any of the possible values, we could use an IN operator instead of the equality (scalar) comparison. e.g.
WHERE ot.value IN ( SELECT v.value
FROM oc_order_total v
WHERE v.code = 'shipping'
)
But that still looks really odd. What's strange is that it's using the same table as the outer query. If we are using a subquery to lookup the set of values related to a string code, that's usually a separate lookup table. And we'd normally prefer a JOIN operation rather than an IN (subquery). Very strange.
Also, the non-aggregate expressions in the SELECT list
o.date_added, o.email, o.total, ot.value
Do not appear in the GROUP BY clause. Most relational databases will throw an error "non-aggregate in select not in group by" something of that ilk.
But a MySQL extension will allow the query to run, but the values returned for the non-aggregates is indeterminate. MySQL will return values for those expressions based on some row included in the collapsed set, but there's no guarantee which row that will be.
We can also get MySQL to throw an error like other databases, by including ONLY_FULL_GROUP_BY in the sql_mode variable.

Filter query by ID

I'm trying to retrieve all the product information, filtering by product category id. The result should be hundreds of products but only returns 5 items. Whats wrong with my query?
SELECT product.id_product,
product.reference,
product.price,
product.active,
product.quantity,
product.id_category_default,
lang.name AS product_name,
lang.description,
lang.link_rewrite AS product_link_rewrite,
category.name AS category_name,
category.link_rewrite AS category_link_rewrite,
image.id_image,
product.id_manufacturer,
discount.reduction,
manufacturer.name AS manufacturer_name
FROM ps_product product
LEFT JOIN ps_specific_price discount ON discount.id_product=product.id_product
LEFT JOIN ps_product_lang lang ON lang.id_product=product.id_product
LEFT JOIN ps_category_lang category ON category.id_category=product.id_category_default
LEFT JOIN ps_image image ON image.id_product=product.id_product
LEFT JOIN ps_manufacturer manufacturer ON manufacturer.id_manufacturer=product.id_manufacturer
WHERE product.active=1
AND product.quantity>=1
AND product.id_category_default IN
(
4,5,6,65,66,90,91,53,54,48,49,50,55,62,67,68,71,19,82,88,89,87,22,24,26,74,
76,77,28,78,79,97,98,99,93,96,35,36,38,39,100
)
GROUP BY product.id_product ASC
Perhaps something as simple as:
GROUP BY product.id_product ASC
needs to be:
ORDER BY product.id_product ASC
mySQL extends the group by clause so that any columns not in the group by may get an indeterminate value.
If ONLY_FULL_GROUP_BY is disabled, a MySQL extension to the standard SQL use of GROUP BY permits the select list, HAVING condition, or ORDER BY list to refer to nonaggregated columns even if the columns are not functionally dependent on GROUP BY columns. ... The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate, which is probably not what you want.
Options:
change group by to order by
add all columns to the group by
don't use group by at all.

Using GROUP BY or DISTINCT with a LEFT JOIN

I have a table with orders and a table with users. It's possible for an order to be placed with an entry in the user table.
With the following MySQL statement I get duplicate values for orders if there is a matching user:
SELECT o.id, u.id as 'user_id', u.name
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE o.status = 'active'
If I add a GROUP BY o.id it solves the issue.
SELECT o.id, u.id as 'user_id'
FROM orders o
LEFT JOIN users u ON o.user_id = u.id
WHERE o.status = 'active'
GROUP BY o.id
It also works if I use SELECT DISTINCT.
My questions are:
Why does it return duplicate fields?
Is it more correct to use GROUP BY or SELECT DISTINCT?
Your detail query -- the query returning every row, rather than the deduplicated version with DISTINCT or GROUP BY -- is finding more than row in users matching each row in orders. So, it is dutifully returning all those rows.
To solve your problem correctly you need to figure out why there are multiple users rows for each order. That is, for some values of order.user_id there are multiple values of users.id.
That seems a little strange to me, but I do not understand your data model. You probably need to get to investigate this data anomaly. A conventional schema would have each user able to place multiple orders, but each order relating to only one user. In that schema this query would yield one row per order but still include users with no orders:
SELECT u.id AS user_id, o.id AS order_id
FROM users AS u
LEFT JOIN orders AS o ON o.user_id = u.id
Could it be that is what you want?
Contrary to some peoples' belief, GROUP BY orders.id and SELECT DISTINCT orders.id, users.id are not the same thing. In fact, your proposed use of GROUP BY misuses the notorious MySQL extension to GROUP BY. Standard SQL will reject your GROUP BY. It will only accept GROUP BY orders.id, users.id, which is indeed equivalent to DISTINCT.
Why does it return duplicate fields?
It returns duplicates because you have not applied anything to stop it from doing so. When you apply GROUP BY or DISTINCT then you actually stop the duplicates.
Is it more correct to use GROUP BY or SELECT DISTINCT
Both are equivalent and can be used as per your convenience. You may find that DISTINCT is faster over GROUP BY under the fact that indexes are not created on your table. But that does not make the usage of GROUP BY incorrect. If indexes are created then they both are equivalent to each other.
Your query does not need a JOIN at all. You can just use:
SELECT o.id, o.user_id
FROM orders o
WHERE o.status = 'active';
As for SELECT DISTINCT or GROUP BY. The two should be equivalent in performance (or very close). They are doing essentially the same work.
The advantage of GROUP BY is that you can add aggregation functions. The advantage of DISTINCT is that you don't have to list all the columns twice, and it accepts *.

Get minimum price with left join / result reduced

I am coding an online shop. A product has a default price, but since it might have different attributes (color, size, ...), those different attributes might result in different prices as well. At the moment I am trying to produce a mysql query which helps me to find the minimum and maximum possible price of a product. Using LEFT JOIN currently reduces the query's result to only one row and I don't know why.
SELECT
products.id AS id,
categories.name AS category_name,
MIN(product_attributes.price) AS min_price, MAX(product_attributes.price) AS max_price,
products.*
FROM products
LEFT JOIN categories ON category_id=categories.id
LEFT JOIN product_attributes ON products.id=product_attributes.product_id
Is this even the correct approach? I do not know that much about mysql, I just try and try and I am happy if it works. Thanks for help anyway.
You are missing a GROUP BY clause in your current query, but I would recommend using a subquery to get the result:
SELECT
p.id AS id,
c.name AS category_name,
pa.min_price,
pa.max_price,
p.*
FROM products p
LEFT JOIN categories c
ON p.category_id = c.id
LEFT JOIN
(
select MIN(product_attributes.price) min_price,
MAX(product_attributes.price) max_price,
product_id
from product_attributes
group by product_id
) pa
ON p.id=pa.product_id
The main reason why I would suggest using a subquery is because MySQL MySQL uses a EXTENSION TO GROUP BY which allows the behavior of not enforcing the FULL GROUP BY.
This extension in MySQL can cause unexpected values to be returned in the columns in the SELECT list that are not in the GROUP BY clause or in an aggregate function.
From the MySQL Docs:
MySQL extends the use of GROUP BY so that the select list can refer to nonaggregated columns not named in the GROUP BY clause. ... You can use this feature to get better performance by avoiding unnecessary column sorting and grouping. However, this is useful primarily when all values in each nonaggregated column not named in the GROUP BY are the same for each group. The server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate. Furthermore, the selection of values from each group cannot be influenced by adding an ORDER BY clause. Sorting of the result set occurs after values have been chosen, and ORDER BY does not affect which values the server chooses.
you need to have GROUP BY clause,
SELECT
products.id AS id,
categories.name AS category_name,
MIN(product_attributes.price) AS min_price,
MAX(product_attributes.price) AS max_price.
products.*
FROM products
LEFT JOIN categories ON category_id=categories.id
LEFT JOIN product_attributes ON products.id=product_attributes.product_id
GROUP BY products.id, categories.name
but be careful when using GROUP BY in mysql, as the statement is perfectly valid if ONLY_FULL_GROUP_BY is disabled by default.
The correct way of doing the query is by using a subquery which separately gets minimum price for each product.
SELECT a.id AS id,
b.name AS category_name,
c.minPrice,
c.maxPrice,
a.*
FROM products a
LEFT JOIN categories b
ON a.category_id = b.id
LEFT JOIN product_attributes c
(
SELECT product_id,
MIN(product_attributes.price) minPrice,
MAX(product_attributes.price) maxPrice
FROM product_attributes
GROUP BY product_id
) d ON a.id = c.product_id