(Beginner) How to exclude null value from Oracle Inner Join Statement Result and How to show ONLY null data in any of the attributes - oracle

(Beginner)
Q1) How to exclude null value from Oracle Inner Join Statement Result. i.e. to display only that row which has all the values (i.e. no null data in any of the attribute)
Q2) How to show ONLY those record/s who has missing/null data in any of the attributes in any of the four table
Below is the dummy example in oracle sql
Created 4 Tables i.e.,
name , details , social , address
`create table name
(
id number,
firstname varchar2(20),
lastname varchar2(20)
);
create table details
(
id number,
dob_month varchar2(20),
dob_day varchar2(20),
dob_year varchar2(20)
);
create table social
(
id number,
ssn varchar2(20),
telephone number
);
create table address
(
id number,
address varchar2(20)
);
`
Now insert dummy data into the above tables
`insert into name values (1, 'Will' , 'Smith');
insert into name values (2, 'Barry' , 'White');
insert into name values (3, 'Tom' , 'Jones');
insert into name values (4, 'Rod' , 'Stewart');
insert into name values (5, 'Elvis' , 'Presley');
insert into details values (1,'May',31,null);
insert into details values (2,'August',22,1980);
insert into details values (3,'October',null,1973);
insert into details values (4,'January',30,1980);
insert into details values (5,'March',11,1980);
insert into social values (1,'123-45-6789',null);
insert into social values (2,'222-45-5555',789456123);
insert into social values (3,'333-45-7777',888888888);
insert into social values (4,null,693456741);
insert into social values (5,'999-45-1111',null);
insert into address values (null, null);
insert into address values (2, '12th street');
insert into address values (null, null);
insert into address values (4, '14th Avenue');
insert into address values (5, null);`
Q1 Pictorial Explanation
For Q1,
I tried with below oracle sql query (both returns the same result) but not able to figure out the exact query which will exclude null value from Oracle Inner Join Statement Result and display only that row which has all the values (i.e. not null)
`select name.id, firstname, lastname, dob_month, dob_year, ssn, telephone, address
from name, details, social, address
where name.id=details.id
and details.id=social.id
and social.id=address.id;
select name.id, firstname, lastname, dob_month, dob_year, ssn, telephone, address
from name join details on name.id=details.id
join social on details.id=social.id
join address on social.id=address.id;`
For Q2, I am looking for sample Query
i.e. How to show ONLY those record/s who has missing/null data in any of the attributes in any of the four table

For the first question, I think you are looking for this:
select name.id, firstname, lastname, dob_month, dob_year, ssn, telephone, address
from name
join details on name.id=details.id
join social on details.id=social.id
join address on social.id=address.id
where dob_year is not null
and dob_day is not null
and telephone is not null
and ssn is not null
and address is not null;
Or a bit shorter
...
where COALESCE(dob_year, dob_day, telephone, ssn, address) is not null;
For Q2 it would be this
select *
from address
where id is null
or address is null;
Some more notes:
It is a poor design to store date parts and then even localized strings. You should never do that. In your case it should be
create table details
(
id number,
dob_date DATE
);
Then next question, why do you create four tables? Maybe you learned at school about database normalization but you have taken too literally. Can a person have more than one SSN/telephone? If not, then add these columns to name table (and maybe rename it to person) instead of a separate table. The same question applies to table address.

Related

Strategy to snapshot large data set Oracle

My Problem:
I have an app and users who login have a department authorization list:
DEPT1, DEPT2, DEPT3, ..., DEPT5000, DEPT5001, ...
Most users have 5,000+ departments assigned to their profile.
I am tasked with writing a data model + application code that will 'snapshot' their list of authorized departments every time a user logs in so that we may refer to what that user was authorized to do (note: the DEPT IDs are not neatly numbered like in this example).
What I've tried:
My first thought was to turn the list of departments into a long CSV string and store it as a CLOB:
CREATE TABLE UI_SECURITY_CONFIG (
SECURITY_CONFIG_ID NUMBER(19,0) NOT NULL,
DEPTSCSV CLOB NOT NULL
);
And each DEPTSCSV CLOB would be unique. If the user has the same security profile as someone else who's logged in previously, it would just select that security config. Otherwise, it would create a new row. Basically, do a select where DEPTSCSV = 'DEPT1, DEPT2, DEPT3 ...' and if it doesn't exist, insert it. But this approach failed because a huge string that big (25,000+ chars) isn't comparable:
SELECT * FROM UI_SECURITY_CONFIG WHERE DEPTSCSV = 'DEPT0001, DEPT0002, DEPT0003, ..., DEPT5001, DEPT5002'
SQL Error [1704] [42000]: ORA-01704: string literal too long
Solution attempt #2:
So then I thought about making each item in the CSV its own row in the table:
CREATE TABLE UI_SECURITY_CONFIG (
SECURITY_CONFIG_ID NUMBER(19,0) NOT NULL,
DEPTID VARCHAR2(20) NOT NULL
);
INSERT INTO UI_SECURITY_CONFIG(SECURITY_CONFIG_ID, DEPTID) VALUES(1, 'DEPT0001');
INSERT INTO UI_SECURITY_CONFIG(SECURITY_CONFIG_ID, DEPTID) VALUES(1, 'DEPT0002');
INSERT INTO UI_SECURITY_CONFIG(SECURITY_CONFIG_ID, DEPTID) VALUES(1, 'DEPT0003');
...
INSERT INTO UI_SECURITY_CONFIG(SECURITY_CONFIG_ID, DEPTID) VALUES(1, 'DEPT5001');
INSERT INTO UI_SECURITY_CONFIG(SECURITY_CONFIG_ID, DEPTID) VALUES(1, 'DEPT5002');
But I'm struggling to write the SQL select that would be an efficient matching algorithm to find if a SECURITY_CONFIG_ID exists that matches exactly the list of Departments.
I'm not even sure there is an efficient way to solve this problem.
Solution Attempt #3:
Ask Stack Overflow. What would you do?
I was able to achieve strategy #1. The Application code (Java) handled the CLOB comparison better than my SQL client (DBeaver) with PreparedStatement:
String sql = "SELECT SECURITY_CONFIG_ID FROM UI_SECURITY_CONFIG WHERE dbms_lob.compare(DEPTSCSV, ?) = 0";
String DEPTSCSV = "DEPT0001, DEPT0002, ...";
try(PreparedStatement objStmt = objConn.prepareStatement(sql)) {
Clob clob1 = objConn.createClob();
clob1.setString(1, DEPTSCSV);
objStmt.setClob(1, clob1);
ResultSet result = objStmt.executeQuery();
...
}

Create and insert table

I made a table for database and the table created but the insert vales are not working.
This is the table
Create table patient (
Patient_ID Number(9) primary key,
First_name varchar2(15),
Last_name varchar2(10),
Contact number(10),
City varchar2(20),
Doctor_ID Number(9) references Doctor(Doctor_ID));
This is the insert statement
insert into patient values ('21345', 'John', 'Smith', '111-111-1111', 'NJ');
insert into patient values ('21346', 'Emily', 'Rose', '222-222-2222', 'LA');
insert into patient values ('21347', 'Mark', 'Cruise', '333-333-3333', 'NY');
insert into patient values ('21348', 'Bran', 'Stark', '444-444-4444', 'TX');
insert into patient values ('21349', 'Hailey', 'Wraith', '555-555-5555', 'AZ');
I am getting an error saying not enough values.
You are only inserting 5 values when your table is expecting 6 (ParentID, First Name, Last Name, Contact, City and Doctor ID)
You need to pass in a value for Doctor_ID
You forgot to add the table column names during the insert so it tries to add all the data your passing to each column in the table. This is a better way of entering your data when it doesn't require you to insert into all table columns.
Insert into `patient` (`table1`, `table2`, `table3`, `table4`, `table5`) values ('21345', 'John', 'Smith', '111-111-1111', 'NJ');
To break down your issue more, you have 6 columns in your table, but the data your passing is 5, it will give you this error because 5 is less than 6. If you don't want to receive this error you need to state each column you are entering into as seen above -- BUT that field will need to be a nullable field.
In this case Doctor_ID is missing
Insert into `patient` (`table1`, `table2`, `table3`, `table4`, `table5`, `table6` ) values ('21345', 'John', 'Smith', '111-111-1111', 'NJ', 'DOCTOR_ID_DATA_HERE');
ANSWER:
Insert into `patient` (`Patient_ID`, `First_name`, `Last_name`, `Contact`, `City`, `Doctor_ID`) values ('2125', 'John', 'Doe', '111-111-1111', 'LA', '30114');
DOCTOR 30114 needs to already exist because you are referencing from another table, please take note!

Fetch data from a table in oracle sql

I've two table in the database, the first one is Person and the second is Pilot. as following:
Person Table:
CREATE TABLE person(
person_id NUMBER PRIMARY KEY,
last_name VARCHAR2(30) NOT NULL,
first_name VARCHAR2(30) NOT NULL,
hire_date VARCHAR2(30) NOT NULL,
job_type CHAR NOT NULL,
job_status CHAR NOT NULL
);
/
INSERT INTO person VALUES (1000, 'Smith', 'Ryan', '04-MAY-90','F', 'I');
INSERT INTO person VALUES (1170, 'Brown', 'Dean', '01-DEC-92','P', 'A');
INSERT INTO person VALUES (2010, 'Fisher', 'Jane', '12-FEB-95','F', 'I');
INSERT INTO person VALUES (2080, 'Brewster', 'Andre', '28-JUL-98', 'F', 'A');
INSERT INTO person VALUES (3190, 'Clark', 'Dan', '04-APR-01','P', 'A');
INSERT INTO person VALUES (3500, 'Jackson', 'Tyler', '01-NOV-05', 'F', 'A');
INSERT INTO person VALUES (4000, 'Miller', 'Mary', '11-JAN-08', 'F', 'A');
INSERT INTO person VALUES (4100, 'Jackson', 'Peter', '08-AUG-11', 'P','I');
INSERT INTO person VALUES (4200, 'Smith', 'Ryan', '08-DEC-12', 'F','A');
COMMIT;
/
Pilot Table:
CREATE TABLE pilot(
person_id NUMBER PRIMARY KEY,
pilot_type VARCHAR2(100) NOT NULL,
CONSTRAINT fk_person_pilot FOREIGN KEY (person_id)
REFERENCES person(person_id)
);
/
INSERT INTO pilot VALUES (1170, 'Commercial pilot');
INSERT INTO pilot VALUES (2010, 'Airline transport pilot');
INSERT INTO pilot VALUES (3500, 'Airline transport pilot');
COMMIT;
/
I'm asked to write a pl/sql block of code that accepts the last name from the user and return the result as following:
1) if the last name is not in the table, it returns all the rows in the table.
2) if the last name is in the table, it shows all of the employee's information.
So far I'm doing well with the code, but I got stuck in the case that there are two employees with the last name. here is the cursor that I wrote:
cursor person_info is
select last_name, first_name, hire_date, job_type, job_status, nvl(pilot_type, '-----------')
from person
full outer join pilot
on person.person_id = pilot.person_id
where upper(last_name) = upper(v_last_name)
group by last_name, first_name, hire_date, job_type, job_status, pilot_type
order by last_name, first_name, hire_date asc;
Logically, there are three cases to be covered:
the first case, when the entered last name is in the table, I return all the rows in the table and that's done.
The second case when there is only one employee with the entered last name, and this case is done as well. The last case when there are more than one employee having the same last name like for example 'Jackson' or 'Smith' in this case, my program crashes and give me the error that my select into statement returns more than one row.
select person_id
into v_n
from person
where upper(last_name) = upper(v_last_name);
if v_n = 1 then
open person_info;
fetch person_info into v_last_name, v_first_name, v_hire_date, v_job_type, v_job_status, v_pilot_type;
Can someone help me in guiding me how to fetch the data correctly? I'm not allowed to create any temporary tables or views.
I'm so sorry for making the problem longer than it should, but I was trying to be as clear as possible in explaining the problem.
Thank you in advance.
if the error is
"ORA-01422 exact fetch returns more than requested number of rows" then I think your answer is here https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:981494932508
If you EXPECT the query to return more than one row, you would code:
for x in ( select * from t where ... )
loop
-- process the X record here
end loop;
Your immediate issue is that you're selecting the matching person_id into a variable, and then seeing if that specific ID is 1. You don't have an actual ID 1 anyway so that check would never match; but it is that querying matching multiple rows that gets the error, as you can't put two matching IDs into a single scalar variable.
The way you've structured it looks like you are trying to count how many matching rows there are, rather than looking for a specific ID:
select count(person_id)
into v_n
from person
where upper(last_name) = upper(v_last_name);
if v_n = 1 then
....
When you do have multiple matches then you will need to use the same mechanism to return all of those as you do when there are no matches and you return all employees. You may find the logic should be in the cursor query rather then in PL/SQL logic. It depends on the details of the assignment though, and how it expects you to return the data in both (or all three) scenarios.
It's also possible you just aren't expected to hit this problem - it isn't clear if the assignment is finding all employees, or only those that are pilots. The issue still exists in general, but with the data you show there aren't any duplicate pilot last names. If you haven't learned about this kind of error yet perhaps you're getting a bit ahead of what your tutor expects.

Oracle Insert all data from another table contain duplicates how to avoid

have two tables A and B both same structure except B has one addition extra column inserting as "null". I need to Retain all data from A in B when I insert like below query it is inserting duplicate values because of that getting "primary Key violation error" when I try to create the "CONSTRAINT PK_Details_A PRIMARY KEY" Please help on this to avoid duplicate values while inserting the records.
Thanks in advance.
Insert into tableB(
id, effectiveDate, endDate
,startDate, Type, salary
,baseSalary, Amount, Amount1
,currency, Percentage, Salary
,Notional
)
select id, effectiveDate, endDate
,startDate, Type, salary
,baseSalary, Amount, Amount1
,currency, Percentage, Salary,null
from tableA;
EDIT
Primary key definition for B copied from comment below:
ALTER TABLE B
ADD CONSTRAINT PK_B
PRIMARY KEY ( oid)
USING INDEX ( CREATE UNIQUE INDEX PK_B ON B ( oid )

Oracle Inserting or Updating a row through a procedure

I have a table
CREATE TABLE STUDENT
(
ID INTEGER PRIMARY KEY,
FIRSTNAME VARCHAR2(1024 CHAR),
LASTNAME VARCHAR2(1024 CHAR),
MODIFIEDDATE DATE DEFAULT sysdate
)
I am inserting a row of data
insert into STUDENT (ID, FIRSTNAME, LASTNAME, MODIFIEDDATE) values (1,'Scott', 'Tiger', sysdate);
When I have to insert a record of data, I need to write a procedure or function which does the following:
if there is no record for the same id insert the row.
if there is a record for the same id and data matches then do nothing.
if there is a record for the same id but data does not match then update the data.
I am new to oracle. From the java end, It is possible to select the record by id and then update that record, but that would make 2 database calls. just to avoid that I am trying update the table using a procedure. If the same can be done in a single database call please mention.
For a single SQL statement solution, you can try to use the MERGE statement, as described in this answer https://stackoverflow.com/a/237328/176569
e.g.
create or replace procedure insert_or_update_student(
p_id number, p_firstname varchar2, p_lastname varchar2
) as
begin
merge into student st using dual on (id = p_id)
when not matched then insert (id, firstname, lastname)
values (p_id, p_firstname, p_lastname)
when matched then update set
firstname = p_firstname, lastname = p_lastname, modifiedate = SYSDATE
end insert_or_update_student;
instead of procedure try using merge in oracle .
If Values is matched it will update the table and if values is not found it will insert the values
MERGE INTO bonuses b
USING (
SELECT employee_id, salary, dept_no
FROM employee
WHERE dept_no =20) e
ON (b.employee_id = e.employee_id)
WHEN MATCHED THEN
UPDATE SET b.bonus = e.salary * 0.1
DELETE WHERE (e.salary < 40000)
WHEN NOT MATCHED THEN
INSERT (b.employee_id, b.bonus)
VALUES (e.employee_id, e.salary * 0.05)
WHERE (e.salary > 40000)
Try this
To solve the second task - "if there is a record for the same id and data matches then do nothing." - starting with 10g we have additional "where" clause in update and insert sections of merge operator.
To do the task we can add some checks for data changes:
when matched then update
set student.last_name = query.last_name
where student.last_name <> query.last_name
This will update only matched rows, and only for rows where data were changed

Resources