Printing data from sub-queries SQL - sql

I have 2 tables: SalesPeople and Customers that have snum and cnum as primary key respectively; both tables have city column as well.
Without using joins, we have to tell the names of customers and salespeople that belong to same city.
I have used nested queries to print the salespeople that belong to the city of customers, but cant figure out how to print customer names with this .
SELECT S.*
FROM SalesPeople S
WHERE City IN(
SELECT City
FROM Customers CX
);

How about this? (Disregard the fact that the WITH factoring clause doesn't exist in Oracle 9i (at least, I think so); you already have those tables).
Sample data:
SQL> with
2 salespeople (snum, city) as
3 (select 1, 'London' from dual union all
4 select 2, 'Paris' from dual union all
5 select 3, 'Rome' from dual
6 ),
7 customers (cnum, city) as
8 (select 100, 'Zagreb' from dual union all
9 select 101, 'Rome' from dual union all
10 select 102, 'Rome' from dual union all
11 select 103, 'Paris' from dual
12 )
Query:
13 select person_num
14 from (select snum as person_num, city from salespeople
15 union
16 select cnum, city from customers
17 )
18 where city = 'Rome';
PERSON_NUM
----------
3
101
102
SQL>

Related

I want to count city column's values which are repeated

id city
1 London
2 Rome
3 London
4 Rome
Expected output like this:
London Rome
2 2
Using case expression...
How can I solve this query?
This is a standard aggregation + group by + having problem.
Sample data:
SQL> with test (id, city) as
2 (select 1, 'London' from dual union all
3 select 2, 'Rome' from dual union all
4 select 3, 'London' from dual union all
5 select 4, 'Rome' from dual union all
6 select 5, 'Zagreb' from dual
7 )
Query:
8 select city,
9 count(*) cnt
10 from test
11 group by city
12 having count(*) > 1;
CITY CNT
------ ----------
London 2
Rome 2
SQL>
What does case expression have to do with it?
You can get your output using conditional aggregation:
SELECT COUNT(CASE city WHEN 'London' THEN 1 END) AS London,
COUNT(CASE city WHEN 'Rome' THEN 1 END) AS Rome
FROM table_name
WHERE city IN ('London', 'Rome')
Which, for the sample data:
CREATE TABLE table_name (id, city) AS
SELECT 1, 'London' FROM DUAL UNION ALL
SELECT 2, 'Rome' FROM DUAL UNION ALL
SELECT 3, 'London' FROM DUAL UNION ALL
SELECT 4, 'Rome' FROM DUAL;
Outputs:
LONDON
ROME
2
2
However, in this case, it is unclear how to handle your requirement for repeated values if the values were not repeated.
fiddle

How to filter records based on another grouped by column in a same table

I have a sample table as below:
Target output:
I have tried with below script but only number of count produced as output:
select ID, count(COUNTRY) from test
group by COUNTRY
having count(COUNTRY)=3;
Could anyone here please help how can I get the targeted output? Thanks.
Here's one option - a subquery:
Sample data:
SQL> with test (id, name, country) as
2 (select 1, 'Mike' , 'Australia' from dual union all
3 select 2, 'Jason', 'Australia' from dual union all
4 select 3, 'Lee' , 'China' from dual union all
5 select 4, 'Simon', 'India' from dual union all
6 select 5, 'Alex' , 'Malaysia' from dual union all
7 select 6, 'John' , 'Australia' from dual
8 )
Query:
9 select id, country
10 from test
11 where country in (select country
12 from test
13 group by country
14 having count(*) = 3
15 )
16 order by id;
ID COUNTRY
---------- ---------
1 Australia
2 Australia
6 Australia
SQL>
You can use analytic functions and only scan the table once:
SELECT id, name, country
FROM (
SELECT id, name, country,
COUNT(*) OVER (PARTITION BY country) AS people_per_country
FROM table_name
)
WHERE people_per_country = 3;
Which, for the sample data:
CREATE TABLE table_name (id, name, country) AS
SELECT 1, 'Mike' , 'Australia' FROM DUAL UNION ALL
SELECT 2, 'Jason', 'Australia' FROM DUAL UNION ALL
SELECT 3, 'Lee' , 'China' FROM DUAL UNION ALL
SELECT 4, 'Simon', 'India' FROM DUAL UNION ALL
SELECT 5, 'Alex' , 'Malaysia' FROM DUAL UNION ALL
SELECT 6, 'John' , 'Australia' FROM DUAL;
Outputs:
ID
NAME
COUNTRY
1
Mike
Australia
2
Jason
Australia
6
John
Australia
db<>fiddle here

How to get the max count of an attribute with 3 tables?

I need to query which author sold the most books and how many books the author sold.
select a.firstname ||''|| a.lastname as fullname,
max(count(datesold))
from author a,
transaction t,
book b
where a.authorid = b.authorid
and b.bookid = t.bookid
group by
a.firstname,
a.lastname;
It gave me an error of not a single-group group function.
Any idea what is the issue here?
With some sample data
SQL> with
2 author (authorid, firstname, lastname) as
3 (select 1, 'Stephen', 'King' from dual union all
4 select 2, 'Jo' , 'Nesbo' from dual),
5 book (bookid, authorid) as
6 (select 100, 1 from dual union all
7 select 200, 1 from dual union all
8 select 300, 2 from dual
9 ),
10 transaction (trans_id, bookid) as
11 (select 1, 100 from dual union all
12 select 2, 100 from dual union all
13 select 3, 100 from dual union all
14 select 4, 300 from dual
15 ),
query uses the RANK analytic function which ranks rows by number of rows in the transaction table (it says how many books were sold). Finally, fetch row(s) that rank as highest:
16 temp as
17 (select a.firstname || ' ' || a.lastname AS fullname,
18 count(t.bookid) cnt,
19 rank() over (order by count(t.bookid) desc) rnk
20 from author a join book b on a.authorid = b.authorid
21 join transaction t on t.bookid = b.bookid
22 group by a.firstname, a.lastname
23 )
24 select fullname, cnt
25 from temp
26 where rnk = 1;
FULLNAME CNT
------------- ----------
Stephen King 3
SQL>
You can use:
select MAX(a.firstname ||' '|| a.lastname) as fullname,
COUNT(datesold)
from author a
INNER JOIN book b
ON (a.authorid = b.authorid)
INNER JOIN transaction t
ON (b.bookid = t.bookid)
GROUP BY
a.authorid
ORDER BY
COUNT(datesold) DESC
FETCH FIRST ROW ONLY;
Do not aggregate by firstname and lastname as there are many people in the world with identical names and you do not want to count everyone with the same name as a single person.
Which, for the sample data:
CREATE TABLE author (authorid, firstname, lastname, dateofbirth) AS
SELECT 1, 'Alice', 'Adams', DATE '1900-01-01' FROM DUAL UNION ALL
SELECT 2, 'Alice', 'Adams', DATE '1910-01-01' FROM DUAL UNION ALL
SELECT 3, 'Betty', 'Baron', DATE '1920-01-01' FROM DUAL UNION ALL
SELECT 4, 'Carol', 'Corrs', DATE '1930-01-01' FROM DUAL UNION ALL
SELECT 5, 'Carol', 'Corrs', DATE '1940-01-01' FROM DUAL;
CREATE TABLE book (bookid, authorid) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL UNION ALL
SELECT 3, 3 FROM DUAL UNION ALL
SELECT 4, 4 FROM DUAL UNION ALL
SELECT 5, 5 FROM DUAL;
CREATE TABLE transaction (bookid, datesold) AS
SELECT 1, DATE '1970-01-01' FROM DUAL UNION ALL
SELECT 1, DATE '1970-01-02' FROM DUAL UNION ALL
SELECT 1, DATE '1970-01-03' FROM DUAL UNION ALL
SELECT 1, DATE '1970-01-04' FROM DUAL UNION ALL
SELECT 3, DATE '1970-01-01' FROM DUAL UNION ALL
SELECT 4, DATE '1970-01-01' FROM DUAL UNION ALL
SELECT 4, DATE '1970-01-02' FROM DUAL UNION ALL
SELECT 5, DATE '1970-01-01' FROM DUAL UNION ALL
SELECT 5, DATE '1970-01-02' FROM DUAL UNION ALL
SELECT 5, DATE '1970-01-03' FROM DUAL;
Outputs:
FULLNAME
COUNT(DATESOLD)
Alice Adams
4
db<>fiddle here

Hierarchical Query Child Override

I have a hierarchical company structure where each company has an optional company sector. Child companies inherit their parent's sector if they don't have their own, but the child company sector overrides its parent's if the child one.
I need to produce a sum of the bill amount grouped by sector. Companies with no sector are ignored.
The ParentCompanyID column in the Company table is a foreign key to itself. There can be an unlimited number of parent/child levels.
Company Table
CompanyID ParentCompanyID
1 null
2 1
3 2
4 null
5 4
6 null
7 6
8 7
CompanySector Table
CompanyID Sector
1 Distribution
4 Distribution
5 Manufacturing
8 Manufacturing
Timesheet Table
CompanyID BillAmount
1 100
1 200
2 100
3 50
4 25
5 75
6 75
7 115
8 115
The result I expect to see here is:
Sector BillAmount
Distribution 475
Manufacturing 190
Below is how I am currently doing it. It is extremely inefficient and doesn't work for unlimited number of hierarchical levels.
WITH Company AS
( SELECT 1 CompanyID, NULL ParentCompanyID FROM dual
UNION
SELECT 2, 1 FROM dual
UNION
SELECT 3, 2 FROM dual
UNION
SELECT 4, NULL FROM dual
UNION
SELECT 5, 4 FROM dual
UNION
SELECT 6, NULL FROM dual
UNION
SELECT 7, 6 FROM dual
UNION
SELECT 8, 7 FROM dual
),
CompanySector AS
( SELECT 1 CompanyID, 'Distribution' Sector FROM dual
UNION
SELECT 4 , 'Distribution' FROM dual
UNION
SELECT 5 , 'Manufacturing' FROM dual
UNION
SELECT 8 , 'Manufacturing' FROM dual
),
Timesheets AS
( SELECT 1 CompanyID, 100 BillAmount FROM dual
UNION
SELECT 1 CompanyID, 200 BillAmount FROM dual
UNION
SELECT 2 CompanyID, 100 BillAmount FROM dual
UNION
SELECT 3 CompanyID, 50 BillAmount FROM dual
UNION
SELECT 4 CompanyID, 25 BillAmount FROM dual
UNION
SELECT 5 CompanyID, 75 BillAmount FROM dual
UNION
SELECT 6 CompanyID, 75 BillAmount FROM dual
UNION
SELECT 7 CompanyID, 115 BillAmount FROM dual
UNION
SELECT 8 CompanyID, 115 BillAmount FROM dual
),
--Dummy tables above
--My current way of doing it below
companies AS
(SELECT c.*,
cs.sector
FROM company c
LEFT OUTER JOIN CompanySector cs
ON c.companyID = cs.companyID
),
sectors AS
(SELECT levelOne.companyID,
NVL(levelOne.sector, NVL(levelTwo.sector, NVL(levelThree.sector, NULL))) sector
FROM companies levelOne
LEFT OUTER JOIN companies levelTwo
ON levelOne.parentcompanyid = levelTwo.companyID
LEFT OUTER JOIN companies levelThree
ON levelTwo.parentcompanyid = levelThree.companyID
WHERE NVL(levelOne.sector, NVL(levelTwo.sector, NVL(levelThree.sector, NULL))) IS NOT NULL
)
SELECT s.sector,
SUM(t.billamount)
FROM sectors s
INNER JOIN timesheets t
ON s.companyID = t.companyID
GROUP BY sector;
What is a cleaner and more efficient way of doing this?
I believe this will do it. Using the hierarchical query syntax, populate sector from the parent record where needed.
WITH
base_sectors AS (
SELECT * FROM company LEFT OUTER JOIN companySector USING (companyID)
)
, final_sectors AS (
SELECT companyID, NVL( sector, PRIOR sector ) AS sector
FROM base_sectors
START WITH parentCompanyID IS NULL
CONNECT BY parentCompanyID = PRIOR companyID
)
SELECT s.sector,
SUM(t.billamount)
FROM final_sectors s
INNER JOIN timesheets t
ON s.companyID = t.companyID
GROUP BY sector;

Is it possible to compare tuples in oracle-compatible sql?

I'm not 100% if tuples is the term for what I'm talking about but I'm looking at something like this:
Table grades
user grade
------------
Jim B
Bill C
Tim A
Jim B+
I know I can do:
SELECT COUNT(*)
FROM grades
WHERE (
(user = 'Jim' AND grade = 'B')
OR (user = 'Tim' AND grade = 'C')
);
But is there a way to do something more like this?
SELECT COUNT(*)
FROM grades
WHERE (user, grade) IN (('Jim','B'), ('Tim','C'));
EDIT: As a side note, I'd only tested with:
(user, grade) = ('Tim','C')
And that fails, so I assumed IN would fail as well, but I was wrong (thankfully!).
The query you posted should be valid syntax
SQL> ed
Wrote file afiedt.buf
1 with grades as (
2 select 'Jim' usr, 'B' grade from dual
3 union all
4 select 'Bill', 'C' from dual
5 union all
6 select 'Tim', 'A' from dual
7 union all
8 select 'Jim', 'B+' from dual
9 )
10 select *
11 from grades
12 where (usr,grade) in (('Jim','B'),
13 ('Tim','C'),
14* ('Tim','A'))
SQL> /
USR GR
---- --
Jim B
Tim A
You could use a subquery to treat a list of tuples like a table:
SELECT COUNT(*)
FROM grades
JOIN (
SELECT 'Jim' as user, 'B' as grade from dual
UNION ALL
SELECT 'Tim', 'C' from dual
UNION ALL
SELECT 'Pim', 'D' from dual
) as SearchTarget
ON SearchTarget.user = grades.user
and SearchTarget.grade = grades.grade