From subquery giving error in oracle - sql

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;

Related

Oracle: How to use left outer join to get all entries from left table and satisfying the condition in Where clause

I have the tables below.
Client:
ID | clientName
--------------
1 A1
2 A2
3 A3
Order:
OrdID clientID status_cd
------------------------
100 1 DONE
101 1 SENT
102 3 SENT
Status:
status_cd status_category
DONE COMPL
SENT INPROG
I have to write a query to get all the clients and count of order against all of them, whether the client_id exists in Order table or not and has the orders with "COMPL" as status category.
In this case, I am using the query below but it's filtering out the clients which has no orders. I want to get all clients such that the expected result is as below.
Query:
select c.ID, count(distinct o.OrdID)
from client c, order o, status s
where c.ID=o.client_id(+)
and o.status_cd=s.status_cd where s.status_category='COMPL'
group by c.ID
Expected result:
C.ID count(distinct o.OrdID)
----------------------------
1 1
2 0
3 0
Can someone please help me with this? I know, in this case, left outer join is behaving like inner join when I am using where clause, but is there any other way to achieve the results above?
This can be dealt with a lot easier when using an explicit join operator:
select c.ID, count(distinct s.status_cd)
from client c
left join orders o on o.clientid = c.id
left join status s on s.status_cd = o.status_cd and s.status_category='COMPL'
group by c.ID;
The above assumes that orders.status_cd is defined as not null
Another option is to move the join between orders and status in a derived table:
select c.ID, count(distinct o.ordid)
from client c
left join (
select o.ordid
from orders o
join status s on s.status_cd = o.status_cd
where s.status_category='COMPL'
) o on o.clientid = c.id
group by c.ID;
The above "states" more clearly (at least in my eyes) that only orders within that status category are of interest compared to the first solution
As usual, there are lots of ways to express this requirement.
Try ANSI join people will hate me an vote down this answer ;) :
select c.ID, count(distinct o.OrdID)
from client c, order o, status s
where c.ID = o.client_id(+)
and o.status_cd = s.status_cd
and s.status_category='COMPL'
group by c.ID
;
or
select c.ID
, nvl((select count(distinct o.OrdID)
from order o, status s
where c.ID = o.client_id
and o.status_cd = s.status_cd
and s.status_category='COMPL'
), 0) as order_count
from client c
group by c.ID
;
or
with ord as
(select client_id, count(distinct o.OrdID) cnt
from order o, status s
where 1=1
and o.status_cd = s.status_cd
and s.status_category='COMPL'
group by client_id
)
select c.ID
, nvl((select cnt from ord o where c.ID = o.client_id ), 0) as order_count
from client c
group by c.ID
;
or
...
The second WHERE should be an AND.
Other than that, you need the plus sign, (+), marking left outer join, in the second join condition as well. It is not enough to left-outer-join the first two tables.
Something like
select c.ID, count(distinct o.OrdID)
from client c, order o, status s
where c.ID=o.client_id(+)
and o.status_cd=s.status_cd(+) AND s.status_category='COMPL'
-- ^^^ ^^^ (not WHERE)
group by c.ID
Of course, it would be much better if you used proper (SQL Standard) join syntax.

SQL TOP 1 Syntax for a nested query

New to SQL Server and I am trying to use top 1 to get the company with the most order in my DB within my code that is already working but I don't know how to use it properly. Only missing syntax I think.
Query #1 is working fine:
SELECT
c.CompanyName, COUNT(DISTINCT OrderID) as Nombre_Commande
FROM
Orders O
INNER JOIN
Customers C ON O.CustomerID = c.CustomerID
GROUP BY
c.CompanyName
What I am trying to do
SELECT TOP (1) *
FROM
(SELECT
c.CompanyName, COUNT(DISTINCT OrderID) AS Nombre_Commande
FROM
Orders O
INNER JOIN
Customers C ON O.CustomerID = c.CustomerID
GROUP BY
c.CompanyName)
You need to give the derived table an alias, and also, specifying top without an order by clause is pretty pointless as rows are returned as a set without any order unless the order is explicitly specified with an order by clause:
SELECT TOP (1) *
FROM (
SELECT c.CompanyName, COUNT(DISTINCT OrderID) as Nombre_Commande
FROM Orders O
INNER JOIN Customers C ON O.CustomerID=c.CustomerID
GROUP by c.CompanyName
) AS YourTable
ORDER BY something_meaningful_maybe_nombre_commande?
How about this?
SELECT TOP 1 c.CompanyName, COUNT(DISTINCT OrderID) as Nombre_Commande
FROM Orders O INNER JOIN
Customers C
ON O.CustomerID = c.CustomerID
GROUP by c.CompanyName
ORDER BY Nombre_Commande DESC;
This assumes that Nombre_Commande is what you want to order by.
By the way, I would be surprised if COUNT(DISTINCT) were really needed for this query. COUNT(*) or COUNT(OrderId) should be sufficient.

Pulling my hair with this Syntax Error

I seem to be running into a query syntax error but cannot seem to isolate it. I am using MS Access and when I run the queries I get a syntax error in FROM clause.
I have two tables and they are in a one to many relationship:
Table 1 called (customer) with the following fields:
ID
FirstName
Table 2 called (tblservice) with the following fields:
serviceID
Timing
Total
customerID <-Foreign Key
First Query:
select c.id, c.firstname, avg(s.Total) / (select count(id) from customer) as LifetimeValue
from tblservice as s join customer as c on s.id = c.id
group by s.id
Second Query(30 day span):
select c.id, c.firstname, avg(s.Total) / (select count(id) from customer) as LifetimeValue
from tblservice as s join customer as c on s.id = c.id
where (s.Timing)>=DateAdd("d",-30,Date())
group by s.id
Try this:
select c.id, c.firstname, avg(s.Total) / count(c.id) as LifetimeValue
from tblservice as s inner join customer as c on s.id = c.id
group by c.id, c.firstname
and
select c.id, c.firstname, avg(s.Total) / count(c.id) as LifetimeValue
from tblservice as s inner join customer as c on s.id = c.id
where (s.Timing)>=DateAdd("d",-30,Date())
group by c.id, c.firstname
You cannot select c.id and c.firstname unless you group by them. And you can use count(c.id) since you are already grouping by c.id. I have not used SQL in MS Access though. So I am not 100% sure. Try it out.
MS Access may require you to use INNER JOIN instead of just JOIN.

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

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

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]