Find the discrepancies in sql - sql

I am using Oracle database.
Suppose that I have a table called "MYTABLE" and it contains the tuples of every dog in the world and the owners:
NAME
OWNER_ID
Aaron
81
Aaron
281
Aaron
404
Michael
81
Michael
281
Michael
404
Brendan
281
Brendan
81
Micon
404
Micon
81
Tyson
404
For DEFAULT every DOG must be associated with 3 different owners, in this case the owners are identified by an id: 81, 281 and 404.
How can I know the dogs that are not associated with 3 rows in the table?
I would like this output:
Brendan
Micon
Tyson
These 3 dogs do not have 3 rows in the given table. They are not associated exactly with owners 81, 281 and 404.

To find out which name is not associated with all three of the 81, 281 and 404 owner_id, you can use conditional aggregation in a HAVING clause:
SELECT name
FROM table_name
GROUP BY name
HAVING COUNT(DISTINCT CASE WHEN OWNER_ID IN (81, 281, 404) THEN OWNER_ID END) < 3
Which, for the sample data:
CREATE TABLE table_name (NAME, OWNER_ID) AS
SELECT 'Aaron', 81 FROM DUAL UNION ALL
SELECT 'Aaron', 281 FROM DUAL UNION ALL
SELECT 'Aaron', 404 FROM DUAL UNION ALL
SELECT 'Michael', 81 FROM DUAL UNION ALL
SELECT 'Michael', 281 FROM DUAL UNION ALL
SELECT 'Michael', 404 FROM DUAL UNION ALL
SELECT 'Brendan', 281 FROM DUAL UNION ALL
SELECT 'Brendan', 81 FROM DUAL UNION ALL
SELECT 'Brendan', 123 FROM DUAL UNION ALL -- Different owner_id
SELECT 'Micon', 404 FROM DUAL UNION ALL
SELECT 'Micon', 81 FROM DUAL UNION ALL
SELECT 'Micon', 81 FROM DUAL UNION ALL -- Duplicate
SELECT 'Tyson', 404 FROM DUAL
Outputs:
NAME
Brendan
Micon
Tyson
If you just want the name where they do not have three different owner_id (any owner_id) then:
SELECT name
FROM table_name
GROUP BY name
HAVING COUNT(DISTINCT OWNER_ID) < 3
Which, for the same sample data, would output:
NAME
Micon
Tyson
If you just want the name where there are not three owner_id (either unique or not) then:
SELECT name
FROM table_name
GROUP BY name
HAVING COUNT(OWNER_ID) < 3
Which, for the same sample data, outputs:
NAME
Tyson
db<>fiddle here

You can GROUP BY the NAME and the use COUNT(*) to exclude al that have less than 3 owners
SELECT "NAME" FROM tab1 GROUP BY "NAME" HAVING COUNT(*) < 3
| NAME |
| :------ |
| Brendan |
| Micon |
| Tyson |
db<>fiddle here

Related

SQL needed to find if a property exists in multiple counties

I need some SQL to determine if a property exists in multiple counties.
I have a list of distinct property ids and county ids, but I'm not sure how to find if the property exists in more than one county.
TABLE: PROPERTIES
PROPERTYID
COUNTYID
12345
1111
12345
1112
23456
1111
34567
2222
In this example, I need some sql that will only show me property 12345 since it exists in both county 1111 and 1112.
I'm sure there is some easy SQL, but I can't figure it out.
Sample data:
SQL> with properties (propertyid, countryid) as
2 (select 12345, 1111 from dual union all
3 select 12345, 1112 from dual union all
4 select 23456, 1111 from dual union all
5 select 34567, 2222 from dual
6 )
Query:
7 select propertyid
8 from properties
9 group by propertyid
10 having count(distinct countryid) > 1;
PROPERTYID
----------
12345
SQL>

Show mapping based on student access

There is a table which has following data:
student subject code
student1 maths 312
student1 physics 785
student2 english 900
student3 geography 317
I am trying to restrict access to each student in the table to view data specific to their chosen subject. But there is one restriction to show maths data to student2. Thereby both student1 and student2 both would be able to see maths data, and this mapping has to be done without altering the master data. So only while displaying the table, student2 should be mapped to both english and maths.
Thanks for the help here!
One option is to - as you said - temporarily use UNION set operator. Something like this:
SQL> WITH
2 test (student, subject, code)
3 AS
4 (SELECT 'student1', 'maths', 312 FROM DUAL
5 UNION ALL
6 SELECT 'student1', 'physics', 785 FROM DUAL
7 UNION ALL
8 SELECT 'student2', 'english', 900 FROM DUAL
9 UNION ALL
10 SELECT 'student3', 'geography', 317 FROM DUAL)
11 SELECT *
12 FROM test
13 WHERE student = '&&par_student'
14 -- add this to your query
15 UNION
16 SELECT 'student2', 'maths', NULL
17 FROM DUAL
18 WHERE '&&par_student' = 'student2';
Enter value for par_student: student1 --> student1 is OK, it has two subjects
STUDENT SUBJECT CODE
-------- --------- ----------
student1 maths 312
student1 physics 785
SQL> undefine par_student
SQL> /
Enter value for par_student: student2 --> for student2, UNION is used
STUDENT SUBJECT CODE
-------- --------- ----------
student2 english 900
student2 maths
SQL> undefine par_student
SQL> /
Enter value for par_student: student3 --> nothing new for student3
STUDENT SUBJECT CODE
-------- --------- ----------
student3 geography 317
SQL>
Depending on tool you use, parameter might look as this (in e.g. TOAD):
WHERE student = :par_student
or any other way parameters are used in that tool of yours.
Something like:
SELECT student, subject, code
FROM (
SELECT t.*,
COUNT(
CASE
WHEN student = :your_student
OR (:your_student, subject) IN (('student2', 'maths'))
THEN 1
END
) OVER (PARTITION BY subject) AS has_access
FROM table_name t
)
WHERE has_access > 0
Then, for the sample data:
CREATE TABLE table_name (student, subject, code) AS
SELECT 'student1', 'maths', 312 FROM DUAL UNION ALL
SELECT 'student1', 'physics', 785 FROM DUAL UNION ALL
SELECT 'student2', 'english', 900 FROM DUAL UNION ALL
SELECT 'student3', 'geography', 317 FROM DUAL;
If :your_student is student1 then the output is:
STUDENT
SUBJECT
CODE
student1
maths
312
student1
physics
785
and if :your_student is student2 then the output is:
STUDENT
SUBJECT
CODE
student2
english
900
student1
maths
312
db<>fiddle here

SQL getting multiple rows in PARTICULAR sql

I have a table abc with following rows:
emp_id_role Group_name Role_name Location_id
12 Insurance Manager Noida
12 Insurance Senior Manager Noida
13 Global Client Services Sw UP
14 Operations Management All Jobs kERALA
and another master table with all the details employee_xyz:
PERSON_ID NAME DOB START_DATE END_DATE SSN
12 DEAN 01-JAN-1990 01-JAN-2017 20-JAN-2017 847474
12 DEAN 01-JAN-1990 21-JAN-2017 03-mar-2018 847474
12 DEAN 01-JAN-1990 04-mar2018 31-DEC-4712 847474
13 SAM 20-JAN-1990 17-JAN-2016 20-JAN-2017 847474
13 SAM 20-JAN-1990 21-JAN-2017 31-DEC-4712 847474
14 JAY 29-dec-1990 21-JAN-2016 31-DEC-4712 847474
I want to fetch the full names from the table employee_xyz for the records in table abc.
When I'm joining these two using the below queries I'm getting more number of rows for an employee than in table abc,
Eg: for employee_id 12 I should get 2 rows as in table abc but I'm getting 9 rows somehow...
Query used is simple :
select * from table_abc abc,
employee_xyz xyz
where xyz.person_id=abc.emp_id_role
and trunc(sysdate) between abc.Start_date and xyz.end_date
and person_id=12;
I suppose you want to select all columns from table_abc with names from employee_xyz.
If your try with select abc.*, xyz.name, you already able to select as two rows since sysdate for person_id= 12 stays in the interval only for third row(start_date:04-mar2018/ end_date:31-DEC-4712) but if you want non-repeating even with more date values stay, you can use distinct : select distinct abc.*, xyz.name as below( where there're two rows in the interval second row's end date converted from '2018-03-03' to '2019-03-03' ) :
with table_abc(emp_id_role, Group_name, Role_name, Location_id) as
(
select 12,'Insurance','Manager','Noida' from dual union all
select 12,'Insurance','Senior Manager','Noida' from dual union all
select 13,'Global Client Services','Sw', 'UP' from dual union all
select 14,'Operations Management','All Jobs','kERALA' from dual
),
employee_xyz(person_id,name, dob, start_date, end_date, ssn) as
(
select 12,'DEAN',date'1990-01-01',date'2017-01-01',date'2017-01-20',847474 from dual union all
select 12,'DEAN',date'1990-01-01',date'2017-01-21',date'2019-03-03',847474 from dual union all
select 12,'DEAN',date'1990-01-01',date'2018-03-04',date'4712-12-31',847474 from dual
)
select distinct abc.*, xyz.name
from table_abc abc join employee_xyz xyz
on xyz.person_id = abc.emp_id_role
where trunc(sysdate) between xyz.Start_date and xyz.end_date
and person_id = 12;
EMP_ID_ROLE GROUP_NAME ROLE_NAME LOCATION_ID NAME
----------- ---------- --------------- ----------- -----
12 Insurance Manager Noida DEAN
12 Insurance Senior Manager Noida DEAN

SQL: Find max number of rows

I am totally new to coding so might be my question is silly sorry about it first.
I have a database that has CUST_REFERRED represent CUST_NUMBER who referred book someone
CUST_NUM NAME_S NAME_F ADDRESS Z_CODE CUST_REFERRED
1001 MORALES BONITA P.O. BOX 651 32328
1002 THOMPSON RYAN P.O. BOX 9835 90404
1003 SMITH LEILA P.O. BOX 66 32306
1004 PIERSON THOMAS 69821 SOUTH AVENUE 83707
1005 GIRARD CINDY P.O. BOX 851 98115
1006 CRUZ MESHIA 82 DIRT ROAD 12211
1007 GIANA TAMMY 9153 MAIN STREET 78710 1003
1008 JONES KENNETH P.O. BOX 137 82003
1009 PEREZ JORGE P.O. BOX 8564 91510 1003
1010 LUCAS JAKE 114 EAST SAVANNAH 30314
1011 MCGOVERN REESE P.O. BOX 18 60606
1012 MCKENZIE WILLIAM P.O. BOX 971 02110
1013 NGUYEN NICHOLAS 357 WHITE EAGLE AVE 34711 1006
1014 LEE JASMINE P.O. BOX 2947 82414
1015 SCHELL STEVE P.O. BOX 677 33111
1016 DAUM MICHELL 9851231 LONG ROAD 91508 1010
1017 NELSON BECCA P.O. BOX 563 49006
1018 MONTIASA GREG 1008 GRAND AVENUE 31206
1019 SMITH JENNIFER P.O. BOX 1151 07962 1003
1020 FALAH KENNETH P.O. BOX 335 08607
My idea is to find customer who referred max book. So as you can see 3 times 1003 number referred book who name is LEILA SMITH
I tried a code that;
SELECT
CUST_REFERRED,
COUNT(*)
FROM
CUSTOMER
GROUP BY
CUST_REFERRED
ORDER BY CUST_REFERRED ASC;
This code gives me:
1003 3
1006 1
1010 1
First, my question is I could not use LIMIT function to find max number
and the second question is How Can I add more information of customer?
SELECT NAME_F,
NAME_S,
ADDRESS,
CUST_REFERRED
FROM CUSTOMER
WHERE CUST_NUM = (SELECT MOST_CUS_REF
FROM (SELECT CUST_REFERRED MOST_CUS_REF, COUNT(CUST_REFERRED)
MOST_CUS_REF_COUNT
FROM (SELECT CUST_REFERRED
FROM customer
WHERE cust_referred IS NOT NULL
)
GROUP BY CUST_REFERRED
HAVING COUNT(CUST_REFERRED) = (SELECT MAX (cust_ref_num)
FROM (SELECT CUST_REFERRED,
COUNT(CUST_REFERRED) cust_ref_num
FROM (SELECT CUST_REFERRED
FROM customer
WHERE cust_referred IS NOT NULL
)
GROUP BY CUST_REFERRED
)
)
)
)
;
try this:
Select CUST_REFERRED, z.cnt from
(SELECT CUST_REFERRED, COUNT(*) cnt
FROM CUSTOMER where CUST_REFERRED is Not null
GROUP BY CUST_REFERRED) Z
where z.cnt =
(select Max(cnt) from
(SELECT COUNT(*) cnt
FROM CUSTOMER where CUST_REFERRED is Not null
GROUP BY CUST_REFERRED) ZZ)
Try this query --
;WITH CTE
AS (
SELECT CUSTOMER_REFID COUNT(*) AS REF_COUNT
FROM CUSTOMER
GROUP BY CUSTOMER_REFID
)
SELECT TOP 1 C2.CUSTOMER_ID
,C2.FIRST_NAME
,C2.LAST_NAME
,REF_COUNT
FROM CTE C1
INNER JOIN CUSTOMER C2
ON C1.CUSTOMER_REFID = C2.CUSTOMER_ID
ORDER BY REF_COUNT DESC
edited to add the referred customer details.
with data (cust_num, name_s, name_f, addr, code, cust_referred) as
(
/* begin: test data */
select 1001 ,'MORALES ','BONITA ','P.O. BOX 651 ',32328, null from dual union all
select 1002 ,'THOMPSON ','RYAN ','P.O. BOX 9835 ',90404, null from dual union all
select 1003 ,'SMITH ','LEILA ','P.O. BOX 66 ',32306, null from dual union all
select 1004 ,'PIERSON ','THOMAS ','69821, SOUTH AVENUE ',83707, null from dual union all
select 1005 ,'GIRARD ','CINDY ','P.O. BOX 851 ',98115, null from dual union all
select 1006 ,'CRUZ ','MESHIA ','82 DIRT ROAD ',12211, null from dual union all
select 1007 ,'GIANA ','TAMMY ','9153 MAIN STREET ',78710, 1003 from dual union all
select 1008 ,'JONES ','KENNETH ','P.O. BOX 137 ',82003, null from dual union all
select 1009 ,'PEREZ ','JORGE ','P.O. BOX 8564 ',91510, 1003 from dual union all
select 1010 ,'LUCAS ','JAKE ','114 EAST SAVANNAH ',30314, null from dual union all
select 1011 ,'MCGOVERN ','REESE ','P.O. BOX 18 ',60606, null from dual union all
select 1012 ,'MCKENZIE ','WILLIAM ','P.O. BOX 971 ',02110, null from dual union all
select 1013 ,'NGUYEN ','NICHOLAS ','357 WHITE EAGLE AVE ',34711, 1006 from dual union all
select 1014 ,'LEE ','JASMINE ','P.O. BOX 2947 ',82414, null from dual union all
select 1015 ,'SCHELL ','STEVE ','P.O. BOX 677 ',33111, null from dual union all
select 1016 ,'DAUM ','MICHELL ',',9851231, LONG ROAD ',91508, 1010 from dual union all
select 1017 ,'NELSON ','BECCA ','P.O. BOX 563 ',49006, null from dual union all
select 1018 ,'MONTIASA ','GREG ','1008 GRAND AVENUE ',31206, null from dual union all
select 1019 ,'SMITH ','JENNIFER ','P.O. BOX 1151 ',07962, 1003 from dual union all
select 1020 ,'FALAH ','KENNETH ','P.O. BOX 335 ',08607, null from dual
/* end: test data */
-- replace the above block with your table
-- eg. select * from customers_table
)
,
max_referred as
(
-- just interested in the first row after sorting by
-- the count of referred column values
select rownum, cust_referred, cnt from
(
select cust_referred, count(cust_referred) cnt from data group by cust_referred order by 2 desc
)
where rownum = 1
)
-- joining on cust_referred column in *data* and *max_referred* tables to get the customer details
-- and joining again to the *data* table for fetching the referred customer name
select
cust.cust_num, cust.name_s, cust.name_f, cust.addr, cust.code, cust.cust_referred, ms.name_f || ms.name_s as "Referred Customer"
from
data cust
join
max_referred mr on (cust.cust_referred = mr.cust_referred)
join
data ms
on (mr.cust_referred = ms.cust_num)
;
You can do it in a single table scan (i.e. without any self joins) using analytic functions:
SELECT *
FROM (
SELECT t.*,
MIN( CUST_REFERRED )
KEEP ( DENSE_RANK FIRST ORDER BY num_referrals DESC )
OVER ()
AS best_referrer
FROM (
SELECT c.*,
COUNT( CUST_REFERRED )
OVER ( PARTITION BY CUST_REFERRED )
AS num_referrals
FROM CUSTOMER c
) t
)
WHERE cust_num = best_referrer;

Oracle Sql : distinct value in a specific field [duplicate]

This question already has answers here:
How to select records with maximum values in two columns?
(2 answers)
Closed 7 years ago.
I have the following table :
**Country Name Number**
us John 45
us Jeff 35
fr Jean 31
it Luigi 25
fr Maxime 23
ca Justin 23
This table is order by Number. I want to have a query that for each country give me the name with highest number :
**Country Name Number**
us John 45
fr Jean 31
it Luigi 25
ca Justin 23
I try to use distinct but I can't only make it on country if I want to print the all thing...
Have an idea ?'
EDIT :
The table is obtain by a subquery
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Countries AS
SELECT 'us' AS Country, 'John' AS Name, 45 AS "Number" FROM DUAL
UNION ALL SELECT 'us' AS Country, 'Jeff' AS Name, 35 AS "Number" FROM DUAL
UNION ALL SELECT 'fr' AS Country, 'Jean' AS Name, 31 AS "Number" FROM DUAL
UNION ALL SELECT 'it' AS Country, 'Luigi' AS Name, 25 AS "Number" FROM DUAL
UNION ALL SELECT 'fr' AS Country, 'Maxime' AS Name, 23 AS "Number" FROM DUAL
UNION ALL SELECT 'ca' AS Country, 'Justin' AS Name, 23 AS "Number" FROM DUAL;
Query 1:
SELECT Country,
MAX( Name ) KEEP ( DENSE_RANK FIRST ORDER BY "Number" DESC ) AS "Name",
MAX( "Number" ) AS "Number"
FROM Countries
GROUP BY Country
Results:
| COUNTRY | Name | Number |
|---------|--------|--------|
| ca | Justin | 23 |
| fr | Jean | 31 |
| it | Luigi | 25 |
| us | John | 45 |
I do not have an Oracle db handy but I got this working in my SQL Server db and am pretty sure it will work in Oracle (meaning I think I am using ANSI sql which should work in most db's):
SELECT m.Country,m.Name,m.number
FROM mytable m
INNER JOIN (
select country, MAX(number) as number
FROM mytable GROUP BY Country
) AS tmp ON m.Country = tmp.Country and m.Number = tmp.number
ORDER BY m.Number DESC
This has the added benefit that it should give you records when you have two people in a given country that have the same number.
You didn't give us a table name so I just called it mytable.
Try below query:
SELECT Country, MAX(numbeer) FROM Table_Name GROUP BY Country
PFB updated query to include Name:
SELECT t1.* FROM table1 t1 INNER JOIN
(SELECT country, max(numbeer) as numbeer FROM table1 GROUP BY country) t2
ON t1.country=t2.country AND t1.numbeer=t2.numbeer;
Use row_number():
select t.Country, t.Name, t.Number
from (select t.*,
row_number() over (partition by country order by number desc) as seqnum
from table t
) t
where seqnum = 1;