Fetch data only if field contains any lower case in the string - sql

I was trying to fetch data with condition "get data only if string contains the lower char in the 4th and 5th position".
Below are the conditions:
and ascii(substr(RAP01.CRDVER,4,1)) between 97 and 122
and ascii(substr(RAP01.CRDVER,5,1)) between 97 and 122;
Table name RAP01 and column name CRDVER.
But It is not fetching all the required data.
Can this approach is correct?

How about this? The key is line 8 - query will return strings whose 4th and 5th character (substr(col, 4, 2))is a lowercase letter ([a-z]) (c means that the search is case sensitive).
SQL> with test (col) as
2 (select '12Bcfx23' from dual union all
3 select '123456' from dual union all
4 select 'ABCDEFGH' from dual
5 )
6 select col, substr(col, 4, 2) sub
7 from test
8 where regexp_like(substr(col, 4, 2), '[a-z]', 'c');
COL SU
-------- --
12Bcfx23 cf
SQL>

Are lower case a subset of ASCII? NO
Can I use ASCII to identify lower case? NO
How can I know if a string has lower case? It has lower case if UPPER of string is not the same as just the string.
Sample:
create table t1 ( a varchar(100) )\\
insert into t1 (a) values ( 'A' )\\
insert into t1 (a) values ( 'a' )\\
insert into t1 (a) values ( 'å' )\\
insert into t1 (a) values ( 'æ' )\\
select (case when a = upper( a ) <-- compare with upper
then a || ' Is Upper'
else a || ' Has lower' end) as r_upper,
(case when not regexp_like(a, '[a-z]', 'c') <-- using ascii
then a || ' Is Upper'
else a || ' Has lower' end) as r_reg
from t1\\
R_UPPER | R_REG
-------------------------
A Is Upper | A Is Upper
a Has lower | a Has lower
å Has lower | å Is Upper (¹)
æ Has lower | æ Is Upper (¹)
(¹) Wrong result using ASCII.

Related

Concatenating CLOBs and VARCHAR in Oracle SQL

I have a table (WORKLOG) in an Oracle SQL database which has a VARCHAR description field of 250 characters. On a separate table (LONGDESCRIPTION) there is a CLOB field which holds a long description. My goal is to build a query which returns the first 4000 characters of the concatenation of the short and long descriptions.
I initially tried this with SUBSTR functions, but that didn't work so well with the CLOB: it was overflowing the VARCHAR(4000) field I'd set up for it (ORA-64203: Destination buffer too small to hold CLOB data after character set conversion.). After searching on here I tried using DBMS_LOB.SUBSTR, but this didn't seem to work either: I got the same 64203, or ORA-22831: Offset or offset+amount does not land on character boundary. I have tried a few ways to fix it, using slightly different functions, but each runs into trouble, and I'm having no luck working out a way to avoid cutting a character but also ensuring I don't overflow the 4000 limit.
Here's the code I'm using. The idea is that it should use the short description if there is no long description, use a truncated long description if there is no short description or if the short description is the same as the start of the long description, and otherwise concatenate the two. The REGEXP functions are to try and remove HTML formatting.
SELECT
WORKLOG.WORKLOGID as Worklog,
WORKLOG.DESCRIPTION as Description,
CASE
WHEN WORKLOG.DESCRIPTION is null AND LENGTH(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>')) > 4000
THEN CAST(SUBSTR(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>'), 1, 3996) as VARCHAR(3996)) || '...'
WHEN WORKLOG.DESCRIPTION is null
THEN CAST(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>') as VARCHAR(4000))
WHEN LONGDESCRIPTION.LDKEY is null
THEN WORKLOG.DESCRIPTION
WHEN CAST(SUBSTR(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>'), 1, LENGTH(WORKLOG.DESCRIPTION)) as VARCHAR(4000)) = WORKLOG.DESCRIPTION AND LENGTH(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>')) > 4000
THEN CAST(SUBSTR(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>'), 1, 3996) as VARCHAR(3996)) || '...'
WHEN CAST(SUBSTR(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>'), 1, LENGTH(WORKLOG.DESCRIPTION)) as VARCHAR(4000)) = WORKLOG.DESCRIPTION
THEN CAST(SUBSTR(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>'), 1, 4000) as VARCHAR(4000))
WHEN LENGTH(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>')) > 3732
THEN WORKLOG.DESCRIPTION || ' // ' || CAST(SUBSTR(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>'), 1, 3732) as VARCHAR(3732)) || '...'
ELSE WORKLOG.DESCRIPTION || ' // ' || CAST(SUBSTR(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>'), 1, 3732) as VARCHAR(3735))
END as Description_Full
FROM
Maximo.WORKLOG
LEFT JOIN
Maximo.LONGDESCRIPTION
ON LONGDESCRIPTION.LDOWNERTABLE = 'WORKLOG'
AND LONGDESCRIPTION.LDOWNERCOL = 'DESCRIPTION'
AND LONGDESCRIPTION.LDKEY = WORKLOG.WORKLOGID
AND LENGTH(REGEXP_REPLACE(LONGDESCRIPTION.LDTEXT, '<.*?>')) > 0
Examples where the description field is 2 characters long and the maximum is 10:
Description
Long description
Expected result
ABC
ABC
ABCDEFGHIJKL
ABCDEFGHIJ
AB
AB
AB
ABCDE
ABCDE
AB
ABCDEFGHIJKL
ABCDEFGHIJ
AB
XY
AB // XY
AB
XYZABCDEF
AB // XYZA
Any help would be greatly appreciated.
Assuming that your CLOB contains well-formed XML or XHTML then you should use a proper XML parser (and not regular expressions) and can use:
SELECT w.WORKLOGID as Worklog,
w.DESCRIPTION as Description,
SUBSTR(
CASE
WHEN w.description IS NOT NULL AND xml.text LIKE w.description || '%'
THEN xml.text
WHEN w.description IS NOT NULL AND xml.text IS NOT NULL
THEN TO_CLOB(w.DESCRIPTION) || '//' || xml.text
WHEN w.description IS NOT NULL
THEN TO_CLOB(w.DESCRIPTION)
WHEN xml.text IS NOT NULL
THEN xml.text
END,
1,
4000
) AS Description_Full
FROM /*Maximo.*/WORKLOG w
LEFT JOIN (
/*Maximo.*/LONGDESCRIPTION ld
CROSS APPLY XMLTABLE(
'/HTML/BODY'
PASSING XMLTYPE(
COALESCE(ld.LONGDESCRIPTION, TO_CLOB('<HTML></HTML>') )
)
COLUMNS
text CLOB PATH '.'
) xml
)
ON ld.LDOWNERTABLE = 'WORKLOG'
AND ld.LDOWNERCOL = 'DESCRIPTION'
AND ld.LDKEY = w.WORKLOGID
Which, for the sample data:
CREATE TABLE worklog (
worklogid NUMBER,
description VARCHAR2(250)
);
CREATE TABLE longdescription (
LDOWNERTABLE VARCHAR2(30),
LDOWNERCOL VARCHAR2(30),
LDKEY NUMBER,
LONGDESCRIPTION CLOB
);
INSERT INTO worklog (worklogid, description)
SELECT 1, 'Value1' FROM DUAL UNION ALL
SELECT 2, 'Value2' FROM DUAL UNION ALL
SELECT 3, NULL FROM DUAL UNION ALL
SELECT 4, NULL FROM DUAL;
INSERT INTO longdescription
SELECT 'WORKLOG', 'DESCRIPTION', 1, '<HTML><BODY>Test</BODY></HTML>' FROM DUAL UNION ALL
SELECT 'WORKLOG', 'DESCRIPTION', 2, NULL FROM DUAL UNION ALL
SELECT 'WORKLOG', 'DESCRIPTION', 3, '<HTML><BODY>Test</BODY></HTML>' FROM DUAL UNION ALL
SELECT 'WORKLOG', 'DESCRIPTION', 4, NULL FROM DUAL;
Outputs:
WORKLOG
DESCRIPTION
DESCRIPTION_FULL
1
Value1
Value1//Test
3
null
Test
2
Value2
Value2
4
null
null
db<>fiddle here

Select only rows with each word beginning with capital letter being output

I am trying to output data from a SQL database for only values where each word begins with a capital letter. If I have the Products column with values of "Product One Apple" and "Product two banana", I would only want to output any values such as "Product One Apple". I am not wanting to update any data, just manipulate the data output. Is this possible without the use of a function, or will this require one?
If you are using SQL Server:-
Table Creation:
create table YourTable(id int identity(1,1),YourColumn varchar(100));
insert into YourTable(YourColumn)
select 'One Letter' Name union all
select 'another One' union all
select 'lower case' union all
select 'Upper case Sensitiive charecters'
Fetch Data:
with SPosition as
(select patindex('% %',YourColumn) SPosition, YourColumn from YourTable
union all
select SPosition+patindex('% %',substring(YourColumn, SPosition+1, len(YourColumn))) SPosition, YourColumn from SPosition
where patindex('% %',substring(YourColumn, SPosition+1, len(YourColumn)))>0
),SelectedWords as(
select substring(YourColumn,SPosition+1,1) StartingPosition,Case When substring(YourColumn,SPosition+1,1) Collate Latin1_General_CS_AI=upper(substring(YourColumn,SPosition+1,1))
then isnull(nullif(ltrim(rtrim(substring(YourColumn,SPosition+1,patindex('% %',substring(YourColumn, SPosition+1, len(YourColumn)))))),''),
substring(YourColumn,SPosition+1,len(YourColumn)))
else null end Words,
SPosition,YourColumn from SPosition
union all
select substring(YourColumn,1,1),Case When substring(YourColumn,1,1) Collate Latin1_General_CS_AI=upper(substring(YourColumn,1,1))
then isnull(nullif(ltrim(rtrim(substring(YourColumn,1,patindex('% %',substring(YourColumn, 1, len(YourColumn)))))),''),
substring(YourColumn,1,len(YourColumn)))
else null end,
1,YourColumn from YourTable
)
select YourColumn,String_Agg(Words,',') from SelectedWords where Words is not null
group by YourColumn
You can run this code by Click Here
yes you can do with following query
Select CONCAT(UPPER(SUBSTRING(colunm_name,1,1)),LOWER(SUBSTRING(column_name,2))) AS Name from table_name;
You can get the ASCII representation number of the first letter, and validate the range between 65 and 90, which are the position that upper letters have in the ASCII table (ex. "#" represents the 64th position):
select d.Name, LEFT(d.Name, 1),ASCII(LEFT(d.Name, 1))
from (
select 'One Letter' Name union all
select 'another One' union all
select 'lower case' union all
select 'Upper'
)d
where ASCII(LEFT(d.Name, 1)) between 65 and 90 --Here the validation
If you want to review the ASCII table (decimal position), check this link: ASCII Table

how to replace a value for string if letters are missing?

I ran into a problem where i have to create a 'LettersOfName' column. As name suggest I have to get letter 2,3 and 5 from ORGANISATIONNAME column and letters 2 and 3 from CLIENTLASTNAME column, then concatenate to form letters of name column. The condition is if letters of name is not equal to length 5 than replace with '22222' also if any of the letters is missing from first name and last name than replace with '22222'. I am using this query.
select
( CASE WHEN LENGTH (UPPER( SUBSTR(ORGANISATIONNAME, 2,2) || SUBSTR(ORGANISATIONNAME,5,1)) || UPPER(SUBSTR(CLIENTLASTNAME,2,2))) != '5' THEN '22222'
ELSE UPPER( SUBSTR(ORGANISATIONNAME, 2,2) || SUBSTR(ORGANISATIONNAME,5,1)) || UPPER(SUBSTR(CLIENTLASTNAME,2,2)) END)
AS LETTERSOFNAME
from client;
So, far this query runs fine, but when we have name like 'Jo Anne' or 'J Shark' it is missing letter '2' and '3' but does not replace the string with '22222'. When length is not equal to 5 it replaces with '22222'. I am using Oracle 12c.
If after the concatenations of the letters you remove all the spaces and the length of the remaining string is less than 5 then replace with '22222':
SELECT
CASE
WHEN LENGTH(REPLACE(SUBSTR(ORGANISATIONNAME, 2, 2) || SUBSTR(ORGANISATIONNAME, 5, 1) || SUBSTR(CLIENTLASTNAME, 2, 2), ' ', '')) < 5 THEN '22222'
ELSE UPPER(SUBSTR(ORGANISATIONNAME, 2, 2) || SUBSTR(ORGANISATIONNAME, 5, 1) || SUBSTR(CLIENTLASTNAME, 2, 2))
END LETTERSOFNAME
FROM client
Or with a CTE:
WITH cte AS (
SELECT
UPPER(REPLACE(
SUBSTR(ORGANISATIONNAME, 2, 2) ||
SUBSTR(ORGANISATIONNAME, 5, 1) ||
SUBSTR(CLIENTLASTNAME, 2, 2),
' ',
''
)) LETTERSOFNAME
FROM client
)
SELECT
CASE
WHEN LENGTH(LETTERSOFNAME) < 5 THEN '22222'
ELSE LETTERSOFNAME
END LETTERSOFNAME
FROM cte
See the demo.
You should first remove the white space between the string and and then apply your case statement on it
replace ('J Shark', ' ', '')
Reason is white space is being counted as a character in J Shark and that is why second and third characters are missing.
Here is an example demo.
Here is my approach:
Put both columns ORGANISATIONNAME and CLIENTLASTNAME to another table with identity column (to identify each row)
Write a function to split text by a string (in this case pass a space)
Get the identity and the splitted data to 2 tables each for column 1 and 2
Consider each table and apply your logic
Concatenate the row values separated by space, with the ID (1 record per ID)
Join the 2 tables (by IDs)
Join the 2 tables for matches in Col-Split data, and get the IDs
Now Query for the data in table in above 1

SQL to find upper case words from a column

I have a description column in my table and its values are:
This is a EXAMPLE
This is a TEST
This is a VALUE
I want to display only EXAMPLE, TEST, and VALUE from the description column.
How do I achieve this?
This could be a way:
-- a test case
with test(id, str) as (
select 1, 'This is a EXAMPLE' from dual union all
select 2, 'This is a TEST' from dual union all
select 3, 'This is a VALUE' from dual union all
select 4, 'This IS aN EXAMPLE' from dual
)
-- concatenate the resulting words
select id, listagg(str, ' ') within group (order by pos)
from (
-- tokenize the strings by using the space as a word separator
SELECT id,
trim(regexp_substr(str, '[^ ]+', 1, level)) str,
level as pos
FROM test t
CONNECT BY instr(str, ' ', 1, level - 1) > 0
and prior id = id
and prior sys_guid() is not null
)
-- only get the uppercase words
where regexp_like(str, '^[A-Z]+$')
group by id
The idea is to tokenize every string, then cut off the words that are not made by upper case characters and then concatenate the remaining words.
The result:
1 EXAMPLE
2 TEST
3 VALUE
4 IS EXAMPLE
If you need to handle some other character as an upper case letter, you may edit the where condition to filter for the matching words; for example, with '_':
with test(id, str) as (
select 1, 'This is a EXAMPLE' from dual union all
select 2, 'This is a TEST' from dual union all
select 3, 'This is a VALUE' from dual union all
select 4, 'This IS aN EXAMPLE' from dual union all
select 5, 'This IS AN_EXAMPLE' from dual
)
select id, listagg(str, ' ') within group (order by pos)
from (
SELECT id,
trim(regexp_substr(str, '[^ ]+', 1, level)) str,
level as pos
FROM test t
CONNECT BY instr(str, ' ', 1, level - 1) > 0
and prior id = id
and prior sys_guid() is not null
)
where regexp_like(str, '^[A-Z_]+$')
group by id
gives:
1 EXAMPLE
2 TEST
3 VALUE
4 IS EXAMPLE
5 IS AN_EXAMPLE
Here's another solution. It was inspired by Aleksej's answer.
The idea? Get all the words. Then aggregate only fully uppercased to a list.
Sample data:
create table descriptions (ID int, Description varchar2(100));
insert into descriptions (ID, Description)
select 1 as ID, 'foo Foo FOO bar Bar BAR' as Description from dual
union all select 2, 'This is an EXAMPLE TEST Description VALUE' from dual
;
Query:
select id, Description, listagg(word, ',') within group (order by pos) as UpperCaseWords
from (
select
id, Description,
trim(regexp_substr(Description, '\w+', 1, level)) as word,
level as pos
from descriptions t
connect by regexp_instr(Description, '\s+', 1, level - 1) > 0
and prior id = id
and prior sys_guid() is not null
)
where word = upper(word)
group by id, Description
Result:
ID | DESCRIPTION | UPPERCASEWORDS
-- | ----------------------------------------- | ------------------
1 | foo Foo FOO bar Bar BAR | FOO,BAR
2 | This is an EXAMPLE TEST Description VALUE | EXAMPLE,TEST,VALUE
It is possible to achieve this thanks to the REGEXP_REPLACE function:
SELECT REGEXP_REPLACE(my_column, '(^[A-Z]| |[a-z][A-Z]*|[A-Z]*[a-z])', '') AS Result FROM my_table
It uses a regex which replaces first upper case char of the line and converts every lower case char and space with blanks.
Try this:
SELECT SUBSTR(column_name, INSTR(column_name,' ',-1) + 1)
FROM your_table;
This should do the trick:
SELECT SUBSTR(REGEXP_REPLACE(' ' || REGEXP_REPLACE(description, '(^[A-Z]|[a-z]|[A-Z][a-z]+|[,])', ''), ' +', ' '), 2, 9999) AS only_upper
FROM (
select 'Hey IF you do not know IT, This IS a test of UPPERCASE and IT, with good WILL and faith, Should BE fine to be SHOWN' description
from dual
)
I have added condition to strip commas, you can add inside that brakets other special characters to remove.
ONLY_UPPER
-----------------------------------
IF IT IS UPPERCASE IT WILL BE SHOWN
This is a function based on some of the regular expression answers.
create or replace function capwords(orig_string varchar2)
return varchar2
as
out_string varchar2(80);
begin
out_string := REGEXP_REPLACE(orig_string, '([a-z][A-Z_]*|[A-Z_]*[a-z])', '');
out_string := REGEXP_REPLACE(trim(out_string), '( *)', ' ');
return out_string;
end;
/
Removes strings of upper case letters and underscores that have lower case letters
on either end. Replaces multiple adjacent spaces with one space.
Trims extra spaces off of the ends. Assumes max size of 80 characters.
Slightly edited output:
>select id,str,capwords(str) from test;
ID STR CAPWORDS(STR)
---------- ------------------------------ ------------------
1 This is a EXAMPLE EXAMPLE
2 This is a TEST TEST
3 This is a VALUE VALUE
4 This IS aN EXAMPLE IS EXAMPLE
5 This is WITH_UNDERSCORE WITH_UNDERSCORE
6 ThiS IS aN EXAMPLE IS EXAMPLE
7 thiS IS aN EXAMPLE IS EXAMPLE
8 This IS wiTH_UNDERSCORE IS
If you only need to "display" the result without changing the values in the column then you can use CASE WHEN (in the example Description is the column name):
Select CASE WHEN Description like '%EXAMPLE%' then 'EXAMPLE' WHEN Description like '%TEST%' then 'TEST' WHEN Description like '%VALUE%' then 'VALUE' END From [yourTable]
The conditions are not case sensitive even if you write it all in uppercase.
You can add Else '<Value if all conditions are wrong>' before the END in case there are descriptions that don't contain any of the values. The example will return NULL for those cases, and writing ELSE Description will return the original value of that row.
It also works if you need to update. It is simple and practical, easy way out, haha.

Oracle NVL with empty string

I have this table where NULL is the NULL value, not the string NULL:
MYCOL
--------
NULL
example
Why does this query not return the NULL row?
select * from example_so where nvl(mycol, '') = '';
'' is again NULL in Oracle, because Oracle doesn't support empty Strings just like Other High Level languages or DBMS..
You need to look for NULL/empty string using IS NULL or IS NOT NULL
No other relational operator work against NULL, though it is syntactically valid. SQLFiddle Demo
It has to be,
select * from example_so where mycol IS NULL
EDIT: As per Docs
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.
Because NULL = NULL is simply unknown. A third state perhaps? It is neither TRUE nor FALSE.
Oracle considers an EMPTY STRING as NULL.
nvl(mycol, '') makes no real sense, as you are making the NULL back to NULL and comparing it again with NULL.
SQL> WITH DATA AS(
2 SELECT 1 val, 'NULL' str FROM dual UNION ALL
3 SELECT 2, NULL str FROM dual UNION ALL
4 SELECT 3, '' str FROM dual UNION ALL
5 SELECT 4, 'some value' str FROM dual)
6 SELECT val, NVL(str, 'THIS IS NULL') FROM data WHERE str IS NULL
7 /
VAL NVL(STR,'THI
---------- ------------
2 THIS IS NULL
3 THIS IS NULL
SQL>
select * from example_so where nvl(mycol, '') = '';
nvl(mycol, '') will be resulted as NULL
and when you compared NULL with empty string it cant be compared
create table t(id varchar2(2));
insert into t values (nvl(null,'')); <------ this will insert NULL
insert into t values (nvl(null,'et'));