SQL joining two tables and counting - sql

I want to retrieve all the customers who own more than 1 car.
I have this code:
SELECT c.fname,
c.lname,
c.cid,
Count(v.cid) AS nmbrofvehicle
FROM customer c
LEFT JOIN vehicle v
ON c.cid = v.cid
GROUP BY c.fname;
But it returns this error:
ORA-00979: not a GROUP BY expression

select c.fname, c.lname, c.cid, count(v.cid) as nmbrofvehicle
from customer c left join vehicle v on c.cid = v.cid
group by c.fname, c.lname, c.cid
having count(*) > 1;

The problem you have there is that you are attempting to group in ungrouped data (e.g. surname).
Some databases (such as MySQL) are very forgiving and try to do this - Oracle, however is not.
Try this:
SELECT a.cid
, a.fname
, a.sname
, NVL(b.nmbrofvehicle, 0) AS nmbrofvehicle
FROM customers a
LEFT JOIN ( SELECT z.cid
, COUNT(z.cid) AS nmbrofvehicle
FROM vehicle z
GROUP BY z.cid
) b
ON ( a.cid = b.cid );
This will take data from customers, and left join any matching data from your vehicles table on cid, or return 0 thanks to this NVL function.

Remove c.cid from the select and add c.lname to the group by:
SELECT c.fname, c.lname,
Count(v.cid) AS nmbrofvehicle
FROM customer c JOIN
vehicle v
ON c.cid = v.cid
GROUP BY c.fname, c.lname
HAVING COUNT(*) > 1;
A LEFT JOIN is unnecessary because you are requiring at least one match.

Related

How to make it in one query

I have the following relational table schema
Customer(customer_id, customer_name, customer_city)
Branch(branch_id, branch_name, branch_city)
Account(account_id, balance, customer_id, branch_id)
Question name every customer who has accounts in branches at least two different cities.
It is working for me the next query but only if I create a view first. Is there any other option to do it together?
My solution which works:
Create view Cust as select c.Customer_Name, c.customer_ID, b.branch_name, b.branch_city from Customer as c inner Join Account as a on c.customer_ID=a.customer_ID join Branch as b on b.branch_id=a.branch_id
SELECT * FROM Cust as c inner join Cust as c1 on c.CustomerID=c1.CustomerID and c.branch_city <> c1.branch_city
Here is one option using exists with an aggregate query
select c.*
from customer c
where exists (
select 1
from account a
inner join branch b on b.branch_id = a.branch_id
where a.customer_id = c.customer_id
group by c.customer_id
having min(b.branch_city) <> max(b.branch_city)
)

Select sum from

I am trying to get the sum of all the rows associated with each customer and join on them.
However I am finding that if no rows exist it leaves out the customer completely.
I would prefer if the sum was zero. How would I achieve this.
Here is the SQL statement:
SELECT
Id, DebitSum
FROM
Customers
JOIN
(SELECT
SUM(Amount) DebitSum, CustomerId
FROM
Purchases
WHERE
Completed IS NULL
GROUP BY
CustomerId) p ON p.CustomerId = Id;
Using SQL Server, if it matters.
Just use LEFT JOIN:
SELECT c.Id, COALESCE(p.DebitSum, 0)
FROM Customers c LEFT JOIN
(SELECT SUM(p.Amount) as DebitSum, p.CustomerId
FROM Purchases p
WHERE p.Completed IS NULL
GROUP BY CustomerId
) p
ON p.CustomerId = c.Id;
This would normally be written without the subquery:
SELECT c.Id, COALESCE(SUM(p.Amount), 0) as DebitSum
FROM Customers c LEFT JOIN
Purchases p
ON p.CustomerId = c.Id;
WHERE p.Completed IS NULL
GROUP BY c.Id
You could use LEFT JOIN:
SELECT Id, COALESCE(DebitSum,0) AS DebitSum
FROM Customers
LEFT JOIN (
SELECT SUM(Amount) DebitSum, CustomerId
FROM Purchases
WHERE Completed IS NULL
GROUP BY CustomerId
) p ON p.CustomerId = Id;
Try this:
SELECT Id, DebitSum
FROM Customers
LEFT JOIN (
SELECT SUM(Amount) DebitSum, CustomerId
FROM Purchases
WHERE Completed IS NULL
GROUP BY CustomerId
) p ON p.CustomerId = Id;
Your doing a JOIN which means it has to exist in both tables/data sets. Changing it to LEFT JOIN only requires it to be in the First table and not the one after the LEFT JOIN
You can also use subquery only:
select id, (select coalesce(sum(p.Amount), 0)
from Purchases p
where p.CustomerId = c.id and p.Completed IS NULL
) as DebitSum
from Customers c
group by id;
SELECT SUM(Amount) DebitSum, CustomerId
there is no AS after the information? select sum(amount) as debitsum, customerid
try this
SELECT c.Id, COALESCE(p.DebitSum, 0) as DebitSum
FROM Customers c LEFT JOIN Purchases p
on c.Id = p.CustomerId
where p.completed is null

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.

Display 2 columns from one table having max count in column 3 and display computed sum of values from another table

I've a customer table and purchases table,
need to show cname, cid with max(customer_visits) from customer table
and sum of total_purchases by customer in purchases table.
I'm doing something like this
select p.cid, c.cname, sum(p.total_price)
from customers c where exists
(select max(visits_made) from customers having visits_made=max(visits_made)
and cid=p.cid)
inner join purchases p on p.cid=c.cid
group by p.cid,c.cname
and
select p.cid, c.cname, sum(p.total_price)
(select max(visits_made) from customers c where c.cid=p.cid)
from purchases p
inner join customers c on c.cid=p.cid
group by p.cid,c.cname
What's going wrong with these queries?
Found the solution, had to include where clause after inner join :D
I think this is just an aggregation query:
select p.cid, c.cname, sum(p.total_price) as total_price,
max(visits_made) as visits_made
from purchases p inner join
customers c
on c.cid = p.cid
group by p.cid, c.cname;

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.