how the utl_raw.cast_to_varchar2(NLSSORT(')) works - plsql

why this query not giving any records. Please guide me
SELECT *
FROM DUAL
WHERE utl_raw.cast_to_varchar2(NLSSORT('sravanth','nls_sort=binary_ai'))
LIKE utl_raw.cast_to_varchar2(NLSSORT('sravan','nls_sort=binary_ai'))|| '%'

The reason this is not working in obvious when displaying the output of NLSSORT :
SELECT NLSSORT('sravanth','nls_sort=binary_ai') FROM DUAL
UNION ALL
SELECT NLSSORT('sravan','nls_sort=binary_ai') FROM DUAL
NLSSORT('SRAVANTH','NLS_SORT=BINARY_AI')
73726176616E746800
73726176616E00
^^
Please note that NLSSORT add an extra NUL char at the end of the string. This is not specified in the documentation -- and you shouldn't probably assume it will be always behave the same. Anyway, if you really want to use NLSSORT that way, you will have to handle the extra byte by hand. See https://stackoverflow.com/a/20490866/2363712 as an example.

Related

Oracle PL/SQL - Check number formats and masks

After a long time without doing PL/SQL...
I need a suggestion from the community, for something that apparently is trivial, but I am a little bit stuck on this.
I am building a load from an CSV file, and there we have a column with the amount.
The CSVs come from different suppliers, and each one can send the amount in different formats.
So I should reject lines from the CSV with amount that are not in correct number format (999,999,999,999.00), because can be an incorrect amount reported by the supplier and should be fixed.
Coming with the formats 999.999.999,00 or 999999999,99, I can do some treatments through PL/SQL to convert.
But I am having problems with values with different formats as 999,9,9,9 or whatever...
I am trying to use common functions (TO_NUMBER, TO_CHAR). But not having much success...
SELECT TO_NUMBER('999,9,9,9') FROM DUAL; or SELECT TO_NUMBER('999,9,9,9','99G990G990G990G990G990D00') FROM DUAL;
The result is ORA-01722: invalid number, and that is brilliant! However, it would reject other formats that seem correct, like 9,999.99
SELECT TO_NUMBER('999,9,9,9','99G999G999G999G999G999D00') FROM DUAL;
Using a format mask, the value 999,9,9,9 is converted to 999999 - and it is not fine. However using the format mask works fine for 9,999.99, for example.
Do you know any other function provided by Oracle that can help solve my issue?
Or any suggestion on how can I do this?
Thank you very much.
Att.,
Guilherme
You can use a regular expression to determine a valid number or not. Then if valid replace comma with nulls and thee convert that to a number. The queries below use the following regular expression:
'^(\d{1,3})(\,\d{3})*(\.\d{2}|\.?)$'
It breaks down as follows
^(\d{1,3}) --- at beginning of string 1 to 3 digits
(\,\d{3})* --- followed by 0 or more sets of comma followed by 3 digits
(.\d{2}|.?) --- that followed by decimal point followed by 2 digits OR (|) optional decimal point
$ --- end of string
Demo:
with test (num, expected)
as (select '999,999,999,999.00', 'valid' from dual union all
select '999,99,999,999.00', 'invalid' from dual union all
select '999.00', 'valid' from dual union all
select '99', 'valid' from dual union all
select '9,999.', 'valid' from dual union all
select '9,999..0', 'invalid' from dual union all
select '999,99999,999.00', 'invalid' from dual
)
select num
, expected
, case when regexp_like(num,'^(\d{1,3})(\,\d{3})*(\.\d{2}|\.?)$')
then to_char(to_number(replace(num,',',null)))
else 'Not Valid Number'
end converted
from test;
In a live setting you would not want the "to_char(to_number ..." structure). This was used for demonstration/testing as both then and else of the case statement must result in same data type. A live version would appear something like:
with test (num)
as (select '999,999,999,999.00' from dual union all
select '999,99,999,999.00' from dual union all
select '999.00' from dual union all
select '99' from dual union all
select '9,999.' from dual union all
select '9,999..0' from dual union all
select '999,99999,999.00' from dual
)
select to_number(replace(num, ',', null))
from test
where regexp_like(num,'^(\d{1,3})(\,\d{3})*(\.\d{2}|\.?)$');

Get the correct Hexadecimal for strange symbol

I have this strange symbol on my pl/sql developer client (check image it's the symbol between P and B )
In the past, and for a different symbol, i was able to update my DB and remove them making this:
update table set ent_name = replace(ent_name, UTL_RAW.CAST_TO_VARCHAR2(HEXTORAW('C29B')), ' ');
The problem is that i dont remember how I translated the symbol (i had at that time) to the C29B.
Can you help me to understand how can i translate the currenct symbol to the HEX format, to i can use the command to remove it from my database?
Thanks
As long as it's in your table, you can use the DUMP function to find it.
Use DUMP to get the byte representation of the data in code of you wish to inspect for weirdness.
A good overview: Oracle / PLSQL: DUMP Function
Here's some text with plain ASCII:
select dump('Dashes-and "smart quotes"') from dual;
Typ=96 Len=25:
68,97,115,104,101,115,45,97,110,100,32,34,115,109,97,114,116,32,113,117,111,116,101,115,34
Now introduce funny characters:
select dump('Dashes—and “smart quotes”') from dual;
Typ=96 Len=31:
68,97,115,104,101,115,226,128,148,97,110,100,32,226,128,156,115,109,97,114,116,32,113,117,111,116,101,115,226,128,157
In this case, the number of bytes increased because my DB is using UTF8. Numbers outside of the valid range for ASCII stand out and can be inspected further.
The ASCIISTR function provides an even more convenient way to see the special characters:
select asciistr('Dashes—and “smart quotes”') from dual;
Dashes\2014and \201Csmart quotes\201D
This one converts non-ASCII characters into backslashed Unicode hex.
The DUMP function takes an additional argument that can be used to format the output in a nice way:
select DUMP('Thumbs 👍', 1017) from dual;
Typ=96 Len=11 CharacterSet=AL32UTF8: T,h,u,m,b,s, ,f0,9f,91,8d
select DUMP('Smiley 😊 Face', 17) from dual;
Typ=96 Len=16: S,m,i,l,e,y, ,f0,9f,98,8a, ,F,a,c,e

How to replace occurrence only on the start of the string in Oracle SQL?

I have a source column and I want to search for string values starting with 05, 5 971971 and 97105 to be replaced by 9715. As showin in output table.
SOURCE OUTPUT
0514377920 971514377920
544233920 971544233920
971971511233920 971511233920
9710511233920 971511233920
I tried following which works for first case.
SELECT REGEXP_REPLACE ('0544377905', '^(\05*)', '9715')FROM dual;
But following is not working, for second case:
SELECT REGEXP_REPLACE ('544377905', '^(\5*)', '9715')FROM dual;
Something is wrong with my regular expression. As I am getting: ORA-12727: invalid back reference in regular expression.
You can provide your four patterns using alternation; that is, in parentheses with a vertical bar between them:
with t(source) as (
select '0514377920' from dual
union all select '544233920' from dual
union all select '971971511233920' from dual
union all select '9710511233920' from dual
)
SELECT source, REGEXP_REPLACE (source, '^(05|5|9719715|97105)', '9715') as output
FROM t;
SOURCE OUTPUT
--------------- --------------------
0514377920 971514377920
544233920 971544233920
971971511233920 971511233920
9710511233920 971511233920
Depending on your data and any other restrictions you have, you may be able to make it as simple as replacing the first part of any string that has a 5 in it, which works for your small sample:
SELECT source, REGEXP_REPLACE (source, '^.[^5]?5', '9715') as output
FROM t;
That matches zero or more characters that are not 5, followed by a 5. That may be too simplistic for your real situation though.

SQLite X'...' notation with column data

I am trying to write a custom report in Spiceworks, which uses SQLite queries. This report will fetch me hard drive serial numbers that are unfortunately stored in a few different ways depending on what version of Windows and WMI were on the machine.
Three common examples (which are enough to get to the actual question) are as follows:
Actual serial number: 5VG95AZF
Hexadecimal string with leading spaces: 2020202057202d44585730354341543934383433
Hexadecimal string with leading zeroes: 3030303030303030313131343330423137454342
The two hex strings are further complicated in that even after they are converted to ASCII representation, each pair of numbers are actually backwards. Here is an example:
3030303030303030313131343330423137454342 evaluates to 00000000111430B17ECB
However, the actual serial number on that hard drive is 1141031BE7BC, without leading zeroes and with the bytes swapped around. According to other questions and answers I have read on this site, this has to do with the "endianness" of the data.
My temporary query so far looks something like this (shortened to only the pertinent section):
SELECT pd.model as HDModel,
CASE
WHEN pd.serial like "30303030%" THEN
cast(('X''' || pd.serial || '''') as TEXT)
WHEN pd.serial like "202020%" THEN
LTRIM(X'2020202057202d44585730354341543934383433')
ELSE
pd.serial
END as HDSerial
The result of that query is something like this:
HDModel HDSerial
----------------- -------------------------------------------
Normal Serial 5VG95AZF
202020% test case W -DXW05CAT94843
303030% test case X'3030303030303030313131343330423137454342'
This shows that the X'....' notation style does convert into the correct (but backwards) result of W -DXW05CAT94843 when given a fully literal number (the 202020% line). However, I need to find a way to do the same thing to the actual data in the column, pd.serial, and I can't find a way.
My initial thought was that if I could build a string representation of the X'...' notation, then perhaps cast() would evaluate it. But as you can see, that just ends up spitting out X'3030303030303030313131343330423137454342' instead of the expected 00000000111430B17ECB. This means the concatenation is working correctly, but I can't find a way to evaluate it as hex the same was as in the manual test case.
I have been googling all morning to see if there is just some syntax I am missing, but the closest I have come is this concatenation using the || operator.
EDIT: Ultimately I just want to be able to have a simple case statement in my query like this:
SELECT pd.model as HDModel,
CASE
WHEN pd.serial like "30303030%" THEN
LTRIM(X'pd.serial')
WHEN pd.serial like "202020%" THEN
LTRIM(X'pd.serial')
ELSE
pd.serial
END as HDSerial
But because pd.serial gets wrapped in single quotes, it is taken as a literal string instead of taken as the data contained in that column. My hope was/is that there is just a character or operator I need to specify, like X'$pd.serial' or something.
END EDIT
If I can get past this first hurdle, my next task will be to try and remove the leading zeroes (the way LTRIM eats the leading spaces) and reverse the bytes, but to be honest, I would be content even if that part isn't possible because it wouldn't be hard to post-process this report in Excel to do that.
If anyone can point me in the right direction I would greatly appreciate it! It would obviously be much easier if I was using PHP or something else to do this processing, but because I am trying to have it be an embedded report in Spiceworks, I have to do this all in a single SQLite query.
X'...' is the binary representation in sqlite. If the values are string, you can just use them as such.
This should be a start:
sqlite> select X'3030303030303030313131343330423137454342';
00000000111430B17ECB
sqlite> select ltrim(X'3030303030303030313131343330423137454342','0');
111430B17ECB
I hope this puts you on the right path.

SQLite3 can't do addition in WHERE clause?

I have the following simple query that I execute on a SQLite3 database:
SELECT AField,AnotherField FROM ATable WHERE AnIntField>strftime('%s');
This works fine and returns the expected result.
If I perform the slightly modified query:
SELECT AField,AnotherField FROM ATable WHERE AnIntField+86400>strftime('%s');
Then I don't get any results! This doesn't make any sense! I have tried putting brackets around AnIntField+86400 but that doesn't help. And yes, the values of AnIntField are sufficiently larger than strftime('%s') that it won't return different results.
Is there any reason for this behaviour?
This is a total guess but what happens if you wrap AnIntField+86400 in the SQLite3 equivalent of a cast ?
In TSQL you'd do something like this:
WHERE CAST(AnIntField + 86400 AS int) > strftime('%s')
My thought is that the AnIntField+val is coming out as an append rather than addition.
Actually, if cast doesn't work, try changing select to
SELECT AnIntField+86400 FROM ...
And see what it spits out.

Resources