How to replace string in Oracle? - sql

From front end User will enter (multiple) employee number as (123,456,789) to search in the application. The way User enters the
employee numbers is pre-defined like above.
This value has to be searched in a Oracle database table in a PL/SQL package.
I have coded like below to format the entered value so that it can be searched in the SQL WHERE condition.
lv_where := REPLACE( REPLACE( REPLACE( '(123,456,789)', ',', ''',''' ), '(', '(''') , ')', ''')')
Output is : ('123','456','789')
Is there a better way?

you can use regexp_replace
regexp_replace('(123,456,789)','([[:digit:]]+)','''\1''')
Result:
('123','456','789')

Unless it is dynamic SQL, such a lv_where won't work. I'd suggest you to split entered values into rows and use them in your query.
In the following example, par_emp represents value entered by user. Basically, what you need begins at line #3.
SQL> with test (par_emp) as
2 (select '(10,20,30)' from dual)
3 select deptno, ename
4 from emp
5 where deptno in (select regexp_substr(replace(replace(par_emp, '(', ''), ')', ''), '[^,]+', 1, level)
6 from test
7 connect by level <= regexp_count(par_emp, ',') + 1
8 )
9 order by deptno;
DEPTNO ENAME
---------- ----------
10 CLARK
10 KING
10 MILLER
20 JONES
20 FORD
20 ADAMS
20 SMITH
20 SCOTT
30 WARD
30 TURNER
30 ALLEN
30 JAMES
30 BLAKE
30 MARTIN
14 rows selected.
SQL>

Related

Multiple table outputs based on one query

I have one table that has 12 conditions (months). I need to produce 12 outputs (tables) for select statements where the only thing that changes is that one condition (month).
Example:
SELECT * FROM table WHERE month = 01;
SELECT * FROM table WHERE month = 02;
SELECT * FROM table WHERE month = 03;
etc.
In reality, the code is pretty large and chunky, I don't feel like copy pasting it numerous times just to change one condition.
Is there a way to write one statement to produce different outputs based on changing condition?
It looks pretty basic to me, but I can't find the answer.
It doesn't sound like a good approach, but without a fuller picture of the task it's hard to say.
What you can do is generate a list of numbers and use it to produce your required statements, such as:
with months as (
select 1 m
union all
select m + 1 from months
where m<12
)
select Concat('select * from table where month = ', m, ';')
from months
Depending on your requirements and RDBMS you can either use them directly in your IDE or dynamically execute.
In Oracle, you could use hierarchical query to generate months; something like this:
SQL> select empno,
2 ename,
3 job,
4 to_char(hiredate, 'fmMonth', 'nls_date_language = english') month
5 from emp
6 where extract(month from hiredate) in (select level from dual
7 connect by level <= 12
8 )
9 order by extract (month from hiredate);
EMPNO ENAME JOB MONTH
---------- ---------- --------- ------------------------------------
7876 ADAMS CLERK January
7934 MILLER CLERK January
7499 ALLEN SALESMAN February
7521 WARD SALESMAN February
7566 JONES MANAGER April
7698 BLAKE MANAGER May
7782 CLARK MANAGER June
7844 TURNER SALESMAN September
7654 MARTIN SALESMAN September
7839 KING PRESIDENT November
7788 SCOTT ANALYST December
7369 SMITH CLERK December
7900 JAMES CLERK December
7902 FORD ANALYST December
14 rows selected.
SQL>
Can be as simple as left/right join to cte months
declare #table table (mm smallint,somedata varchar(20))
insert into #table
values
(1,'some data for 1')
,(3,'some data for 3')
,(5,'some data for 5')
;with cte_months as (
select 1 mm
union all
select mm + 1 from cte_months
where mm<12
)
select c.mm,t.somedata from #table t
right join cte_months c on t.mm = c.mm

Ignore column in the WHERE clause if the parameter is null, else use IN clause to filter the column in WHERE clause

I need to write a query where I should Ignore any filter on that column if the respective parameter is Null, but should filter with IN clause if the said parameter is not null. I am trying to use the below query but I am not able to make it work. It is the HR DB and Employees table in Oracle 11 XE and I am trying to pass Job ID as a param and this param could be null or it could contain multiple values.
What I have done so far -
SELECT * FROM HR.EMPLOYEES
WHERE
CASE WHEN NVL(:PARAM_JOB_ID,'NONE')= 'NONE' THEN 'NONE' ELSE JOB_ID END IN NVL(:PARAM_JOB_ID,'NONE');
Please guide.
I would use IS NULL logic here:
SELECT *
FROM HR.EMPLOYEES
WHERE JOB_ID IN (:PARAM_JOB_ID) OR :PARAM_JOB_ID IS NULL;
You'll have to split values in :PARAM_JOB_ID into rows. Something like this (Scott's sample schema and its EMP table):
select job, ename
from emp
where ( job in (select trim(regexp_substr(:param_job_id, '[^,]+', 1, level))
from dual
connect by level <= regexp_count(:param_job_id, ',') + 1
)
or :param_job_id is null
)
order by job, ename;
Demonstration in SQL*Plus:
SQL> select job, ename
2 from emp
3 where ( job in (select trim(regexp_substr('&&param_job_id', '[^,]+', 1, level))
4 from dual
5 connect by level <= regexp_count('&&param_job_id', ',') + 1
6 )
7 or '&&param_job_id' is null
8 )
9 order by job, ename;
Enter value for param_job_id: --> empty parameter returns all rows
JOB ENAME
--------- ----------
ANALYST FORD
ANALYST SCOTT
CLERK ADAMS
CLERK JAMES
CLERK MILLER
CLERK SMITH
MANAGER BLAKE
MANAGER CLARK
MANAGER JONES
PRESIDENT KING
SALESMAN ALLEN
SALESMAN MARTIN
SALESMAN TURNER
SALESMAN WARD
14 rows selected.
SQL> undefine param_job_id
SQL> /
Enter value for param_job_id: CLERK, PRESIDENT
JOB ENAME
--------- ----------
CLERK ADAMS
CLERK JAMES
CLERK MILLER
CLERK SMITH
PRESIDENT KING
SQL>

Writing a PLSQL select in array

I'm using a DB 'A' to output a list of numbers :
123455
123456
123457
And I'm looking to build a dynamic statement to look into a DB 'B' with those results as a filter
a. Build an array with the values from DB 'A'
SELECT * FROM my_table
WHERE number in &array;
How can I achieve this ?
The DB 'B' is an Oracle DB.
Thanks
Hm. Looks like your "background" is not Oracle, because there are no "DB"s there. I mean, there are, but not in a context you're using them. If "DB" stands for a "Database", to me it looks as if you're actually talking about tables here.
Also, I don't understand what
The DB 'B' is in PLSQL means.
If "database" is a table, how is it in PL/SQL?
Anyway, to get you started: I'm fetching some data from Scott's EMP and DEPT tables. For collections, I'm using Oracle's built-in types.
These are employees in departments 10 and 20:
SQL> select deptno, ename
2 from emp
3 where deptno in (10, 20)
4 order by deptno, ename;
DEPTNO ENAME
---------- ----------
10 CLARK
10 KING
10 MILLER
20 ADAMS
20 FORD
20 JONES
20 SCOTT
20 SMITH
8 rows selected.
PL/SQL procedure which does something with them (the way I understood the question):
SQL> declare
2 l_a sys.odcinumberlist;
3 l_b sys.odcivarchar2list;
4 begin
5 select deptno
6 bulk collect into l_a
7 from dept
8 where deptno in (10, 20);
9
10 select ename
11 bulk collect into l_b
12 from emp
13 where deptno in (select * from table(l_a))
14 order by ename;
15
16 for i in l_b.first .. l_b.last loop
17 dbms_output.put_line(l_b(i));
18 end loop;
19 end;
20 /
ADAMS
CLARK
FORD
JONES
KING
MILLER
SCOTT
SMITH
PL/SQL procedure successfully completed.
SQL>
Lines #1 - 3 - declaration section
lines #5 - 8 - inserting values (departments) into l_a collection
Lines #10 - 14 - inserting values (employees) into l_b collection, based on values stored in l_a
Lines #16 - 18 - displaying contents of l_b
See if it helps.
[EDIT] After seeing your comment: as far as I can tell, you can't do what you wanted, not as simple as you'd want it to. This is how it works - you enter a comma-separated values as a parameter (that's your "array"), split it into rows and use the result as a subquery:
SQL> SELECT *
2 FROM dept
3 WHERE deptno IN ( SELECT REGEXP_SUBSTR ( '&&par_depts',
4 '[^,]+',
5 1,
6 LEVEL)
7 FROM DUAL
8 CONNECT BY LEVEL <= REGEXP_COUNT ( '&&par_depts', ',') + 1);
Enter value for par_depts: 10,20
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
SQL>
This is a SQL*Plus example; you'll probably have to substitute '&&par_depts' with :par_depts (depending on a tool you use).
Use the MEMBER operator.
First create a collection type in SQL:
CREATE TYPE int_list IS TABLE OF INT;
Then just use it in an SQL statement:
SELECT *
FROM my_table
WHERE value MEMBER OF int_list(123455, 123456, 123457);
Which, for the sample data:
CREATE TABLE my_table ( id, value ) AS
SELECT LEVEL, 123453 + LEVEL FROM DUAL CONNECT BY LEVEL <= 5;
Outputs:
ID
VALUE
2
123455
3
123456
4
123457
If you want it in PL/SQL then:
DECLARE
items int_list := int_list(123455, 123456, 123457);
BEGIN
FOR row IN (
SELECT *
FROM my_table
WHERE value MEMBER OF items
)
LOOP
DBMS_OUTPUT.PUT_LINE( row.id || ', ' || row.value );
END LOOP;
END;
/
Which, for the same data, outputs:
2, 123455
3, 123456
4, 123457
db<>fiddle here
However, if you just want to connect two databases then setup a database link.

SQL Oracle - Replace function in Where Clause

I want to use the REPLACE function in the Where clause to replace a character that I put into a input box. For example if I input R123;R321 I want it to change my input to R123','R321.
Example of my query:
Select
order_no
From
order
Where
order_no in ('&Order_No')
I input the data with this box:
Any help would be appreciated, or if there is some other way to do it without the REPLACE function.
That won't work like this, I'm afraid. You'll have to split input value into rows, e.g.
SQL> select deptno, ename
2 from emp
3 where deptno in (select regexp_substr('&&deptnos', '[^;]+', 1, level)
4 from dual
5 connect by level <= regexp_count('&&deptnos', ';') + 1
6 )
7 order by deptno, ename;
Enter value for deptnos: 10;20
DEPTNO ENAME
---------- ----------
10 CLARK
10 KING
10 MILLER
20 ADAMS
20 FORD
20 JONES
20 SCOTT
20 SMITH
8 rows selected.
SQL>

ORACLE SQL it is possible to customize the order result of a query?

I have a case in which I need to order the query result in a customized order like the following :
the DEPARTEMENT_ID needs to be in this order ( 10 then 50 then 20 )
is there a way to get this result ?
You could use CASE expression in the ORDER BY clause.
I answered a similar question here, https://stackoverflow.com/a/26033176/3989608, you could just tweak it to have your customized conditions in the CASE expression.
For example,
SQL> SELECT ename,
2 deptno
3 FROM emp
4 ORDER BY
5 CASE deptno
6 WHEN 20 THEN 1
7 WHEN 10 THEN 2
8 WHEN 30 THEN 3
9 END
10 /
ENAME DEPTNO
---------- ----------
SMITH 20
FORD 20
ADAMS 20
JONES 20
SCOTT 20
CLARK 10
KING 10
MILLER 10
ALLEN 30
TURNER 30
WARD 30
MARTIN 30
JAMES 30
BLAKE 30
14 rows selected.
SQL>
One way you can do it is to have an other column DISPLAY_ORDER having serial number data in the order that you want it.
so the sql will be
select JOB_ID, DEPARTMENT_ID
from EMPLOYEES
order by DISPLAY_ORDER;
You can use the DECODE to accomplish this.
SELECT JOB_ID,DEPARTMENT_ID
FROM YOURTABLE
ORDER BY DECODE(DEPARTEMENT_ID, 10, 1, 50, 2, 20, 3,4)
Refer to these threads below for more information.
Custom Order in Oracle SQL
DECODE