Use COUNT and JOIN in a same query SQL - sql

My tables:
Customers:
+------+----+
| Name | ID |
+------+----+
| Phu | 12 |
| Nam | 23 |
| Mit | 33 |
+------+----+
Orders:
+----+------------+
| ID | Order |
+----+------------+
| 12 | Laptop |
| 12 | Mouse |
| 33 | Smartphone |
| 23 | Keyboard |
| 33 | Computer |
+----+------------+
I want to get output like this:
+------+--------+
| Name | Orders |
+------+--------+
| Phu | 2 |
| Mit | 2 |
+------+--------+
I use this query but this doesn't work:
SELECT
Name,
COUNT(*) AS 'Orders'
FROM
Orders a
INNER JOIN
Customers b ON a.ID = b.ID
GROUP BY
a.ID
HAVING
COUNT(*) > 1;
It has the error like this:
Column 'Customers.Name' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Any help is really appreciated. Thank you.

SELECT c.Name, COUNT(o.ID) AS Orders
FROM Customers c INNER JOIN Orders o ON c.ID = o.ID
GROUP BY o.ID
HAVING Orders > 1
Working Sqlfiddle: http://sqlfiddle.com/#!2/869790/4

As mentioned in my comment every column in the select statement has to be in the group by clause.
SELECT Name, COUNT(*) AS 'Orders'
FROM Orders a
INNER JOIN Customers b
ON a.ID = b.ID
GROUP BY b.Name
HAVING COUNT(*)>1;

Related

Can someone help me figure out if I'm making a mistake in my query?

I'm trying to create a query that returns the names of all people in my database that have less than half of the money of the person with the most money.
These is my query:
select P1.name
from Persons P1 left join
AccountOf A1 on A1.person_id = P1.id left join
BankAccounts B1 on B1.id = A1.account_id
group by name
having SUM(B1.balance) < MAX((select SUM(B1.balance) as b
from AccountOf A1 left join
BankAccounts B1 on B1.id = A1.account_id
group by A1.person_id
order by b desc
LIMIT 1)) * 0.5
This is the result:
+-------+
| name |
+-------+
| Evert |
+-------+
I have the following tables in the database:
+---------+--------+--+
| Persons | | |
+---------+--------+--+
| id | name | |
| 11 | Evert | |
| 12 | Xavi | |
| 13 | Ludwig | |
| 14 | Ziggy | |
+---------+--------+--+
+--------------+---------+
| BankAccounts | |
+--------------+---------+
| id | balance |
| 11 | 525000 |
| 12 | 750000 |
| 13 | 1900000 |
| 14 | 1600000 |
+--------------+---------+
+-----------+-----------+------------+
| AccountOf | | |
+-----------+-----------+------------+
| id | person_id | account_id |
| 301 | 11 | 12 |
| 302 | 13 | 12 |
| 303 | 13 | 14 |
| 304 | 14 | 11 |
| 305 | 14 | 13 |
+-----------+-----------+------------+
What am I missing here? I should get two entries in the result (Evert, Xavi)
I wouldn't approach the logic this way (I would use window functions). But your final having has two levels of aggregation. That shouldn't work. You want:
having SUM(B1.balance) < (select 0.5 * SUM(B1.balance) as b
from AccountOf A1 join
BankAccounts B1 on B1.id = A1.account_id
group by A1.person_id
order by b desc
limit 1
)
I also moved the 0.5 into the subquery and changed the left join to a join -- the tables need to match to get balances.
I would recommend window functions, if your - undisclosed! - database supports them.
You can join and aggregate just once, and then use a window max() to get the top balance. All that is then left to is to filter in an outer query:
select *
fom (
select p.id, p.name, coalesce(sum(balance), 0) balance,
max(sum(balance)) over() max_balance
from persons p
left join accountof ao on ao.person_id = p.id
left join bankaccounts ba on ba.id = ao.account_id
group by p.id, p.name
) t
where balance > max_balance * 0.5

How to exclude entire Customer ID data based on a condition

I need to exclude all data relating to Customer ID's that have at least one instance where a condition applies. For instance:
This is all my data with conditions
+-------------+----------------+
| Customer ID | Condition |
+-------------+----------------+
| 1 | Contacted |
| 1 | No Answer |
| 1 | Left Voicemail |
| 1 | Spoke to |
| 2 | No Answer |
| 2 | Left Voicemail |
| 3 | Spoke to |
| 3 | No Answer |
| 4 | Contacted |
| 4 | Left Voicemail |
+-------------+----------------+
I need to exclude data with conditions equal to 'Contacted'. Currently, I am using the below code and getting the following results:
SELECT a.customerID,
c.condition
FROM Tablea a
JOIN Tablec c ON
c.customerID = a.customerID
WHERE c.condition NOT LIKE 'Contacted'
+-------------+----------------+
| Customer ID | Condition |
+-------------+----------------+
| 1 | No Answer |
| 1 | Left Voicemail |
| 1 | Spoke to |
| 2 | No Answer |
| 2 | Left Voicemail |
| 3 | Spoke to |
| 3 | No Answer |
| 4 | Left Voicemail |
+-------------+----------------+
However, I would like to exclude all of the Customer ID rows if the Customer ID has the condition. Ideally the code would produce the following:
+-------------+----------------+
| Customer ID | Condition |
+-------------+----------------+
| 2 | No Answer |
| 2 | Left Voicemail |
| 3 | Spoke to |
| 3 | No Answer |
+-------------+----------------+
Any help is greatly appreciated!
This will get you the results you are looking for:
Select customerid, condition from
Tablec c
WHERE customerid NOT IN
(
Select customerid from
Tablec
WHERE condition LIKE 'Contacted'
)
In your above example I am uncertain as to why you are joining to Tablea, as you aren't pulling any data from that table except for customerID, which is already in Tablec.
However if you wanted to do the join, you could do:
Select a.customerID, c.condition from Tablea a
JOIN Tablec c ON c.customerID = a.customerID
WHERE c.customerid NOT IN
(
Select customerid from
Tablec
WHERE condition LIKE 'Contacted'
)
Here's one option using not exists:
select customerid, condition
from Tablec c
where not exists (
select 1
from Tablec c2
where c.customerid = c2.customerid and c2.condition = 'Contacted'
)
This only uses Tablec since it appears to have both customerid and condition. You can always join it back to TableA as needed.
This is slightly hacky but I'm not sure what you're original dataset looks like.
SELECT a.customerID,
c.condition
FROM Tablea a
INNER JOIN Tablec c ON
c.customerID = a.customerID
WHERE c.customerID NOT IN
(
SELECT a2.customerID
FROM Tablec a2
WHERE a2.condition = 'Contacted'
)

PostgreSQL can't make Self Join

I have a table:
| acctg_cath_id | parent | description |
| 1 | 20 | Bills |
| 9 | 20 | Invoices |
| 20 | | Expenses |
| 88 | 30 |
| 89 | 30 |
| 30 | |
And I want to create a self join in order to group my items under a parent.
Have tried this, but it doesn't work:
SELECT
accounting.categories.acctg_cath_id,
accounting.categories.parent
FROM accounting.categories a1, accounting.categories a2
WHERE a1.acctg_cath_id=a2.parent
I get error: invalid reference to FROM-clause entry for table "categories"
When I try:
a.accounting.categories.acctg_cath_id
b.accounting.categories.acctg_cath_id
I get error: cross-database references are not implemented: a.accounting.categories.acctg_cath_id
Desired output:
Expenses (Parent 20)
Bills (Child 1)
Invoices (Child 9)
What am I doing wrong here?
It seems you merely want to sort the rows:
select *
from accounting.categorie
order by coalesce(parent, acctg_cath_id), parent nulls first, acctg_cath_id;
Result:
+---------------+--------+-------------+
| acctg_cath_id | parent | description |
+---------------+--------+-------------+
| 20 | | Expenses |
| 1 | 20 | Bills |
| 9 | 20 | Invoices |
| 30 | | |
| 88 | 30 | |
| 89 | 30 | |
+---------------+--------+-------------+
Your syntax is performing a cross join:
FROM accounting.categories a1, accounting.categories a2
Try the following:
SELECT
a2.acctg_cath_id,
a2.parent
FROM accounting.categories a1
JOIN accounting.categories a2 ON (a1.acctg_cath_id = a2.parent)
;
Examine the DBFiddle.
You don't need grouping, only self join:
select
c.acctg_cath_id parentid, c.description parent,
cc.acctg_cath_id childid, cc.description child
from (
select distinct parent
from categories
) p inner join categories c
on p.parent = c.acctg_cath_id
inner join categories cc on cc.parent = p.parent
where p.parent = 20
You can remove the WHERE clause if you want all the parents with all their children.
See the demo.
Results:
> parentid | parent | childid | child
> -------: | :------- | ------: | :-------
> 20 | Expences | 1 | Bills
> 20 | Expences | 9 | Invoices
You don't need a self-join. You don't need aggregation. You just need a group by clause:
SELECT ac.*
FROM accounting.categories ac
ORDER BY COALESCE(ac.parent, ac.acctg_cath_id),
(CASE WHEN ac.parent IS NULL THEN 1 ELSE 2 END),
ac.acctg_cath_id;

Access Queries comparing two tables

I have two tables in Access, Table A and Table B:
Table MasterLockInsNew:
+----+-------+----------+
| ID | Value | Date |
+----+-------+----------+
| 1 | 123 | 12/02/13 |
| 2 | 1231 | 11/02/13 |
| 4 | 1265 | 16/02/13 |
+----+-------+----------+
Table InitialPolData:
+----+-------+----------+---+
| ID | Value | Date |Type
+----+-------+----------+---+
| 1 | 123 | 12/02/13 | x |
| 2 | 1231 | 11/02/13 | x |
| 3 | 1238 | 10/02/13 | y |
| 4 | 1265 | 16/02/13 | a |
| 7 | 7649 | 18/02/13 | z |
+----+-------+----------+---+
All I want are the rows from table B for IDs not contained in A. My current code looks like this:
SELECT Distinct InitialPolData.*
FROM InitialPolData
WHERE InitialPolData.ID NOT IN (SELECT Distinct InitialPolData.ID
from InitialPolData INNER JOIN
MasterLockInsNew
ON InitialPolData.ID=MasterLockInsNew.ID);
But whenever I run this in Access it crashes!! The tables are fairly large but I don't think this is the reason.
Can anyone help?
Thanks
or try a left outer join:
SELECT b.*
FROM InitialPolData b left outer join
MasterLockInsNew a on
b.id = a.id
where
a.id is null
Simple subquery will do.
select * from InitialPolData
where id not in (
select id from MasterLockInsNew
);
Try using NOT EXISTS:
SELECT Distinct i.*
FROM InitialPolData AS i
WHERE NOT EXISTS (SELECT 1
FROM MasterLockInsNew AS m
WHERE m.ID = i.ID)

Postgres group by columns and within group select other columns by max aggregate

This is probably a standard problem, and I've keyed off some other greatest-n-per-group answers, but so far been unable to resolve my current problem.
A B C
+----+-------+ +----+------+ +----+------+-------+
| id | start | | id | a_id | | id | b_id | name |
+----+-------+ +----+------+ +----+------+-------+
| 1 | 1 | | 1 | 1 | | 1 | 1 | aname |
| 2 | 2 | | 2 | 1 | | 2 | 2 | aname |
+----+-------+ | 3 | 2 | | 3 | 3 | aname |
+----+------+ | 4 | 3 | bname |
+----+------+-------+
In English what I'd like to accomplish is:
For each c.name, select its newest entry based on the start time in a.start
The SQL I've tried is the following:
SELECT a.id, a.start, c.id, c.name
FROM a
INNER JOIN (
SELECT id, MAX(start) as start
FROM a
GROUP BY id
) a2 ON a.id = a2.id AND a.start = a2.start
JOIN b
ON a.id = b.a_id
JOIN c
on b.id = c.b_id
GROUP BY c.name;
It fails with errors such as:
ERROR: column "a.id" must appear in the GROUP BY clause or be used in an aggregate function Position: 8
To be useful I really need the ids from the query, but cannot group on them since they are unique. Here is an example of output I'd love for the first case above:
+------+---------+------+--------+
| a.id | a.start | c.id | c.name |
+------+---------+------+--------+
| 2 | 2 | 3 | aname |
| 2 | 2 | 4 | bname |
+------+---------+------+--------+
Here is a Sqlfiddle
Edit - removed second case
Case 1
select distinct on (c.name)
a.id, a.start, c.id, c.name
from
a
inner join
b on a.id = b.a_id
inner join
c on b.id = c.b_id
order by c.name, a.start desc
;
id | start | id | name
----+-------+----+-------
2 | 2 | 3 | aname
2 | 2 | 4 | bname
Case 2
select distinct on (c.name)
a.id, a.start, c.id, c.name
from
a
inner join
b on a.id = b.a_id
inner join
c on b.id = c.b_id
where
b.a_id in (
select a_id
from b
group by a_id
having count(*) > 1
)
order by c.name, a.start desc
;
id | start | id | name
----+-------+----+-------
1 | 1 | 1 | aname