How to get 'IS NULL' where clause values if initial condition returns none in oracle - oracle

I have a table with values as follows
![ID S_ID NAME
1 James
2 2455 Patrick
3 2566 Sam
4 25988 Rick]1
My requirement is to get the NAME based on S_ID and if the given S_ID is not in the table then get the NAME with no S_ID
EX:
SELECT NAME FROM STUDENTS WHERE S_ID=2455
The result would be Patrick
SELECT NAME FROM STUDENTS WHERE S_ID=2411
The result should be James
I have tried
select NAME from STUDENTS where S_ID=2455 or S_ID IS NULL
It gives be bot James and Patrick
I would really appreciate any help

Here's one option:
SQL> set ver off
SQL> with students (id, s_id, name) as
2 (select 2, 2389, 'Patrick' from dual union all
3 select 1, null, 'James' from dual union all
4 select 3, 2566, 'Sam' from dual
5 )
6 select name from students where s_id = &&par_s_id
7 and exists (select null from students where s_id = &&par_s_id)
8 union all
9 select name from students where s_id is null
10 and not exists (select null from students where s_id = &&par_s_id);
Enter value for par_s_id: 2389
NAME
-------
Patrick --> because S_ID = 2389 exists in a table
SQL> undefine par_s_id
SQL> /
Enter value for par_s_id: 123456
NAME
-------
James --> because S_ID = 123456 doesn't exist
SQL>
As you already have the table, you'd use code from line #6 onward.

#Joe,
As per my understanding if S_ID is found in the STUDENT table it should return the NAME of the student, if S_ID is not found the query should return a message as 'No #S_ID'.
Please let me know if I am making any mistake in understanding your question !
[Solution]
Its a Tsql so we can use it as is in the sql or in any language. And note I have tested it in MSSQL but the approach remains the same.
declare #sidInput int
set #sidInput = 2455
if exists (select NAME from STUDENTS where S_ID = #sidInput)
begin
select NAME from STUDENTS where S_ID = #sidInput
end
else
begin
declare #outputMsg varchar(20)
set #outputMsg = 'No ' + convert( varchar(10), #sidInput)
select top 1 #outputMsg as NAME from STUDENTS where S_ID not in ( #sidInput )
end
[Test Cases]
1) For Input #sidInput = 2455
Output - NAME as Patrick
2) For Input #sidInput = 2411
Output - NAME as James
3) For Input #sidInput = 2222 #S_ID value not found in STUDENT table
Output - NAME as No 2222
Let me know if it works for you.
Regards,
Arnab

#Joe,
try this t-sql (and pardon me I was using maria db - so used it ) . basically it remains the same as for my above response its just that I have updated the else block. ( Note - If you are using MySQL can simply use this as it for testing )
DROP PROCEDURE IF EXISTS prod_studentName;
DELIMITER $$
CREATE PROCEDURE`prod_studentName`()`
BEGIN
declare sidValue int;
set sidValue = 2389;
if exists (select NAME from STUDENT where S_ID = sidValue )
then
select NAME from STUDENT where S_ID = sidValue;
else
select NAME from STUDENT where S_ID is NULL;
end if;`
END$$
DELIMITER ;
refer to the attached image for the table inputs -
test outcomes
positive test - lookup for s_id = 2389
outcome -
negative test - lookup for s_id = 7777 this id doesn't even exist at all
outcome -
hope this helps you

Related

How to run command "describe table_name" via jdbc to query Oracle database

I know how to run normal select query.
Class.forName("oracle.jdbc.driver.OracleDriver");
String url = "jdbc:oracle:thin:#xxx";
String uname = "";
String passwd = "";
Connection conn = DriverManager.getConnection(url, uname, passwd);
Statement stmt = conn.createStatement();
String sql = "select * from table_name";
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out.println();
}
But how to run query like sql = "describe table_name" ?
describe is a SQL*Plus command; although it works elsewhere (such as in SQL Developer or TOAD), it is not a "standard" command so I don't think you can use it the way you wanted.
Therefore, as you already know how to run a "normal" query, do it again, but this time by fetching data from user_tab_columns which contains data you need. For example:
SQL> SELECT column_name, data_type, data_precision, data_length, nullable
2 from user_tab_columns
3 where table_name = 'TEMP';
COLUMN_NAME DATA_TYPE DATA_PRECISION DATA_LENGTH N
--------------- --------------- -------------- ----------- -
ID NUMBER 22 Y
ENAME VARCHAR2 10 Y
JOB VARCHAR2 15 Y
DEPT NUMBER 22 Y
HIREDATE DATE 7 Y
LOC VARCHAR2 10 Y
6 rows selected.
which can be compared to
SQL> describe temp
Name Null? Type
----------------------------------------- -------- ----------------------------
ID NUMBER
ENAME VARCHAR2(10)
JOB VARCHAR2(15)
DEPT NUMBER
HIREDATE DATE
LOC VARCHAR2(10)
SQL>
As of comments: there's that nice view named dictionary you can query and find some useful information, i.e. a system view which then lets you find another information. Here's how:
SQL> select * from dictionary
2 where lower(table_name) like '%user%' and lower(comments) like '%comment%';
TABLE_NAME COMMENTS
------------------------- --------------------------------------------------
USER_COL_COMMENTS Comments on columns of user's tables and views
USER_INDEXTYPE_COMMENTS Comments for user-defined indextypes
USER_MVIEW_COMMENTS Comments on materialized views owned by the user
USER_OPERATOR_COMMENTS Comments for user-defined operators
USER_TAB_COMMENTS Comments on the tables and views owned by the user
OK; it is user_tab_comments and user_col_comments I need. So let's add some comments to the temp table:
SQL> comment on table temp is 'Sample table for Stack Overflow';
Comment created.
SQL> comment on column temp.ename is 'Employee''s name';
Comment created.
Result:
SQL> select * from user_tab_comments where table_name = 'TEMP';
TABLE_NAME TABLE_TYPE COMMENTS
------------------------- ----------- --------------------------------------------------
TEMP TABLE Sample table for Stack Overflow
SQL> select * from user_col_comments where table_name = 'TEMP';
TABLE_NAME COLUMN_NAME COMMENTS
------------------------- --------------- --------------------------------------------------
TEMP ID
TEMP ENAME Employee's name
TEMP JOB
TEMP DEPT
TEMP HIREDATE
TEMP LOC
6 rows selected.
SQL>

How to filter and retrieve the results after a Specific characters from stored procedure in oracle plsql?

I have a column "Names" in the "Employee" table that has following values. The values either contain only single name (first, last, username) or Multiple names separated with semicolon (;). I need to search the values from that table either by first name or last name or username.
I have created a procedure but it is fetching only 1st,4th,5th records. Please let me know how to retrieve 2nd and 3rd records as well.
Firstname and lastname can be given by user with minimum of 2 characters length.
Username is given entire.
Employee:
ID Name Title
1 Andrea Warbutton (awr01) Manager
2 Claire Taylor (cta02);Mark Kites (mak03);Anitha Rooney (anr06) HOD;Supervisor;Business
3 Dave Rites (dar12);Jessica Simpson (jesi10) Lead;Analyst
4 Nick Ken (nik56) Product (Local,Regional)
5 Claire Pilkington (cpt09) Sales Owner
Code:
Create or replace empl (pm_firstname varchar2(100),
pm_lastname varchar2(100),
pm_username varchar2(100))
BEGIN
Select * from Employee
where Upper(Name) like Upper(pm_firstname ||'%'||) -- this will fetch 1st,4th,5th record
OR Upper(SUBSTR(Name, INSTR(Name),' '+1)) like Upper(pm_lastname ||'%'||) -- this will fetch 1st,4th,5th record
OR upper(REGEXP_SUBSTR(Name,'\((.+)\)',1,1,NULL,1)) = Upper(pm_username); -- -- this will fetch 1st,4th,5th record
END;
End empl ;
Please let me know how to retrieve 2nd and 3rd records as well.
Desired Output:
When searched with firstname = "Andrea", the output is below
ID Name Title
1 Andrea Warbutton (awr01) Manager
When searched with firstname = "Claire", the output is below
ID Name Title
2 Claire Taylor (cta02) HOD
5 Claire Pilkington (cpt09) Sales Owner
When searched with lastname = "Simps", the output is below
ID Name Title
3 Jessica Simpson (jesi10) Analyst
When searched with username = "mak03", the output is below
ID Name Title
2 Mark Kites (mak03) Supervisor
When searched with username = "nik56", the output is below
ID Name Title
4 Nick Ken (nik56) Product (Local,Regional)
with
x as (select id, name, '"'||replace(name, ';', '","')||'"' xml from employee),
n as (select id, name, column_value as cv from x, xmltable(xml))
select id,
trim(regexp_substr(cv, '(\S*)(\s)')) fname,
trim(regexp_substr(cv, '(\S*)(\s)', 1, 2)) lname,
regexp_substr(cv, '\((.+)\)', 1, 1, NULL, 1) uname
from n
Your task would be much easier if you normalize these data. Above query outputs:
ID FNAME LNAME UNAME
1 Andrea Warbutton awr01
2 Claire Taylor cta02
2 Mark Kites mak03
2 Anitha Rooney anr06
3 Dave Rites dar12
3 Jessica Simpson jesi10
4 Nick Ken nik56
5 Claire Pilkington cpt09
demo
Now you can search first, last, usernames however you want. First expression finds first word, then second and word between brackets.
Edit:
I posted the table structure with just ID and Name columns. However, I
have Titles column also in the same format separated with (semicolon).
In this case, How can I Normalize Titles as well along with Names
This query worked for provided examples:
with
x as (select id, name, '"'||replace(name, ';', '","')||'"' xmln,
'"'||replace(title, ';', '","')||'"' xmlt
from employee),
n1 as (select id, trim(xn.column_value) nm, rownum rn from x, xmltable(xmln) xn),
n2 as (select id, trim(xt.column_value) tt, rownum rn from x, xmltable(xmlt) xt)
select id, trim(regexp_substr(nm, '(\S*)(\s)')) fname,
trim(regexp_substr(nm, '(\S*)(\s)', 1, 2)) lname,
regexp_substr(nm, '\((.+)\)', 1, 1, NULL, 1) uname,
tt title
from n1 join n2 using (id, rn)
dbfiddle demo
Be careful however, because we cannot write ideal query. If you have entries like Benicio Del Toro, Mary Jo Catlett, Jean Claude Van Damme, it's impossible to write correct regexp. Sometimes second word is part of lastname, sometimes it is firstname, middlename etc.
The proper way is to modify table structure, divide rows, check results and put correct values in correct name columns. Now you have lists which are hard to search and every method may return wrong results.
No need for PL/SQL.
SQL> with temp as
2 (select id,
3 regexp_substr(name, '[^;]+', 1, column_value) name
4 from employee cross join
5 table(cast(multiset(select level from dual
6 connect by level <= regexp_count(name, ';') + 1
7 ) as sys.odcinumberlist))
8 )
9 select id, name
10 from temp
11 where instr(name, '&search_for_name') > 0;
Enter value for search_for_name: Claire
ID NAME
---------- ------------------------------
2 Claire Taylor (cta02)
5 Claire Pilkington (cpt09)
SQL> /
Enter value for search_for_name: mak03
ID NAME
---------- ------------------------------
2 Mark Kites (mak03)
SQL>
What does it do?
temp CTE splits semi-colon separated values into rows
final query uses a simple instr function which detects whether "rows" (extracted previously) contain value you're looking for
If it must be a function, that code can be reused. As you didn't say what exactly (which datatype, I mean) you want to return, I returned a string.
SQL> create or replace function f_search (par_what in varchar2)
2 return sys.odcivarchar2list
3 is
4 retval sys.odcivarchar2list;
5 begin
6 with temp as
7 (select id,
8 regexp_substr(name, '[^;]+', 1, column_value) name
9 from employee cross join
10 table(cast(multiset(select level from dual
11 connect by level <= regexp_count(name, ';') + 1
12 ) as sys.odcinumberlist))
13 )
14 select id ||' - '|| name
15 bulk collect into retval
16 from temp
17 where instr(name, par_what) > 0;
18
19 return retval;
20 end;
21 /
Function created.
SQL> select * from table(f_search('Andrea'));
COLUMN_VALUE
--------------------------------------------------------------------------------
1 - Andrea Warbutton (awr01)
SQL> select * from table(f_search('Claire'));
COLUMN_VALUE
--------------------------------------------------------------------------------
2 - Claire Taylor (cta02)
5 - Claire Pilkington (cpt09)
SQL>

How to assign a value to variable from select statement (PL/SQL Developer)

I'm working with PL/SQL Developer.
I'm trying to update values in a column(existing table).
The values used to populate rows should be auto incremented. The starting value is the maximum value that already exist in such field.
An example, I have the following table
ORDER_ID T_NAME T_PRICE
20 CAR 50
NULL VAN 100
NULL BIKE 10
NULL BOAT 300
After running the query I would expect the table to look like:
ORDER_ID T_NAME T_PRICE
20 CAR 50
21 VAN 100
22 BIKE 10
23 BOAT 300
The query I created so far is:
DECLARE
temp_order_id number;
BEGIN
:temp_order_id = SELECT ISNULL(MAX((ORDER_ID)),0) + 1 FROM SALES_ACC;
update SALES_ACC
set (ORDER_ID) = :temp_order_id , :temp_order_id = :temp_order_id + 1
where (ORDER_ID) is null;
END;
Oracle doesn't like assigning a value from select statement to the temp_order_id variable.
Does anyone has any idea how to fix it?
You don't need pl/sql for this - you can do it in a single update statement - eg:
create table test1 as
select 20 order_id, 'CAR' t_name, 50 t_price from dual union all
select null order_id, 'VAN' t_name, 100 t_price from dual union all
select null order_id, 'BIKE' t_name, 10 t_price from dual union all
select null order_id, 'BOAT' t_name, 300 t_price from dual;
update test1
set order_id = (select max(order_id) from test1) + rownum
where order_id is null;
commit;
select * from test1
order by 1;
ORDER_ID T_NAME T_PRICE
---------- ------ ----------
20 CAR 50
21 VAN 100
22 BIKE 10
23 BOAT 300
drop table test1;
As a side note, it sounds like order_id is something that should really be the primary key of the table - if you had that, then you wouldn't be allowed to add a row without a value. Plus, you would also need a sequence that you would then use when inserting data into the table - e.g.:
insert into test1 (order_id, t_name, t_price)
values (test1_seq.nextval, 'TRIKE', 30);
ORACLE's recomended way for this is either:
Create a sequence and a trigger on the table to assign order_id as soon as row is being inserted
or, for Oracle 12c, you can have an IDENTITY column
See How to create id with AUTO_INCREMENT on Oracle?, both approaches are described there.
Within the DECLARE ... BEGIN ... END; section you are in PL/SQL syntax. That is not equal to the SQL syntax. Within PL/SQL syntax you should make use of the so called select into statement.
SELECT ISNULL(MAX((ORDER_ID)),0) + 1
into :temp_order_id
FROM SALES_ACC

How to do a pivot on the oracle database with field type varchar2

I am newbie to Oracle but have worked on mysql previously. We have migrated the database from mysql to oracle and want help in this scenario.
I have table in the oracle 10g which is in the following format:
student_id student_key student_value
---------- ----------- -------------
1 name john
2 name bill
1 age 28
2 age 26
2 result pass
now i want to create a pivot on it so i use the following query:
select student_id, decode(student_key, 'name', student_Value, null) as studentName
from student_table
the output comes as
student_id studentName
---------- -----------
1 john
1 null
2 bill
2 null
2 null
Oracle 11 has specialized PIVOT functionality (a nice introduction is here), but 10g doesn't. Here's how to do it in 10g:
SELECT
student_id,
MAX(CASE WHEN student_key = 'name' THEN student_value END) AS StudentName,
MAX(CASE WHEN student_key = 'age' THEN student_value END) AS Age,
MAX(CASE WHEN student_key = 'result' THEN student_value END) AS Result
FROM myTable
GROUP BY student_id
As with the Oracle PIVOT command, you need to know the number of output columns ahead of time.
Also note that MAX is used because we're rolling rows up to columns, which means we have to group, which means we need an aggregate function, so why not MAX? If you use MIN instead of MAX you'll get the same results.

How to find records in one table which aren't in another

I'm very new to oracle sql and programming and I need some help with one of my first projects. I'm working with this table schema:
Column Data Type Length Precision Scale Nullable
EMPLOYEE_ID NUMBER 22 6 0 No
START_DATE DATE 7 - - No
END_DATE DATE 7 - - No
JOB_ID VARCHAR2 10 - - No
DEPARTMENT_ID NUMBER 22 4 0 Yes
I want to display all employees who have never changed their jobs, not even once(employees not listed in the above table) This table is labeled job_history. How would I go about doing this? I'm not sure on how to get this started.
select * from employees
where employee_id not in ( select employee_id from job_history)
/
You can use a left join and check for a null employee_id on the job_history table.
select * from employees
left join job_history
on job_history.employee_id = employees.employee_id
where job_history.employee_id is NULL
Fairly often an execution plan is better for
select * from employees e where not exists
(select 1 from job_history jh.employee_id = e.employee_id)
than "not in ()".
And if the tables have the same structure the best result will with
select * from employees
minus
select * from job_history

Resources