ORA-00979: not a GROUP BY expression? [duplicate] - sql

This question already has answers here:
Oracle ORA-00979 - "not a GROUP BY expression"
(4 answers)
Closed 8 years ago.
I have found the solution to this, but what in case one of the column is a subquery, how can i include it in group by, or do i need to include that in group by. I will paste the query here..
SELECT s.customerid, s.denomid,
(SELECT su.quantity
FROM stockupdations su
WHERE s.customerid = su.custid
AND s.denomid = su.denomid
AND s.curid = su.curid) AS cur_stock, c.name AS cus_name, d.denomname AS denom,
cur.curcode AS currency
FROM stock s
LEFT JOIN customers c
ON s.customerid = c.custid
LEFT JOIN denomination d
ON d.denomid = s.denomid
LEFT JOIN currency cur
ON cur.curid = s.curid
GROUP BY s.denomid, s.customerid, c.name, d.denomname, cur.curcode
ORDER BY s.customerid ASC

What about a WITH statement?
WITH tmp AS
(
SELECT s.customerid, s.denomid,
c.name AS cus_name,
d.denomname AS denom,
cur.curcode AS currency
FROM stock s
LEFT JOIN customers c
ON s.customerid = c.custid
LEFT JOIN denomination d
ON d.denomid = s.denomid
LEFT JOIN currency cur
ON cur.curid = s.curid
GROUP BY s.denomid, s.customerid, c.name, d.denomname, cur.curcode
ORDER BY s.customerid ASC
)
SELECT tmp.customerid, tmp.denomid,
su.quantity,
tmp.cus_name,
tmp.denom,
tmp.currency
FROM tmp
INNER JOIN stockupdations su
ON tmp.customerid = su.custid
AND tmp.denomid = su.denomid
AND tmp.curid = su.curid

You can use your "Inner query" in the from clause than on the select.
Say I have a CUSTOMER table and ORDER table,
I can have something like
SELECT C.CUSTOMER_ID, COUNT(T.ORDER_ID)
FROM CUSTOMERS C
JOIN (SELECT CUSTOMER_ID, ORDER_ID, ORDER_DATE, ORDER_STATUS FROM ORDERS O WHERE O.STATUS <> 'DECLINED') T
ON T.CUSTOMER_ID = C.CUSTOMER ID
GROUP BY C.CUSTOMER_ID
(This SQL is just an example, and I know there are better ways to write this, but I could not think of any other example immediately)

You don't have to do everything at once. Try breaking your query into multiple pieces. Subqueries, analytic functions, or other complicated logic will look like simple rows to the outer query. (Don't worry about performance, Oracle will re-write it and do everything as one step if it makes sense.)
--Step 3
select [simple values]
from
(
--Step 2
select [insanity]
from
(
--Step 1
select [madness]
from
[impossible joins]
)
)
group by [simple values]

Related

Column "not contained in either an aggregate function or a GROUP BY clause"

The query:
SELECT
A.mrno, A.remarks,
B.itemcode, B.description, B.uom, B.quantity,
C.whsecode, MAX(C.quantity) AS whseqty, D.rate
FROM
Mrhdr A
INNER JOIN
Mrdtls B ON A.mrno = B.mrno
INNER JOIN
inventoryTable C ON B.itemcode = C.itemcode
INNER JOIN
Items D ON B.itemcode = D.itemcode
WHERE
(A.mrno = #MRNo AND B.quantity < C.quantity);
The error:
Column 'Mrhdr.mrno' is invalid in the select list because it is not
contained in either an aggregate function or the GROUP BY clause.
It says that the column mrno is not contained in the aggregate function of something, but when I do something about it like put it in a GROUP BY clause, the next column requests return the same error until the last column except the C.quantity column, and when they are all in a GROUP BY clause it will only return the same output not returning the highest or maximum value for the quantity. What should I do with the other columns when I use MAX or aggregate functions.
The output of the query above:
If I put all of the columns in a GROUP BY clause it returns an output with two of the itemcode FG 4751, it just removes the error of aggregate function, but I just want the highest value to be returned (just the 100, the highest quantity in the warehouse/inventory).
You want to deal with the maximum inventory quantity per product. But you are joining all inventory rows, where you should only pick the maximum quantity rows.
This can be done with a lateral join, if your DBMS supports this (you have forgotton to tell us which you are using) or simply by joining the rows in question by applying a window function as follows.
SELECT
A.mrno, A.remarks,
B.itemcode, B.description, B.uom, B.quantity,
C.whsecode, C.whseqty, D.rate
FROM
Mrhdr A
INNER JOIN
Mrdtls B ON A.mrno = B.mrno
INNER JOIN
(
SELECT
itemcode, whsecode, quantity as whseqty,
MAX(quantity) OVER (PARTITION BY itemcode) AS max_qty
FROM inventoryTable
) C ON B.itemcode = C.itemcode AND C.whseqty = C.max_qty
INNER JOIN
Items D ON B.itemcode = D.itemcode
WHERE
A.mrno = #MRNo AND B.quantity < C.whseqty;
This query should work in most DBMS. If you are working with a DBMS that supports the standard SQL FETCH WITH TIES clause, I'd change the join to:
INNER JOIN
(
SELECT itemcode, whsecode, quantity as whseqty
FROM inventoryTable
ORDER BY RANK() OVER (PARTITION BY itemcode ORDER BY quantity DESC)
FETCH FIRST ROW WITH TIES
) C ON B.itemcode = C.itemcode
so as to only select the top rows inside the subquery already and not to awkwardly filter them later. But well, a lateral join may even be considered more straight-forward here.
The alternative to putting everything in a group by clause would be to use a window function. The question then becomes what is the MAX value relative to?
For example, you could get the MAX value based on all criteria, which would return a similar result to group by without leaving only distinct values for the column.
SELECT
A.mrno,
A.remarks,
B.itemcode,
B.description,
B.uom,
B.quantity,
C.whsecode,
MAX(C.quantity) OVER(PARTITION BY A.mrno, A.remarks, B.itemcode, B.description, B.uom, B.quantity, C.whsecode, D.rate) AS whseqty,
D.rate
FROM
Mrhdr A
INNER JOIN
Mrdtls B ON A.mrno = B.mrno
INNER JOIN
inventoryTable C ON B.itemcode = C.itemcode
INNER JOIN
Items D ON B.itemcode = D.itemcode
WHERE
(A.mrno = #MRNo AND B.quantity < C.quantity);

adding column to select gives group_by error

I have a following query:
SELECT transactions.created,
installments.*,
transactions.installment_id,
Sum(transactions.amount) AS s,
Sum(installments.amount) AS s1
FROM installments
LEFT OUTER JOIN transactions
ON transactions.installment_id = installments.id
WHERE installments.customer_id = ?
GROUP BY installments.id
which gives me error
org.h2.jdbc.JdbcSQLException: Column "TRANSACTIONS.CREATED" must be in
the GROUP BY list; SQL statement:
But I need the created field and don't want to use it in GROUP_BY.
SELECT *,s,S1,created
FROM installments
LEFT OUTER JOIN
(
SELECT SUM(transactions.amount) AS s, SUM(installments.amount) AS
s1,created
FROM transactions
GROUP BY created
) A ON A.installment_id = installments.id
WHERE installments.customer_id=?
Including it in the GROUP BY fixes the syntax problem:
SELECT t.created, i.*,
Sum(t.amount) AS s,
Sum(i.amount) AS s1
FROM installments i LEFT OUTER JOIN
transactions t
ON t.installment_id = i.id
WHERE i.customer_id = ?
GROUP BY i.id, t.created;
But the real issue is that you might have multiple transactions. Which created value do you want? You probably intend something more like:
SELECT Min(t.created) as created, i.*,
Sum(t.amount) AS s,
Sum(i.amount) AS s1
FROM installments i LEFT OUTER JOIN
transactions t
ON t.installment_id = i.id
WHERE i.customer_id = ?
GROUP BY i.id;
I should note that the calculation of s1 is probably wrong, because it is getting multiplied by the number of transactions. I'm thinking something more like this may be what you intend:
SELECT t.created, i.*,
t.amount as s,
Sum(i.amount) as s1
FROM installments i LEFT OUTER JOIN
(SELECT t.installment_id, t.created as created,
SUM(t.amount) as s
FROM transactions t
GROUP BY t.installment_id
) t
ON t.installment_id = i.id
WHERE i.customer_id = ?
GROUP BY i.id, t.created, t.amount;

From subquery giving error in oracle

I am having this query :
SELECT T.custno,T.custlastname,AVG(T.OrderAmount) , T.OrderCount
FROM(
SELECT A.custno,A.custlastname,count(b.ordno) as OrderCount, sum(c.qty*d.prodprice) AS OrderAmount
FROM customer A
JOIN ordertbl B ON A.custno=b.custno
JOIN ordline C ON b.ordno=c.ordno
JOIN product D ON c.prodno=d.prodno
WHERE A.custstate='CO'
GROUP BY A.custno,A.custlastname, b.ordno) AS T
GROUP BY T.custno,T.custlastname;
I get this error :
ORA-00933: SQL command not properly ended
When i execute inner subquery explicitly, it runs fine. Please let me know the reason.
One can try at http://sqlfiddle.com/#!4/6b48d/12
I guess You are missing one column in group by clause in outer query. Try the below query
SELECT T.custno,T.custlastname,AVG(T.OrderAmount) , T.OrderCount
FROM (SELECT A.custno,
A.custlastname,
count(b.ordno) as OrderCount,
sum(c.qty*d.prodprice) AS OrderAmount
FROM customer A
JOIN ordertbl B ON A.custno=b.custno
JOIN ordline C ON b.ordno=c.ordno
JOIN product D ON c.prodno=d.prodno
WHERE A.custstate='CO'
GROUP BY A.custno,A.custlastname, b.ordno) T
GROUP BY T.custno,T.custlastname,T.OrderCount;
Try this :
SELECT T.custno,T.custlastname,avg(T.OrderAmount) , T.OrderCount
FROM (SELECT A.custno,
A.custlastname,
count(b.ordno) as OrderCount,
sum(c.qty*d.prodprice) AS OrderAmount
FROM customer A
JOIN ordertbl b ON A.custno=b.custno
JOIN ordline c ON b.ordno=c.ordno
JOIN product d ON c.prodno=d.prodno
WHERE A.custstate='CO'
GROUP BY A.custno,A.custlastname,b.ordno,OrderAmount,
OrderCount) AS T)
GROUP BY T.custno,T.custlastname,T.OrderAmount,T.OrderCount;

Need hints on seemingly simple SQL query

I'm trying to do something like:
SELECT c.id, c.name, COUNT(orders.id)
FROM customers c
JOIN orders o ON o.customerId = c.id
However, SQL will not allow the COUNT function. The error given at execution is that c.Id is not valid in the select list because it isn't in the group by clause or isn't aggregated.
I think I know the problem, COUNT just counts all the rows in the orders table. How can I make a count for each customer?
EDIT
Full query, but it's in dutch... This is what I tried:
select k.ID,
Naam,
Voornaam,
Adres,
Postcode,
Gemeente,
Land,
Emailadres,
Telefoonnummer,
count(*) over (partition by k.id) as 'Aantal bestellingen',
Kredietbedrag,
Gebruikersnaam,
k.LeverAdres,
k.LeverPostnummer,
k.LeverGemeente,
k.LeverLand
from klanten k
join bestellingen on bestellingen.klantId = k.id
No errors but no results either..
When using an aggregate function like that, you need to group by any columns that aren't aggregates:
SELECT c.id, c.name, COUNT(orders.id)
FROM customers c
JOIN orders o ON o.customerId = c.id
GROUP BY c.id, c.name
If you really want to be able to select all of the columns in Customers without specifying the names (please read this blog post in full for reasons to avoid this, and easy workarounds), then you can do this lazy shorthand instead:
;WITH o AS
(
SELECT CustomerID, CustomerCount = COUNT(*)
FROM dbo.Orders GROUP BY CustomerID
)
SELECT c.*, o.OrderCount
FROM dbo.Customers AS c
INNER JOIN dbo.Orders AS o
ON c.id = o.CustomerID;
EDIT for your real query
SELECT
k.ID,
k.Naam,
k.Voornaam,
k.Adres,
k.Postcode,
k.Gemeente,
k.Land,
k.Emailadres,
k.Telefoonnummer,
[Aantal bestellingen] = o.klantCount,
k.Kredietbedrag,
k.Gebruikersnaam,
k.LeverAdres,
k.LeverPostnummer,
k.LeverGemeente,
k.LeverLand
FROM klanten AS k
INNER JOIN
(
SELECT klantId, klanCount = COUNT(*)
FROM dbo.bestellingen
GROUP BY klantId
) AS o
ON k.id = o.klantId;
I think this solution is much cleaner than grouping by all of the columns. Grouping on the orders table first and then joining once to each customer row is likely to be much more efficient than joining first and then grouping.
The following will count the orders per customer without the need to group the overall query by customer.id. But this also means that for customers with more than one order, that count will repeated for each order.
SELECT c.id, c.name, COUNT(orders.id) over (partition by c.id)
FROM customers c
JOIN orders ON o.customerId = c.id

SQL Find next Date for each Customer, SQL For each?

I have some problems with an SQL statement. I need to find the next DeliveryDate for each Customer in the following setup.
Tables
Customer (id)
DeliveryOrder (id, deliveryDate)
DeliveryOrderCustomer (customerId, deliveryOrderId)
Each Customer may have several DeliveryOrders on the same deliveryDate. I just can't figure out how to only get one deliveryDate for each customer. The date should be the next upcoming DeliveryDate after today. I feel like I would need some sort of "for each" here but I don't know how to solve it in SQL.
Another simpler version
select c.id, min(o.date)
from customer c
inner join deliveryordercustomer co o on co.customerId = c.id
inner join deliveryorder o on co.deliveryOrderId = o.id and o.date>getdate()
group by c.id
This would give the expected results using a subselect. Take into account that current_date may be rdbms specific, it works for Oracle.
select c.id, o.date
from customer c
inner join deliveryordercustomer co o on co.customerId = c.id
inner join deliveryorder o on co.deliveryOrderId = o.id
where o.date =
(select min(o2.date)
from deliveryorder o2
where o2.id = co.deliveryOrderId and o2.date > current_date)
You need to use a group by. There's a lot of ways to do this, here's my solution that takes into account multiple orders on same day for customer, and allows you to query different delivery slots, first, second etc. This assumes Sql Server 2005 and above.
;with CustomerDeliveries as
(
Select c.id, do.deliveryDate, Rank()
over (Partition BY c.id order by do.deliveryDate) as DeliverySlot
From Customer c
inner join DeliveryOrderCustomer doc on c.id = doc.customerId
inner join DeliveryOrder do on do.id = doc.deliveryOrderId
Where do.deliveryDate>GETDATE()
Group By c.id, do.deliveryDate
)
Select id, deliveryDate
From CustomerDeliveries
Where DeliverySlot = 1