why do sqlplus variables act funny with a period? - oracle

why do sqplus variables act funny when attempting to use a period after them?
SQL> set define on
SQL> accept goo char prompt 'goo: ';
goo: mygoo
SQL> select '&goo' from dual;
old 1: select '&goo' from dual
new 1: select 'mygoo' from dual
MYGO
-----
mygoo
SQL> select '&goo something' from dual;
old 1: select '&goo something' from dual
new 1: select 'mygoo something' from dual
MYGOOSOMETHING
---------------
mygoo something
SQL> select '&goo.something' from dual;
old 1: select '&goo.something' from dual
new 1: select 'mygoosomething' from dual
MYGOOSOMETHIN
--------------
mygoosomething
SQL> select '&goo..something' from dual;
old 1: select '&goo..something' from dual
new 1: select 'mygoo.something' from dual
MYGOO.SOMETHIN
---------------
mygoo.something
why do I have to double up on periods? is there some better way?

That's not funny at all, but expected default behavior.
SET CONCAT character is a period (unless you set it to some other character). If you want to add a period right after the substitution variable, you have to use two consecutive periods.
If you set concat character to e.g. #, then period acts normally:
SQL> set concat '#'
SQL> select '&goo.' from dual;
old 1: select '&goo.' from dual
new 1: select 'mygoo.' from dual
'MYGOO
------
mygoo.
If you set it back to a period, it acts funny again:
SQL> set concat '.'
SQL> select '&goo.' from dual;
old 1: select '&goo.' from dual
new 1: select 'mygoo' from dual
'MYGO
-----
mygoo
SQL> select '&goo..' from dual;
old 1: select '&goo..' from dual
new 1: select 'mygoo.' from dual
'MYGOO
------
mygoo.
Finally, back to #, which also acts funny and you have to use two of them:
SQL> set concat '#'
SQL> select '&goo#' from dual;
old 1: select '&goo#' from dual
new 1: select 'mygoo' from dual
'MYGO
-----
mygoo
SQL> select '&goo##' from dual;
old 1: select '&goo##' from dual
new 1: select 'mygoo#' from dual
'MYGOO
------
mygoo#
SQL>

Related

Is there a way to accept comma separated input in PLSQL

I am trying to confirm if it's acceptable to provide a comma separated input through a prompt in PLSQL query. For example the input to be like order_no = 12345;67890;09876
Thanks in advance!
Here is my code:
select order_no
from CUSTOMER_ORDER_JOIN
where order_no like nvl('&Order_no', '%')
Generally speaking, no, you can't do that because Oracle considers string you enter as a whole. For example, this query returns nothing because there's no job whose name is Clerk,Manager (both values).
SQL> with emp (ename, job) as
2 (select 'Smith', 'Clerk' from dual union all
3 select 'Allen', 'Salesman' from dual union all
4 select 'Ward' , 'Salesman' from dual union all
5 select 'Jones', 'Manager' from dual
6 )
7 select ename, job
8 from emp
9 where job in ('&par_jobs');
Enter value for par_jobs: Clerk,Manager
no rows selected
SQL>
However, if you have Apex installed in your database, and that Apex version supports apex_string.split function, then you can do something which is close to what you're looking for:
SQL> with emp (ename, job) as
2 (select 'Smith', 'Clerk' from dual union all
3 select 'Allen', 'Salesman' from dual union all
4 select 'Ward' , 'Salesman' from dual union all
5 select 'Jones', 'Manager' from dual
6 )
7 select ename, job
8 from emp
9 where job in (select * from table(apex_string.split('&par_jobs', ',')));
Enter value for par_jobs: Clerk,Manager
ENAME JOB
----- --------
Smith Clerk
Jones Manager
SQL>
Otherwise, you'll have to type a little bit more to achieve that, e.g.
SQL> with emp (ename, job) as
2 (select 'Smith', 'Clerk' from dual union all
3 select 'Allen', 'Salesman' from dual union all
4 select 'Ward' , 'Salesman' from dual union all
5 select 'Jones', 'Manager' from dual
6 )
7 select ename, job
8 from emp
9 where job in (select regexp_substr('&&par_jobs', '[^,]+', 1, level)
10 from dual
11 connect by level <= regexp_count('&&par_jobs', ',') + 1
12 );
Enter value for par_jobs: Clerk,Manager
ENAME JOB
----- --------
Smith Clerk
Jones Manager
SQL>
Basically, YMMV.

PL/SQL Regex search based on both characters and digits

I have a table TA which has a column inv_ref with has data as below.
inv_ref
----------
MX/3280/20
CT/3281/20
CT/3109/20
MX/3272/20
RF/3275/20
My requirement is to fetch whereas middle 4 digit number of inv_ref between 3270 to 3299 also begin with either MX and CT.
select * from TA where regexp_like(inv_ref, 'CT/32[7-9][0-9]/20')
Above query only returns CT, how can return both CT and MX related values omitting RF?
You can use:
SELECT *
FROM TA
WHERE REGEXP_LIKE(inv_ref, '^(CT|MX)/32[7-9][0-9]/20$')
Then if you have the test data:
CREATE TABLE TA ( inv_ref, is_valid ) AS
SELECT 'MX/3280/20', 'Valid' FROM DUAL UNION ALL
SELECT 'CT/3281/20', 'Valid' FROM DUAL UNION ALL
SELECT 'CT/3109/20', 'Invalid, number too low' FROM DUAL UNION ALL
SELECT 'MX/3272/20', 'Valid' FROM DUAL UNION ALL
SELECT 'RF/3275/20', 'Invalid, wrong start' FROM DUAL UNION ALL
SELECT 'CX/3299/20', 'Invalid, wrong start' FROM DUAL UNION ALL
SELECT 'MT/3270/20', 'Invalid, wrong start' FROM DUAL UNION ALL
SELECT 'ACT/3270/20', 'Invalid, wrong start' FROM DUAL;
This outputs:
INV_REF | IS_VALID
:--------- | :-------
MX/3280/20 | Valid
CT/3281/20 | Valid
MX/3272/20 | Valid
db<>fiddle here
It would be much easier to add virtual generated columns:
alter table your_table add (
P1 generated always as (regexp_substr(inv_ref,'^[^/]+')),
P2 generated always as (to_number(regexp_substr(inv_ref,'/(\d+)/',1,1,null,1))),
P3 generated always as (regexp_substr(inv_ref,'[^/]+$'))
);
In this case you can use standard predicates on those columns. Moreover, you can even create indexes on those columns.
Full test case:
CREATE TABLE YOUR_TABLE ( inv_ref ) AS
SELECT 'MX/3280/20' FROM DUAL UNION ALL
SELECT 'CT/3281/20' FROM DUAL UNION ALL
SELECT 'CT/3109/20' FROM DUAL UNION ALL
SELECT 'MX/3272/20' FROM DUAL UNION ALL
SELECT 'RF/3275/20' FROM DUAL UNION ALL
SELECT 'CX/3299/20' FROM DUAL UNION ALL
SELECT 'MT/3270/20' FROM DUAL UNION ALL
SELECT 'ACT/3270/20' FROM DUAL;
alter table your_table add (
P1 generated always as (regexp_substr(inv_ref,'^[^/]+')),
P2 generated always as (to_number(regexp_substr(inv_ref,'/(\d+)/',1,1,null,1))),
P3 generated always as (regexp_substr(inv_ref,'[^/]+$'))
);
select * from your_table
where p1 IN ('CT', 'MX')
and p2 BETWEEN 3270 and 3299;
Result:
MX/3280/20 MX 3280 20
CT/3281/20 CT 3281 20
MX/3272/20 MX 3272 20
You could use
SQL> create table my_test ( inv_ref varchar2(100) ) ;
SQL> insert into my_test values ( 'MX/3280/20') ;
SQL> insert into my_test values ( 'CD/3281/20') ;
SQL> insert into my_test values ( 'CD/3109/20') ;
SQL> insert into my_test values ( 'MX/3272/20') ;
SQL> insert into my_test values ( 'RF/3275/20') ;
Table created.
SQL> SQL> SQL>
1 row created.
SQL> SQL>
1 row created.
SQL> SQL>
1 row created.
SQL> SQL>
1 row created.
SQL> SQL>
1 row created.
SQL> commit ;
Commit complete.
Two options ( in my case I use CD instead of CT ). This option will work as long as the strings are limited to the example. If you would have other combination it won't , as CD|MX means C or D or M or X ). See comments to the answer. #MTO, Thanks for your comments.
SQL> select * from my_test where regexp_like(inv_ref, '[CD|MX]/32[7-9][0-9]/20')
INV_REF
--------------------------------------------------------------------------------
MX/3280/20
CD/3281/20
MX/3272/20
SQL> select * from my_test where regexp_like(inv_ref, 'CD/32[7-9][0-9]/20') or
regexp_like(inv_ref, 'MX/32[7-9][0-9]/20')
INV_REF
--------------------------------------------------------------------------------
MX/3280/20
CD/3281/20
MX/3272/20
You can use the values separated by | (or) as follows:
select * from TA where regexp_like(inv_ref, '^(CT|MX)/32[7-9][0-9]/20');
db<>fiddle

How to sort texts with '_' in Oracle exactly like EXCEL?

In Excel When I sort the texts in ascending order, results shows as below. Text with the underscore character precedes the others. And in Excel cell, when I type in ="_" < "A", then "True" shows as expected.
C10_
C10A
C20_
C20A
But, In Oracle, when I sort in ascending order, results shows as below.
(I guess, Oracle treats '_' < 'A' False)
C10A
C10_
C20A
C20_
How can I make Oracle sort the list exactly as Excel does? I have changed ASC to DESC, but the result was not what I expect.
My sorting code is as below,
WITH DATAA AS (
SELECT *
FROM
(
SELECT 'C10_'rr FROM DUAL
UNION
SELECT 'C10A' rr FROM DUAL
UNION
SELECT 'C20_' rr FROM DUAL
UNION
SELECT 'C20A' rr FROM DUAL
)
)
SELECT *
FROM DATAA
ORDER BY rr ASC;
You can achieve this by altering the sorting method using NLS_SORT as following:
According to ORACLE Documentation:
NLS_SORT specifies the type of sort for character data. It overrides
the default value that is derived from NLS_LANGUAGE.
NLS_SORT contains either of the following values:
NLS_SORT = BINARY | sort_name
BINARY specifies a binary sort. sort_name specifies a linguistic sort
sequence.
Here is how you can achieve the result.
SQL> -- Your original query
SQL> --
SQL> WITH DATAA AS (
2 SELECT *
3 FROM
4 (
5 SELECT 'C10_'rr FROM DUAL UNION
6 SELECT 'C10A' rr FROM DUAL UNION
7 SELECT 'C20_' rr FROM DUAL UNION
8 SELECT 'C20A' rr FROM DUAL )
9 )
10 SELECT *
11 FROM DATAA
12 ORDER BY rr ASC;
RR
----
C10A
C10_
C20A
C20_
--
SQL> -- Now altering the sorting method
SQL> --
SQL> --
SQL> alter session set NLS_SORT = German;
Session altered.
SQL> --
SQL> --
SQL> -- Now see the result
SQL> --
SQL> --
SQL> WITH DATAA AS (
2 SELECT *
3 FROM
4 (
5 SELECT 'C10_'rr FROM DUAL UNION
6 SELECT 'C10A' rr FROM DUAL UNION
7 SELECT 'C20_' rr FROM DUAL UNION
8 SELECT 'C20A' rr FROM DUAL )
9 )
10 SELECT *
11 FROM DATAA
12 ORDER BY rr ASC;
RR
----
C10_
C10A
C20_
C20A
SQL>
Cheers!!

remove a varchar2 string from the middle of table data values

Data in the file_name field of the generation table should be an assigned number, then _01, _02, or _03, etc. and then .pdf (example 82617_01.pdf).
Somewhere, the program is putting a state name and sometimes a date/time stamp, between the assigned number and the 01, 02, etc. (82617_ALABAMA_01.pdf or 19998_MAINE_07-31-2010_11-05-59_AM.pdf or 5485325_OREGON_01.pdf for example).
We would like to develop a SQL statement to find the bad file names and fix them. In theory it seems rather simple to find file names that include a varchar2 data type and remove it, but putting the statement together is beyond me.
Any help or suggestions appreciated.
Something like:
UPDATE GENERATION
SET FILE_NAME (?)
WHERE FILE_NAME (?...LIKE '%STRING%');?
You can find the problem rows like this:
select *
from Files
where length(FILE_NAME) - length(replace(FILE_NAME, '_', '')) > 1
You can fix them like this:
update Files
set FILE_NAME = SUBSTR(FILE_NAME, 1, instr(FILE_NAME, '_') -1) ||
SUBSTR(FILE_NAME, instr(FILE_NAME, '_', 1, 2))
where length(FILE_NAME) - length(replace(FILE_NAME, '_', '')) > 1
SQL Fiddle Example
You can also use Regexp_replace function:
SQL> with t1(col) as(
2 select '82617_mm_01.pdf' from dual union all
3 select '456546_khkjh_89kjh_67_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col
8 , regexp_replace(col, '^([0-9]+)_(.*)_(\d{2}\.pdf)$', '\1_\3') res
9 from t1;
COL RES
-------------------------------------- -----------------------------------------
82617_mm_01.pdf 82617_01.pdf
456546_khkjh_89kjh_67_01.pdf 456546_01.pdf
19998_MAINE_07-31-2010_11-05-59_AM.pdf 19998_MAINE_07-31-2010_11-05-59_AM.pdf
5485325_OREGON_01.pdf 5485325_01.pdf
To display good or bad data regexp_like function will come in handy:
SQL> with t1(col) as(
2 select '826170_01.pdf' from dual union all
3 select '456546_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col bad_data
8 from t1
9 where not regexp_like(col, '^[0-9]+_\d{2}\.pdf$');
BAD_DATA
--------------------------------------
19998_MAINE_07-31-2010_11-05-59_AM.pdf
5485325_OREGON_01.pdf
SQL> with t1(col) as(
2 select '826170_01.pdf' from dual union all
3 select '456546_01.pdf' from dual union all
4 select '19998_MAINE_07-31-2010_11-05-59_AM.pdf' from dual union all
5 select '5485325_OREGON_01.pdf' from dual
6 )
7 select col good_data
8 from t1
9 where regexp_like(col, '^[0-9]+_\d{2}\.pdf$');
GOOD_DATA
--------------------------------------
826170_01.pdf
456546_01.pdf
To that end your update statement might look like this:
update your_table
set col = regexp_replace(col, '^([0-9]+)_(.*)_(\d{2}\.pdf)$', '\1_\3');
--where clause if needed

Short test statements in PL-SQL?

In SQL server I can print out the value of something with a select statement.
SELECT 'xyz'
SELECT GetDate()
Can I do something similar in Oracle without adding FROM <tablename>?
This is the purpose of the dual table. Oracle supplies the Dual in every database and it's accessible, by default, to everyone that connects. It's a single-row, single column table that is useful for testing expressions and pseuducolumns against. Example
SELECT 'xyz' from dual;
SQL> select user,sysdate,lower(user) loweruser, 10*1023 from dual;
USER SYSDATE LOWERUSER 10*1023
---------- ---------- ---------- ----------
NKODNER 22-NOV-11 nkodner 10230
There is a dual dummy table in Oracle, so try:
SELECT GetDate() FROM dual
You can't. You must use DUAL fictious table
To get current system date, you would type
SELECT SYSDATE FROM DUAL

Resources