alternatives to "Having" in SQL - sql

I'm in the DB classes, and I have to understand and show that it is possible to get the results of HAVING and GROUP BY without using themselves. I searched for alternatives for those 2 commands, but I didn't understand anything.
Can someone give me examples?
select n_name
from(
select n_name, count( distinct c_custkey) AS aa
from nation AS n INNER JOIN customer AS c ON c.c_nationkey=n.n_nationkey
GROUP BY n_name
HAVING aa=(select MAX(cc)
from ( select n_name, count(distinct c_custkey) AS cc
from nation AS n
INNER JOIN customer AS c ON c.c_nationkey=n.n_nationkey
GROUP BY n_name
ORDER BY n_name))
ORDER BY n_name);

As a solution, you can use WITH to combine duplicate parts of the request and do the check through INNER JOIN:
WITH
base_data AS (
SELECT n_name, COUNT(DISTINCT c_custkey) AS count
FROM nation AS n INNER JOIN customer AS c ON c.c_nationkey=n.n_nationkey
GROUP BY n_name
)
SELECT n_name
FROM
base_data
INNER JOIN (
SELECT MAX(count) AS max_count
FROM base_data
) AS max_data ON base_data.count = max_data.max_count
ORDER BY n_name

Related

Can I sum the count of two columns from two different tables?

I'm trying to add together the counts of two different tables and group them by the same variable
Here is what I have so far:
SELECT a.storenumber,
Count (howmanytotal) AS total_counts_store
FROM (
SELECT month_counts.howmany,
new_counts.howmany) AS howmanytotal
from (
SELECT a.storenumber,
count (b.riid_) AS howmany
FROM $b$ b
INNER JOIN $a$ a
ON b.riid_=a.riid_
GROUP BY a.storenumber) month_counts
FROM (
SELECT a.storenumber,
count (c.riid_) AS howmany
FROM $c$ c
INNER JOIN $a$ a
ON c.riid_=a.riid_
GROUP BY a.storenumber) new_counts
ON month_counts.storenumber = new_counts.storenumber) theend
where I'm at now:
SELECT howmanytotal AS total_counts_store
FROM (
SELECT Count (howmany) AS howmanytotal)
FROM (
SELECT month_counts.howmany,
new_counts.howmany)
FROM (
SELECT a.storenumber,
count (b.riid_) AS howmany
FROM $b$ b
inner join $a$ a
ON b.riid_=a.riid_
GROUP BY a.storenumber) month_counts
UNION
(
SELECT count (c.riid_) AS howmany
FROM $c$ c
inner join $a$ a
ON c.riid_=a.riid_
GROUP BY a.storenumber) new_counts
ON month_counts.storenumber = new_counts.storenumber) ORDER BY $a$.storenumber
Getting this error: Error: java.sql.SQLSyntaxErrorException: ORA-00923: FROM keyword not found where expected
Please correct SELECT statement:
Join the subqueries:
select
storenumber,
month_counts.howmany as month_count,
new_counts.howmany as new_count,
month_counts.howmany + new_counts.howmany as total_count
from (...) month_counts
join (...) new_counts using (storenumber)
order by storenumber;
If it is possible for a storenumber to be missing from one of the subquery results, then outer join and use COALESCE or NVL to deal with the nulls. Here is a query with a full outer join, which is not available in MySQL, but in Oracle and many other DBMS.
select
storenumber,
month_counts.howmany as month_count,
new_counts.howmany as new_count,
nvl(month_counts.howmany, 0) + nvl(new_counts.howmany, 0) as total_count
from (...) month_counts
full outer join (...) new_counts using (storenumber)
order by storenumber;
Ending up using sum and union to complete. Thank you for your help.
SELECT storenumber,
SUM(howmany) AS howmanytotal
FROM (SELECT a.storenumber,
Count (b.riid_) AS howmany
FROM $b$ b
inner join $a$ a
ON b.riid_ = a.riid_
GROUP BY a.storenumber
UNION
SELECT a.storenumber,
Count (c.riid_) AS howmany
FROM $c$ c
inner join $a$ a
ON c.riid_ = a.riid_
GROUP BY a.storenumber)
GROUP BY storenumber
ORDER BY storenumber
This gave me a list of store ids and how many active subscribers we have at each store (taken from two separate tables)

All combinations of counts

I have a table, with columns
product_id, status, for example:
product_id
status
1
ok
2
bad
1
ok
3
bad
2
bad
1
ok
I'd like to show count of all possible combinations of product_ID and status:
product_id
status
count
1
ok
3
1
bad
0
2
ok
0
2
bad
2
3
ok
0
3
bad
1
The solution I've found is that I could use a Cartesian join and then union it with regular counts and aggregate the results (works fine):
SELECT product_id, status, SUM(cnt) FROM (
---all combinations, no count
SELECT DISTINCT t1.product_id, t2.status, 0 AS cnt
FROM
details t1,
details t2
UNION
---counts of existing combinations
SELECT DISTINCT product_id, status, COUNT(status) AS cnt
FROM details
GROUP BY product_id, status) AS T1
GROUP BY product_id, status
Now I am wondering, is here a better way to do it?
I learning SQL with PostgreSQL and Access SQL. Comments are added to clarify (left-out in Access code).
Use CROSS JOIN to build all combinations and top up with a LEFT JOIN:
SELECT p.product_id, s.status, COUNT(t.any_not_null_column)
FROM (SELECT DISTINCT product_id FROM t) AS p
CROSS JOIN (SELECT DISTINCT status FROM t) AS s
LEFT JOIN t ON p.product_id = t.product_id AND s.status = t.status
GROUP BY p.product_id, s.status
The following is a Postgres solution (a database I strongly recommend over MS Access). The idea is to generate all the rows and then use left join and group by to get the counts you want:
select p.product_id, s.status, count(d.product_id)
from (select distinct product_id from details) p cross join
(values ('ok'), ('bad')) s left join
details d
on d.product_id = p.product_id and d.status = s.status
group by p.product_id, s.status;
Note: You might have other tables that have the list of products and/or statuses that you want.
An equivalent version in MS Access (which would also work in Postgres) might look like:
select p.product_id, s.status, count(d.product_id)
from ((select distinct product_id from details) p,
(select distinct status from details) s
) left join
details d
on d.product_id = p.product_id and
d.status = s.status
group by p.product_id, s.status;

Selecting rows with the most repeated values at specific column

Problem in general words: I need to select value from one table referenced to the most repeated values in another table.
Tables have this structure:
screenshot
screenshot2
The question is to find country which has the most results from sportsmen related to it.
First, INNER JOIN tables to have relation between result and country
SELECT competition_id, country FROM result
INNER JOIN sportsman USING (sportsman_id);
Then, I count how much time each country appear
SELECT country, COUNT(country) AS highest_participation
FROM (SELECT competition_id, country FROM result
INNER JOIN sportsman USING (sportsman_id))
GROUP BY country
;
And got this screenshot3
Now it feels like I'm one step away from solution ))
I guess it's possible with one more SELECT FROM (SELECT ...) and MAX() but I can't wrap it up?
ps:
I did it with doubling the query like this but I feel like it's so inefficient if there are millions of rows.
SELECT country
FROM (SELECT country, COUNT(country) AS highest_participation
FROM (SELECT competition_id, country FROM result
INNER JOIN sportsman USING (sportsman_id)
) GROUP BY country
)
WHERE highest_participation = (SELECT MAX(highest_participation)
FROM (SELECT country, COUNT(country) AS highest_participation
FROM (SELECT competition_id, country FROM result
INNER JOIN sportsman USING (sportsman_id)
) GROUP BY country
))
Also I did it with a view
CREATE VIEW temp AS
SELECT country as country_with_most_participations, COUNT(country) as country_participate_in_#_comp
FROM(
SELECT country, competition_id FROM result
INNER JOIN sportsman USING(sportsman_id)
)
GROUP BY country;
SELECT country_with_most_participations FROM temp
WHERE country_participate_in_#_comp = (SELECT MAX(country_participate_in_#_comp) FROM temp);
But not sure if it's easiest way.
If I understand this correctly you want to rank the countries per competition count and show the highest ranking country (or countries) with their count. I suggest you use RANK for the ranking.
select country, competition_count
from
(
select
s.country,
count(*) as competition_count,
rank() over (order by count(*) desc) as rn
from sportsman s
inner join result r using (sportsman_id)
group by s.country
) ranked_by_count
where rn = 1
order by country;
If the order of the result rows doesn't matter, you can shorten this to:
select s.country, count(*) as competition_count
from sportsman s
inner join result r using (sportsman_id)
group by s.country
order by count(*) desc
fetch first rows with ties;
You seem to be overcomplicating this. Starting from your existing join query, you can aggregate, order the results and keep the top row(s) only.
select s.country, count(*) cnt
from sportsman s
inner join result r using (sportsman_id)
group by s.country
order by cnt desc
fetch first 1 row with ties
Note that this allows top ties, if any.
SELECT country
FROM (SELECT country, COUNT(country) AS highest_participation
FROM (SELECT competition_id, country FROM result
INNER JOIN sportsman USING (sportsman_id)
) GROUP BY country
order by 2 desc
)
where rownum=1

Aggregation while using count(distinct)

I have a table with 50 columns named as public_report. I want count(distinct) of a specific column along with all the columns as I need to run this in Tableau
select count(distinct l.lead_key), l.*
from public_report l
group by l.lead_key
I am facing this error while i execute a query for
Error occurred while trying to execute a query: [SQLState 42803]
ERROR: column "l.enquiry_key" must appear in the GROUP BY clause or be
used in an aggregate function
II tried adding l.enquiry_key but it threw a other column name as well. have around 50 columns can anyone suggest.
Also tried this
select t.lk, t.c, lp.*
from (select lead_key lk, count(distinct lead_key) c
from public_report
group by lead_key) t
join public_report lp
on lp.lead_key = t.lk
but this is not giving me the correct count.
This table has 8700000 distinct lead_key values. But I am getting 14565498 as count d value
Please help
Perhaps a correlated subquery is what you want?
select p.*, (select count(*) from public_report p2
where p2.lead_key = p.lead_key)
from public_report p
Is this what you want?
select lp.*, t.count_distinct
from public_report lp cross join
(select count(distinct lead_key) as count_distinct
from public_report
) t;
Or, because most databases support count(distinct) as a window function:
select pr.*, count(distinct lead_key) over () as count_distinct
from public_report pr
If youwantknow the number of distinct lead_key You should not group by for the same row you want aggregated
select count(distinct l.lead_key)
from public_report l
if you want the count for each key then use group by ket and count(*)
select l.lead_key, count(*)
from public_report l
group by l.lead_key
if you want count(distinct l.lead_key) aside the others column you could use a cross join
select lp.*, t. my_count
from public_report lp
cross join (
select count(distinct l.lead_key) my_count
from public_report l
) t
if you want the count of each lead_key aside the the others columns you could use an inner join with subwuery with group by
select lp.*, t.my_lk_count
from public_report lp
inner join (
select l.lead_key, count(*) my_lk_count
from public_report l
group by l.lead_key
) t ON t.lead_key = lp.lead_key

Joining two tables and displaying the count of records based on country in Northwind schema

In oracle, i have two tables - Supplier and customer. Each has a field name 'Country'
I have to find the total number of suppliers and customers by Country and then add these two counts to display as a single count for each country.
I have two queries, one for each table(supplier and customer). But how to combine them?
SELECT S.Country, count(distinct(S.CompanyName)) as cnt
from NW_Suppliers S
group by S.Country
SELECT C.Country, count(distinct(C.CustomerID)) as cnt1
from NW_Customers C
group by C.Country
Can anyone please help?
Just join the two together:
SELECT COALESCE( S.Country, C.Country ) AS country,
COALESCE( c.cnt, 0 ) + COALESCE( s.cnt, 0 ) AS total
FROM ( SELECT Country,
COUNT( DISTINCT CompanyName ) AS cnt
FROM NW_Suppliers
GROUP BY Country
) S
FULL OUTER JOIN
( SELECT Country,
COUNT( DISTINCT CustomerID ) AS cnt
FROM NW_Customers
GROUP BY Country
) C
ON ( s.country = c.country )
Or UNION the two tables:
SELECT Country,
COUNT( DISTINCT name ) AS total
FROM (
SELECT Country, CompanyName AS Name
FROM NW_Suppliers
UNION
SELECT Country, CustomerID
FROM NW_Customers
)
GROUP BY Country
They will give slightly different answers depending on how you want to treat duplicates between the two tables but its unclear from your description what your intended behaviour should be.
SELECT S.Country, count(distinct(S.CompanyName)) as cnt
from NW_Suppliers S
group by S.Country
Union all
SELECT C.Country, count(distinct(C.CustomerID)) as cnt
from NW_Customers C
group by C.Country