How to read the length of a string in Oracle? - oracle

I created the following table with one text column:
create table table1 (
col1 varchar2(20)
);
Then I inserted one Polish letter to it:
insert into table1 values ('ą')
How can I obtain the number of characters in this string? It should be 1, but all functions that I know return other results:
LENGTH(COL1) -> 2,
LENGTHC(COL1) -> 2,
LENGTH2(COL1) -> 2,
LENGTH4(COL1) -> 2,
LENGTHB(COL1) -> 6.
The SELECT * FROM NLS_DATABASE_PARAMETERS; returns:
PARAMETER VALUE
---------------------------------
NLS_RDBMS_VERSION 18.0.0.0.0
NLS_NCHAR_CONV_EXCP FALSE
NLS_LENGTH_SEMANTICS BYTE
NLS_COMP BINARY
NLS_SORT BINARY
NLS_NCHAR_CHARACTERSET AL16UTF16
NLS_CHARACTERSET AL32UTF8
NLS_ISO_CURRENCY AMERICA
NLS_TERRITORY AMERICA
NLS_LANGUAGE AMERICAN
... ...
You can check it on: dbfiddle.uk.

It is most likely a bug with the version of Oracle you are running the query on or an issue with the character being encoded on the web server.
I have almost all of the same NLS parameters on my system except I am on Oracle 19 instead of 18.
Even on livesql.oracle.com it returns the results as you would expect but again, that database is on Oracle 19.

Related

Oracle REGEXP_LIKE ignore CASE SENSITIVITY

We are trying the following request in Oracle Database 19.3 and the result is ever like the case sensitivity was ignores.
select 1 from dual where regexp_like('CHIEN', '[a-z]+', 'c');
And the result of the request is
1
Even with:
ALTER SESSION SET NLS_COMP=ANSI;
ALTER SESSION SET NLS_SORT=GERMAN_AI;
We are thinking that's come from a NLS parameter but we don't found which one.
We have those parameters:
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
nls_calendar string GREGORIAN
nls_comp string BINARY
nls_currency string €
nls_date_format string DD/MM/RR
nls_date_language string FRENCH
nls_dual_currency string €
nls_iso_currency string FRANCE
nls_language string FRENCH
nls_length_semantics string BYTE
nls_nchar_conv_excp string FALSE
nls_numeric_characters string ,
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
nls_sort string FRENCH
nls_territory string FRANCE
nls_time_format string HH24:MI:SSXFF
nls_timestamp_format string DD/MM/RR HH24:MI:SSXFF
nls_timestamp_tz_format string DD/MM/RR HH24:MI:SSXFF TZR
nls_time_tz_format string HH24:MI:SSXFF TZR
And our NLS_LANG is set to French_France.AL32UTF8
Finally I found the solution.
The problem was with NLS_SORT=FRENCH (or any linguistic sort)
NLS_SORT=FRENCH => AaBbCcDdEeFfGgHhIiJjKkLlMmNn .... Z
And when we are lookup for regular expression [a-z], we include BCDE ... Z
The solution is to set NLS_SORT=BINARY
In my case, I set environment variable NLS_SORT=BINARY. Without that, Oracle take the default NLS_SORT value that correspond to NLS_LANG.

Number column in Oracle table storing 'hidden' decimal places

First ever question here, because searching for these terms is way too vague. I don't know how the data got in to the table (I think just by "insert into tomtest(test) values (280.1);" but can't be sure) but this example seems odd. Why does the straight select show 1 decimal place, but the to_char select shows 14 decimal places?
SQL> set linesize 100
SQL> select banner from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 12c Enterprise Edition Release 12.2.0.1.0 - 64bit Production
PL/SQL Release 12.2.0.1.0 - Production
CORE 12.2.0.1.0 Production
TNS for Linux: Version 12.2.0.1.0 - Production
NLSRTL Version 12.2.0.1.0 - Production
SQL> describe tomtest
Name Null? Type
----------------------------------------------------- -------- ------------------------------------
TEST NUMBER
SQL> select * from tomtest;
TEST
----------
280.1
SQL> select * from tomtest where test = 280.1;
no rows selected
SQL> select to_char(test) from tomtest;
TO_CHAR(TEST)
----------------------------------------
280.10000000000002
SQL> SELECT data_type, data_length, data_precision, data_scale FROM USER_TAB_COLS WHERE TABLE_NAME = 'TOMTEST' AND COLUMN_NAME = 'TEST';
DATA_TYPE DATA_LENGTH DATA_PRECISION DATA_SCALE
NUMBER 22
The reason is the default numberformat in sql plus.
in the database you have the value 280.10000000000002 and not 280.1
here is an example
SQL> select 128.000000000000000000006 as test from dual;
test
-------------------------
128
SQL> select 128.200000000000000000006 as test from dual;
test
-------------------------
128,2
you can change the number format and check again
SQL> SET NUMF 999999999.99999999999999999999999999999999
SQL> select 128.200000000000000000006 as test from dual;
test
-------------------------------------------
128.20000000000000000000600000000000
SQL>
Update:
This phenomenon can be explained by the fact that your data type is only defined as a number, without precise specification of precision.
This means that you can save a decimal number, but do not give an exact specification of the decimal places.
Here is an example that shows the differences.
CREATE TABLE test_num(
n1 NUMBER,
n2 NUMBER(23,15));
INSERT INTO test_num VALUES(208.20000000000006,208.20000000000006);
SELECT t.*FROM test_num t;
Resut in PLSQL/Developer:
N N2
--- ---
208,2 208,200000000000030
in plsql Developer you can display numbers as char. If you want to that activate a checkbox in the preferences: Tools -> Preferences -> SQL Window -> Number fields to_char
in your case, I would round a number value to a certain number of decimal places on insert/update or change your data type from number to number(p, s) (Where p is the precision and s is the scale.)

Set NLS_SORT and NLS_COMP in oracle

I am trying to set NLS_COMP and NLS_SORT for the current session to check if it will make a difference with regards to some SQL query I am running that is case sensitive, for some reason the parameters will not change
what am I doing wrong?
SELECT * FROM nls_database_parameters where parameter in ('NLS_COMP','NLS_SORT');
>>> NLS_SORT BINARY
>>> NLS_COMP BINARY
SELECT * FROM nls_instance_parameters where parameter in ('NLS_COMP','NLS_SORT');
>>> NLS_SORT NULL
>>> NLS_COMP BINARY
ALTER SESSION SET NLS_COMP=LINGUISTIC;
>>>session SET altered.
ALTER SESSION SET NLS_SORT=BINARY_CI;
>>>session SET altered.
SELECT * FROM nls_database_parameters where parameter in ('NLS_COMP','NLS_SORT');
>>> NLS_SORT BINARY
>>> NLS_COMP BINARY
SELECT * FROM nls_instance_parameters where parameter in ('NLS_COMP','NLS_SORT');
>>> NLS_SORT NULL
>>> NLS_COMP BINARY
You are setting the session values but reading the system ones. Try reading from NLS_SESSION_PARAMETERS:
SELECT *
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER IN ('NLS_COMP', 'NLS_SORT');

Are default values supported in Oracle for columns with char semantics?

I was trying to add a column to a table but got a surprising effect with the DEFAULT clause. In a table with existing rows, I added a new column like so:
alter table t add c char(1 char) default 'N' not null;
When I subsequently added a check constraint to the table, it failed:
alter table t add constraint chk check(c in ('N', 'Y'));
Which resulted in
ERROR at line 1:
ORA-02293: cannot validate (T.CHK) - check constraint violated.
Other info:
Because I'm setting the units explicitly (i.e., char(1 char) as opposed to just char(1)), I don't expect the value of nls_length_semanatics to be relevant.
After adding the column as char(1 char), the newly added "N"s are actually "N " and I'm not sure what the extra whitespace is.
Adding the column as char(1 byte) works as expected;
Adding the column without the "default 'N' not null" followed by updating all existing rows to 'N', followed by altering the column to 'not null' also works as expected.
NLS_CHARACTERSET is AL32UTF8, but I don't expect that to be relevant either.
Database is Oracle 11g; 11.2.0.1.0.
Thanks.
I believe that what you're seeing is a bug that relies on a couple different things interacting
First, the database character set has to be a variable width character set (i.e. AL32UTF8) so that a single character may require up to four bytes of storage.
Second, the column must be declared with character length semantics
Third, starting in 11.1, Oracle added an optimization so that if you add a column to the table that is declared NOT NULL and that has a DEFAULT that Oracle could do that simply by updating the data dictionary rather than actually storing the default value in every row of the table.
When both of those things are true, it appears that the value that is returned has a length of 4 and is padded with the CHR(0) character.
SQL> select * from v$version;
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
PL/SQL Release 11.2.0.1.0 - Production
CORE 11.2.0.1.0 Production
TNS for 64-bit Windows: Version 11.2.0.1.0 - Production
NLSRTL Version 11.2.0.1.0 - Production
SQL> create table foo( col1 number );
Table created.
SQL> insert into foo values( 1 );
1 row created.
SQL> commit;
Commit complete.
SQL> alter table foo add c char(1 char) default 'N' not null;
Table altered.
SQL> alter table foo add constraint chk_foo check( c in ('Y', 'N') );
alter table foo add constraint chk_foo check( c in ('Y', 'N') )
*
ERROR at line 1:
ORA-02293: cannot validate (SCOTT.CHK_FOO) - check constraint violated
SQL> select c, dump(c) from foo;
C DUMP(C)
---- ------------------------------
N Typ=1 Len=4: 78,0,0,0
If you actually force the value to be stored in the table, you'll get the expected behavior where there is no CHR(0) padding. So if I insert a new row into the table, it passes.
SQL> insert into foo(col1) values (2);
1 row created.
SQL> select c, dump(c) from foo;
C DUMP(C)
---- ------------------------------
N Typ=1 Len=4: 78,0,0,0
N Typ=1 Len=1: 78
You could also issue an UPDATE to update the rows that aren't actually storing the value in the rows of the table
SQL> update foo
2 set c = 'N'
3 where c != 'N';
1 row updated.
SQL> select c, dump(c) from foo;
C DUMP(C)
---- ------------------------------
N Typ=1 Len=1: 78
N Typ=1 Len=1: 78
You tagged oracle11g, but you didn't specify a version.
This works for me on 11.2.0.2 on Linux x86-64.
SQL*Plus: Release 11.2.0.2.0 Production on Mon Mar 26 13:13:52 2012
Copyright (c) 1982, 2010, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management and OLAP options
SQL> create table tt(a number);
Table created.
SQL> insert into tt values (1);
1 row created.
SQL> commit;
Commit complete.
SQL> alter table tt add c char(1 char) default 'N' not null;
Table altered.
SQL> alter table tt add constraint chk check(c in('N','Y'));
Table altered.
SQL> select * from tt;
A C
---------- -
1 N
SQL> column dump(c) format a30
SQL> select c, length(c),dump(c) from tt;
C LENGTH(C) DUMP(C)
- ---------- ------------------------------
N 1 Typ=96 Len=1: 78
So....perhaps you have a bug in your version?
Hope that helps.
The reason for the ORA-02293 error, as you've already mentioned, is because it is inserting 'N ' (with a padded white space) rather than 'N'. So your constraint is violated.
The more interesting question is, why is it adding that space? Well, by definition, a CHAR is fixed width, where a VARCHAR is not. A CHAR will always pad white space to fill the entire memory space allocated for the column. Because you've chosen a width of 1 CHAR, and AL32UTF8 is a varying width character set, that would seem to conflict with the fixed width nature of CHAR. It looks like it gets padded to fill the extra byte(s) not used by the 'N'. Or, at least, I assume that is what is happening.

SQL Developer showing a weird data format in output

I tried to run a simple query which shows a weird date output format. When i try to run the below query, I get no value as output.
select value from SYS.nls_database_parameters where parameter = 'NLS_date_format';
please help.
See the difference? Your query first, mine second.
SQL> select value from SYS.nls_database_parameters where parameter = 'NLS_date_format';
no rows selected
SQL> select value from SYS.nls_database_parameters where parameter = 'NLS_DATE_FORMAT';
VALUE
--------------------------------------------------------------------------------
DD-MON-RR
SQL>
Parameters' names are UPPERCASE, e.g.
SQL> select parameter from nls_database_parameters where rownum < 5;
PARAMETER
------------------------------
NLS_LANGUAGE
NLS_TERRITORY
NLS_CURRENCY
NLS_ISO_CURRENCY
SQL>

Resources