Condense AND clause Oracle sql - oracle

I've come across this in one of the sql scripts we have for one of our apps. I notice that it's used in various other places, but isn't it just checking for an existence of an item?
AND INSTR((SELECT (',' || REPLACE('OWN, JO', ' ', NULL) || ',') b FROM DUAL),
(',' || aao.AcctRoleCd || ',')) > 0
Where it's looking to see if 'OWN' or 'JO' is in aao.AcctRoleCd. If it is then INSTR would result in its index in the string, so it'll be greater than one. So the AND clause would be true.
Isn't this poor to check if an item exists like this? Would something more of the lines of an IN clause be better?
AND aao.AcctRoleCd IN ('OWN', 'JO');

Almost:
'OWN, JO' is a text literal.
REPLACE('OWN, JO', ' ', NULL) just strips the space from the string giving 'OWN,JO'.
',' || 'OWN,JO' || ',' just concatenates commas to the start and end of the string giving ',OWN,JO,'.
(SELECT ',OWN,JO,') b FROM DUAL) is redundant and you can just use the previous text literal.
INSTR( ',OWN,JO,', (',' || aao.AcctRoleCd || ',') ) > 0 is looking for a comma at the start and end of the substring which equals aao.AcctRoleCd so could match either 'OWN', 'JO' or 'OWN,JO'.
So you can replace it with:
AND aao.AcctRoleCd IN ( 'OWN', 'JO', 'OWN,JO' )
Now, it may be that 'OWN,JO' is not a match you are expecting (or may not even be a valid value) and you can strip it from the list but that is something you will need to determine.

You are right, but thanks for sharing that surprisingly bad code.
(SELECT (',' || REPLACE('OWN, JO', ' ', NULL) || ',') b FROM DUAL)
is a completely unnecessary scalar subquery. It could be replaced by
',' || REPLACE('OWN, JO', ' ', NULL) || ','
However, since that snippet has only literals then it could be further replaced by the result:
,OWN,JO,
and yes, it would seem that whole INSTR could be replaced by the code you suggest, unless aao.AcctRoleCd could contain 'N,J' or some such in which case the original code and your code would get different results. I seriously doubt that is a problem.
Best regards, Stew

Related

In Oracle execution time for finding a data inside comma seperated column taking too long

I am trying to find out if the list of data is in comma separated column. I have a code select * from my_table where (regexp_substr ( listcolumn, '[^,]+', 1, level ) in ('a','d')) connect by level <= regexp_count(listcolumn, ',') + 1; and table with a lot data. The problem is it works for small data table but it is taking too much time to execute for a lot a table with a lot of data. I am not expert in database. so can you please help to how to resolve this problem. Thank you in advance.
Don't split the string, look for a sub-string match (with the surrounding delimiters so that you match entire terms):
SELECT *
FROM my_table
WHERE ',' || listcolumn || ',' LIKE '%,' || 'a' || ',%'
OR ',' || listcolumn || ',' LIKE '%,' || 'd' || ',%';

Can you Insert a Carriage Return within a listagg function that is also concatenating different elements?

I am currently attempting to aggregate a list of concatenated values. However, I was wondering if it was possible to use a carriage return function within the listagg so that each concatenated value would be returned onto a new line. Does anyone have any experience with this? Thank you!
Here is a portion of my code: (Please note that this is also taking place inside a view join)
(select (listagg ((z.event_name || ' - ' || z.event_date), '; ') within group
(order by z.event_name))
from
a_visit_event z
where
z.subject_no = av.subject_no
and
z.visit_date = av.visit_date) event_name,
Just add chr(13)
i.e.
listagg ((z.event_name || ' - ' || z.event_date), '; '||chr(13))

Cannot insert empty string in oracle table

i am trying to insert data into one of my table.
insert into t_transaction_query_log
(log_id,
posting_id,
fee_id,
fee_status,
fee_type,
pay_mode,
accounting_date,
policy_id,
money_id,
finish_time,
source_type,
fee_amount,
cr_seg,
dr_seg,
product_id' || v_insert_str_cr_seg ||
v_insert_str_dr_seg || ')
select s_transaction_query_log__lg_id.nextval,
tg.je_posting_id,
tg.fee_id,
tg.fee_status,
tg.fee_type,
60,
tg.accounting_date,
tg.policy_id,
tg.currency_id,
trunc(sysdate,
''dd''),
2,
tg.fee_amount,' ||v_str_cr_seg
|| ',' || v_str_dr_seg || ',
tg.product_id' || v_insert_str_cr_seg ||
v_insert_str_dr_seg || '
from t_ri_fee_gl tg
where tg.posted = ''Y''
i have declared v_str_cr_seg,v_str_dr_seg as empty strings i.e v_str_dr_seg varchar(50)='';
before executing this query i am able to append some values to these two strings
but in case there is no change in these strings from the declaration
i am getting the computed query as
insert into t_transaction_query_log
(log_id,
posting_id,
fee_id,
fee_status,
fee_type,
pay_mode,
accounting_date,
policy_id,
money_id,
finish_time,
source_type,
fee_amount,
cr_seg,
dr_seg,
product_id)
select s_transaction_query_log__lg_id.nextval,
tg.je_posting_id,
tg.fee_id,
tg.fee_status,
tg.fee_type,
60,
tg.accounting_date,
tg.policy_id,
tg.currency_id,
trunc(sysdate,
'dd'),
2,
tg.fee_amount, , ,
tg.product_id
from t_ri_fee_gl tg
where tg.posted = 'Y'
which in turn results in missing expression error.
i have tried using nvl function as
nvl(v_str_cr_seg, '') and `nvl(v_str_cr_seg, null)`
but still i am getting the same error
how can i insert even though v_str_cr_seg and v_str_dr_seg are unchanged
i.e empty strings ''
Ora DB:
empty string = null
enter link description here
Note:
Oracle Database currently treats a character value with a length of zero as null. However, this may not continue to be true in future releases, and Oracle recommends that you do not treat empty strings the same as nulls.
If this is an insert Statement in the middle of a PL/SQL Routine, then you would not string concatenate. It would rather look like this
tg.fee_amount, v_str_cr_seg, v_str_dr_seg
But something from your question tells me that you are composing a string that you want to execute immediate.
In this case you have to make sure you are giving quotes:
tg.fee_amount,''' ||v_str_cr_seg
|| ''',''' || v_str_dr_seg || ''',
tg.product_id''' || v_insert_str_cr_seg ||
v_insert_str_dr_seg || '''
You need to escape the single quote (') with two quotes in a row ('')
But if your strings already come with quotes around it unless null you may want to work with nvl this way
tg.fee_amount,' ||nvl(v_str_cr_seg,'''''')
|| ',' || nvl(v_str_dr_seg,'''''') || ',
Try using chr(0) instead of ''. Null character is a sign of the end of a string.

How to select rows with 4-byte UTF-8 chars in Oracle DB?

I need to detect whether my table has 4-byte UTF-8 characters and delete them if any.
I tried following SQLs, but they return error ORA-12726: unmatched bracket in regular expression. All brackets are matched, so error is incorrect.
select REGEXP_REPLACE(asd𠜎aasd',
'['
|| UTL_I18N.RAW_TO_CHAR ('010000', 'UTF8')
|| '-'
|| UTL_I18N.RAW_TO_CHAR ('01FFFF', 'UTF8')
|| ']', '')
from dual;
select REGEXP_REPLACE('asd𠜎aasd',
'['
|| chr(to_number('010000', 'xxxxxx'))
|| '-'
|| chr(to_number('01FFFF', 'xxxxxx'))
|| ']', '')
from dual;
What is wrong with these queries?
Do you know how to find rows by character code points range?
You can use the UNISTR function; the 𠜎 character is codepoint U+2070E, which in UTF-16 is D841DF0E. As the documentation notes:
Supplementary characters are encoded as two code units, the first from the high-surrogates range (U+D800 to U+DBFF), and the second from the low-surrogates range (U+DC00 to U+DFFF).
Which means you can represent it with:
select unistr('\D841\DF0E') from dual;
UNISTR('\D841\DF0E')
--------------------
𠜎
You can then use UNISTR to construct your range:
select REGEXP_REPLACE('asd𠜎aasd',
'['
|| UNISTR('\D800\DC00')
|| '-'
|| UNISTR('\DBFF\DFFF')
|| ']', '')
from dual;
REGEXP_REPLACE('ASD𠜎AASD','['||UNISTR('\D800\DC00')||'-'||UNISTR('\DBFF\DFFF')||']','')
----------------------------------------------------------------------------------------
asdaasd
Assuming you want to exclude all supplementary characters; you can adjust the range if you have a more narrow focus.
maybe this not what exacly you need but to solve the error ora-12726 use this query
select REGEXP_REPLACE('asdaasd',
'\['
|| chr(to_number('010000', 'xxxxxx'))
|| '-'
|| chr(to_number('01FFFF', 'xxxxxx'))
|| '\]', '')
from dual;
When you convert from raw to char you must supply the octet sequence in the chosen encoding. What you supply are the Unicode code points but not their utf8 encoding. To get that, you can use online tools like this one or a code table. E.g. U+010000 is represented as f0908080in utf-8 (range extended to U+010000-U+02FFFF to include source character).
So in the sample case, use the following:
select REGEXP_REPLACE('asd'||UTL_I18N.RAW_TO_CHAR ('F0A09C8E', 'AL32UTF8')||'aasd',
'['
|| UTL_I18N.RAW_TO_CHAR ('F0908080', 'AL32UTF8')
|| '-'
|| UTL_I18N.RAW_TO_CHAR ('F0AFBFBF', 'AL32UTF8')
|| ']', '')
from dual;
If you prefer RAW_TO_CHAR, you can do it like this:
SELECT REGEXP_REPLACE('asd𠜎aasd',
'['
|| UTL_I18N.RAW_TO_CHAR ('F0908080', 'AL32UTF8')
|| '-'
|| UTL_I18N.RAW_TO_CHAR ('F48FBFBF', 'AL32UTF8')
|| ']', '')
FROM dual;
Oracle Character set UTF8 is not the same as commonly known encoding UTF-8. Oracle Character set UTF8 means CESU-8 encoding. For UTF-8 you have to use Oracle character set AL32UTF8
U+01FFFF is not the biggest Unicode codepoint, your string may contain "冕" (U+02F8D3 CJK COMPATIBILITY IDEOGRAPH-2F8D3). The biggest possible Unicode codepoint is U+10FFFF, which is equal to UNISTR('\DBFF\DFFF')(as provided by Alex Poole's answer), resp. UTL_I18N.RAW_TO_CHAR ('F48FBFBF', 'AL32UTF8').
btw, for the conversion I use this page: Unicode code converter

How to use SELECT result record as DECODE parameters?

Is it possible to use SELECT result as DECODE parameters when this SELECT returns only one record with prepared string?
For example:
SELECT replace(replace(serialized_data)..)..) as result FROM table
Returns following result in ONE ROW:
0,'label0',1,'label1',2,'label2'
But when I put this to DECODE it's being interpreted as ONE PARAMETER.
Is there possiblity to convert this result "string" to "pure" sql code? ;)
Thanks for any help.
You could kind of replicate decode by use of instr and substr. An example below (which could probably be tidied up considerably but works):
select DTXT
,Nvl(
Substr(
DTXT
,Instr(DTXT, SEARCHTXT || VALMTCH)
+ Length(SEARCHTXT || VALMTCH)
, Instr(DTXT, VALSEP, Instr(DTXT, SEARCHTXT || VALMTCH))
- Instr(DTXT, SEARCHTXT || VALMTCH)
- Length(SEARCHTXT || VALMTCH))
,CASEOTHER)
as TXTMATCH
from (select '0=BLACK;1=GREEN;2=YELLOW;' as DTXT
,'1' as SEARCHTXT
,';' as VALSEP
,'=' as VALMTCH
,'OTHER' as CASEOTHER
from Dual)
You do need to have a semi-colon (VALSEP) at the end of the text though to ensure you can find the last value (though it's possible to work around that if I looked into it further).
list_agg or wm_concat depending on version of oracle.
http://docs.oracle.com/cd/E14072_01/server.112/e10592/functions087.htm
You can use dynamic SQL:
declare
DECTXT varchar2(4000);
begin
select replace(replace(serialized_data)..)..) as result into dectxt from table;
execute immediate 'select decode(col1,' || DECTXT || ') from tab1';
end;
This is just a quick example, not showing any output, etc.

Resources