Count occurrences with exclude criteria - sql

I have a Table
City ID
Austin 123
Austin 123
Austin 123
Austin 145
Austin 145
Chicago 12
Chicago 12
Houston 24
Houston 45
Houston 45
Now I want to count the occurrences of all Citis with different ids so since Chicago has only one id (=12) I am not interested in Chicago and it should not appear in the resultset that should looks like this:
city Id Occurrences
Austin 123 3
Austin 145 2
Houston 34 1
Houston 45 2
I am able to get myself an overview with
select city, Id from Table
group by city, Id
But I am not sure how to only select the once having different ids and to count them.
Could anyone help me out here?

You can use window functions and aggregation:
select city, id, occurences
from (
select city, id, count(*) occurences, count(*) over(partition by city) cnt_city
from mytable
group by city, id
) t
where cnt_city > 1

Related

How to aggregate using distinct values across two columns?

I have the following data in an orders table:
revenue expenses location_1 location_2
3 6 London New York
6 11 Paris Toronto
1 8 Houston Sydney
1 4 Chicago Los Angeles
2 5 New York London
7 11 New York Boston
4 6 Toronto Paris
5 11 Toronto New York
1 2 Los Angeles London
0 0 Mexico City London
I would like to create a result set that has 3 columns:
a list of the 10 DISTINCT city names
the sum of revenue for each city
the sum of expenses for each city
The desired result is:
location revenue expenses
London 6 13
New York 17 33
Paris 10 17
Toronto 15 28
Houston 1 8
Sydney 1 8
Chicago 1 4
Los Angeles 2 6
Boston 7 11
Mexico City 0 0
Is it possible to aggregate on distinct values across two columns? If yes, how would I do it?
Here is a fiddle:
http://sqlfiddle.com/#!9/0b1105/1
Shorter (and often faster):
SELECT location, sum(revenue) AS rev, sum(expenses) AS exp
FROM (
SELECT location_1 AS location, revenue, expenses FROM orders
UNION ALL
SELECT location_2 , revenue, expenses FROM orders
) sub
GROUP BY 1;
May be faster:
WITH cte AS (
SELECT location_1, location_2, revenue AS rev, expenses AS exp
FROM orders
)
SELECT location, sum(rev) AS rev, sum(exp) AS exp
FROM (
SELECT location_1 AS location, rev, exp FROM cte
UNION ALL
SELECT location_2 , rev, exp FROM cte
) sub
GROUP BY 1;
The (materialized!) CTE adds overhead, which may outweigh the benefit. Depends on many factors like total table size, available indexes, possible bloat, available RAM, storage speed, Postgres version, ...
fiddle
You could UNION ALL two queries and then select from it...
select location, sum(rev) as rev, sum(exp) as exp
from (
select location_1 as location, sum(revenue) as rev, sum(expenses) as exp
from orders
group by location_1
union all
select location_2 as location, sum(revenue) as rev, sum(expenses) as exp
from orders
group by location_2
)z
group by location
order by 1

Select unique countries with more than one customer

I need to show the countries that have more than one individual.
Customers
customer_id first_name last_name age country
1 John Doe 31 USA
2 Robert Luna 22 USA
3 David Robinson 22 UK
4 John Reinhardt 25 UK
5 Betty Doe 28 UAE
So the query should return
customer_id first_name last_name age country
1 John Doe 31 USA
2 Robert Luna 22 USA
3 David Robinson 22 UK
4 John Reinhardt 25 UK
I tried tis query but it didn't work.
SELECT last_name, Country
FROM Customers
GROUP BY Country
HAVING COUNT(Customer_id) > 1;
The actual table can be found here
Try using the following query. Thanks
SELECT * FROM CUSTOMERS C
WHERE C.COUNTRY IN (SELECT COUNTRY FROM CUSTOMERS GROUP BY COUNTRY HAVING COUNT(*)>1)
You could use a windowed count as a filter:
with c as (
select *, Count(*) over(partition by country) cnt
from Customers
)
select *
from c
where cnt > 1;

SQL ordering cities ascending and persons descending

I have been stuck in complicated problem. I do not know the version of this SQL, it is school edition. But it is not relevant info now anyway.
I want order cities ascending and numbers descending. With descending numbers I mean when there is same city couple times it orders then biggest number first.
I also need row numbers, I have tried SELECT ROW_NUMBER() OVER(ORDER BY COUNT(FIRST_NAME)) row with no succes.
I have two tables called CUSTOMERS and EMPLOYEES. Both of them having FIRST_NAME, LAST_NAME, CITY.
Now I have this kind of code:
SELECT
CITY, COUNT(FIRST_NAME),
CASE WHEN COUNT(FIRST_NAME) >= 0 THEN 'CUSTOMERS'
END
FROM CUSTOMERS
GROUP BY CITY
UNION
SELECT
CITY, COUNT(FIRST_NAME),
CASE WHEN COUNT(FIRST_NAME) >= 0 THEN 'EMPLOYEES'
END
FROM EMPLOYEES
GROUP BY CITY
This SQL code gives me list like this:
CITY
NEW YORK 2 CUSTOMERS
MIAMI 1 CUSTOMERS
MIAMI 4 EMPLOYEES
LOS ANGELES 1 CUSTOMERS
CHIGACO 1 CUSTOMERS
HOUSTON 1 CUSTOMERS
DALLAS 2 CUSTOMERS
SAN JOSE 2 CUSTOMERS
SEATTLE 2 CUSTOMERS
SEATTLE 5 EMPLOYEES
BOSTON 1 CUSTOMERS
BOSTON 3 EMPLOYEES
I want it look like this:
ROW CITY
1 NEW YORK 2 CUSTOMERS
2 MIAMI 4 EMPLOYEES
3 MIAMI 1 CUSTOMERS
4 LOS ANGELES 1 CUSTOMERS
5 CHIGACO 1 CUSTOMERS
6 HOUSTON 1 CUSTOMERS
7 DALLAS 2 CUSTOMERS
8 SAN JOSE 2 CUSTOMERS
9 SEATTLE 5 EMPLOYEES
10 SEATTLE 2 CUSTOMERS
11 BOSTON 3 EMPLOYEES
12 BOSTON 1 CUSTOMERS
You can use window functions in the ORDER BY:
SELECT c.*
FROM ((SELECT CITY, COUNT(*) as cnt, 'CUSTOMERS' as WHICH
FROM CUSTOMERS
GROUP BY CITY
) UNION ALL
(SELECT CITY, COUNT(*), 'EMPLOYEES'
FROM EMPLOYEES
GROUP BY CITY
)
) c
ORDER BY MAX(cnt) OVER (PARTITION BY city) DESC,
city,
cnt DESC;

SQL query to get only rows match the condition based on two separated columns under one 'group by'

The simple SELECT query would return the data as below:
Select ID, User, Country, TimeLogged from Data
ID User Country TimeLogged
1 Samantha SCO 10
1 John UK 5
1 Andrew NZL 15
2 John UK 20
3 Mark UK 10
3 Mark UK 20
3 Steven UK 10
3 Andrew NZL 15
3 Sharon IRL 5
4 Andrew NZL 25
4 Michael AUS 5
5 Jessica USA 30
I would like to return a sum of time logged for each user grouped by ID
But for only ID numbers where both of these values Country = UK and User = Andrew are included within their rows.
So the output in the above example would be
ID User Country TimeLogged
1 John UK 5
1 Andrew NZL 15
3 Mark UK 30
3 Steven UK 10
3 Andrew NZL 15
First you need to identify which IDs you're going to be returning
SELECT ID FROM MyTable WHERE Country='UK'
INTERSECT
SELECT ID FROM MyTable WHERE [User]='Andrew';
and based on that, you can then filter to aggregate the expected rows.
SELECT ID,
[User],
Country,
SUM(Timelogged) as Timelogged
FROM mytable
WHERE (Country='UK' OR [User]='Andrew')
AND ID IN( SELECT ID FROM MyTable WHERE Country='UK'
INTERSECT
SELECT ID FROM MyTable WHERE [User]='Andrew')
GROUP BY ID, [User], country;
So, you have described what you need to write almost perfectly but not quite. Your result table indicates that you want Country = UK OR User = Andrew, rather than AND
You need to select and group by, then include a WHERE:-
Select ID, User, Country, SUM(Timelogged) as Timelogged from mytable
WHERE Country='UK' OR User='Andrew'
Group by ID, user, country

Order pairs/triplets of rows given the sum of a column

I'd like to order pairs (or group of 3,4 etc.) of rows given the SUM of a certain value.
The rows are consecutive based on the concatenation of Name+Surname+Age
To better understand given the following table:
ID Name Surname Age Salary
------------------------------
1 John Smith 30 2
2 John Smith 30 10
3 Rick James 22 300
4 Rick James 22 1000
5 Rick James 22 5
6 Mike Brown 50 200
7 Mike Brown 50 20
I'd like to have a final table that should be ordered DESC by the sum of Salary of each Name+Surname+Age and keeping the rows with same Name+Surname+Age next to each others despite the ID column is different. This would be the expected result:
ID Name Surname Age Salary
------------------------------
3 Rick James 22 300
4 Rick James 22 1000
5 Rick James 22 5
6 Mike Brown 50 200
7 Mike Brown 50 20
1 John Smith 30 2
2 John Smith 30 10
As you can see the rows with Name+Surname+Age = "Rick Jams 22" are on the top since their total sum would be 1305, followed by "Mike Brown 50" (sum = 220) and "John Smith 30" (sum = 12).
Additionally, the number of rows has to be the same in the resulting table.
How can I do that using Oracle SQL?
Thanks for any help
SELECT t.*,
COALESCE(SUM(salary) OVER (PARTITION BY name, surname, age), 0) ss
FROM mytable t
ORDER BY
ss DESC
Try this:
SELECT ID, Name, Surname, Age, Salary
FROM (
SELECT ID, Name, Surname, Age, Salary,
SUM(Salary) OVER (PARTITION BY Name, Surname, Age) AS sum_of_sal
FROM mytable) t
ORDER BY sum_of_sal DESC, ID
The query uses the window version of SUM in order to calculate the sum of salaries per Name, Surname, Age partition. We can use this field in an outer query to do the sorting.
or try this
SELECT ID, Name, Surname, Age, Salary
FROM mytable
ORDER BY SUM(Salary) OVER (PARTITION BY Name, Surname, Age) DESC, ID