Merging tags to values separated by new line character in Oracle SQL - sql

I have a database field with several values separated by newline.
Eg-(can be more than 3 also)
A
B
C
I want to perform an operation to modify these values by adding tags from front and end.
i.e the previous 3 values should need to be turned into
<Test>A</Test>
<Test>B</Test>
<Test>C</Test>
Is there any possible query operation in Oracle SQL to perform such an operation?

Just replace the start and end of each string with the XML tags using a multi-line match parameter of the regular expression:
SELECT REGEXP_REPLACE(
REGEXP_REPLACE( value, '^', '<Test>', 1, 0, 'm' ),
'$', '</Test>', 1, 0, 'm'
) AS replaced_value
FROM table_name;
Which, for the sample data:
CREATE TABLE table_name ( value ) AS
SELECT 'A
B
C' FROM DUAL;
Outputs:
| REPLACED_VALUE |
| :------------- |
| <Test>A</Test> |
| <Test>B</Test> |
| <Test>C</Test> |
db<>fiddle here

You can use normal replace function as follows:
Select '<test>'
|| replace(your_column,chr(10),'</test>'||chr(10)||'<test>')
|| '</test>'
From your_table;
It will be faster than its regexp_replace function.
Db<>fiddle

Related

PostgreSQL - Extract string before ending delimiter

I have a column of data that looks like this:
58,0:102,56.00
52,0:58,68
58,110
57,440.00
52,0:58,0:106,6105.95
I need to extract the character before the last delimiter (',').
Using the data above, I want to get:
102
58
58
57
106
Might be done with a regular expression in substring(). If you want:
the longest string of only digits before the last comma:
substring(data, '(\d+)\,[^,]*$')
Or you may want:
the string before the last comma (',') that's delimited at the start either by a colon (':') or the start of the string.
Could be another regexp:
substring(data, '([^:]*)\,[^,]*$')
Or this:
reverse(split_part(split_part(reverse(data), ',', 2), ':', 1))
More verbose but typically much faster than a (expensive) regular expression.
db<>fiddle here
Can't promise this is the best way to do it, but it is a way to do it:
with splits as (
select string_to_array(bar, ',') as bar_array
from foo
),
second_to_last as (
select
bar_array[cardinality(bar_array)-1] as field
from splits
)
select
field,
case
when field like '%:%' then split_part (field, ':', 2)
else field
end as last_item
from second_to_last
I went a little overkill on the CTEs, but that was to expose the logic a little better.
With a CTE that removes everything after the last comma and then splits the rest into an array:
with cte as (
select
regexp_split_to_array(
replace(left(col, length(col) - position(',' in reverse(col))), ':', ','),
','
) arr
from tablename
)
select arr[array_upper(arr, 1)] from cte
See the demo.
Results:
| result |
| ------ |
| 102 |
| 58 |
| 58 |
| 57 |
| 106 |
The following treats the source string as an "array of arrays". It seems each data element can be defined as S(x,y) and the overall string as S1:S2:...Sn.
The task then becomes to extract x from Sn.
with as_array as
( select string_to_array(S[n], ',') Sn
from (select string_to_array(col,':') S
, length(regexp_replace(col, '[^:]','','g'))+1 n
from tablename
) t
)
select Sn[array_length(Sn,1)-1] from as_array
The above extends S(x,y) to S(a,b,...,x,y) the task remains to extracting x from Sn. If it is the case that all original sub-strings S are formatted S(x,y) then the last select reduces to select Sn[1]

Convert array to rows in Postgres

If I have something like this in SQL statement ('A','B','C'), how do I convert it into a column with multiple rows like this
col
---
A
B
C
I cannot change the way that string is created (as it is injected into SQL query from external program). For example, I cannot make it as ['A','B','C'] (replace with square brackets). I could wrap anything around it though like [('A','B','C')] or whatever.
Any help?
UPDATE 1
I have PostgreSQL 8.4.20
You could create an ARRAY from VALUES and then unnest it:
SELECT
unnest(ARRAY[col_a, col_b, col_c])
FROM
(VALUES('A','B','C')) AS x(col_a, col_b, col_c)
Result:
| unnest |
|--------|
| A |
| B |
| C |
Edit: you could also tweak jspcal's answer by using dollar quotes ($$) like this so you can concatenate your string into the SQL statement:
SELECT * FROM regexp_split_to_table(
regexp_replace(
$$('A','B','C','D','foo')$$,
'^\(''|''\)+', '', 'g'),
''','''
);
The built-in regexp_split_to_table function will do this for you. Since you plan to inject it directly without escaping, use $$ (dollar quoting) from thibautg's answer.
select * from regexp_split_to_table(
regexp_replace($$('A','B','C')$$, '^\(''|''\)+', '', 'g'),
''','''
);

SQL: How to divide text from a row in different columns?

If anyone can give me a hint how to get data from a row with text in different columns. I will have an example below.
Much appreciated
I have a column called TEXT that contains:
TEXT
<FROM> USER_SCHEMA1.T_POSTAL_CODES
<FROM> USER_SCHEMA2.T_USER_NAMES
<FROM> USER_SCHEMA3.T_LOCATIONS
Desired result: TWO DIFFERENT COLUMNS
SCHEMA_NAME TABLE_NAME
USER_SCHEMA1 T_POSTAL_CODES
USER_SCHEMA2 T_USER_NAMES
USER_SCHEMA3 T_LOCATIONS
How to translate this into sql?
This is what i need from ALL_SOURCE, but column TEXT to put it in two columns one SCHEMA_NAME and one TABLE_NAME.
select * from ALL_SOURCE S
where S.OWNER_NAME like 'FINANCE_SCHEMA%' -- in order to be on the right schema
and S.TEXT like '<FROM%'; -- what to use next?
Thank you for you help
You can leverage PARSENAME for this pretty easily. I would really recommend you stop using reserved words as column names. It makes code a lot more challenging than it needs to be.
with MyTextTable as
(
select MyText = '<FROM> USER_SCHEMA1.T_POSTAL_CODES' union all
select '<FROM> USER_SCHEMA2.T_USER_NAMES' union all
select '<FROM> USER_SCHEMA3.T_LOCATIONS'
)
select *
, [SCHEMA_NAME] = parsename(REPLACE(MyText, '<FROM> ', ''), 2)
, TABLE_NAME = parsename(REPLACE(MyText, '<FROM> ', ''), 1)
from MyTextTable
using substring() and stuff() with charindex() to find the location of the first period and/or space in the string.
select
schema_name = substring([text]
, charindex(' ',[text])+1
, charindex('.',[text])-(charindex(' ',[text])+1)
)
, table_name = stuff([text],1,charindex('.',[text]),'')
from t
rextester demo: http://rextester.com/EEMU6399
returns:
+--------------+----------------+
| schema_name | table_name |
+--------------+----------------+
| USER_SCHEMA1 | T_POSTAL_CODES |
| USER_SCHEMA2 | T_USER_NAMES |
| USER_SCHEMA3 | T_LOCATIONS |
+--------------+----------------+

TO_CHAR function logic <<Number format>>

I am new to oracle and found something hard to understand. Even though i understand the functionality of TO_CHAR , i am new to concept of number format model. Please help me understand the below logic.
select TRIM(substr(TO_CHAR (160, '000'),1,3)) from dual;
Output -> 16
select TRIM(substr(TO_CHAR (160),1,3)) from dual;
Output -> 160
For the 1st query why has the oracle returned the value 16 rather than
160?
Start the format string with FM, e.g. 'FM000'.
Without the FM there is a preceding space saved for a minus sign.
select '|' || to_char(160,'000') || '|' as no_FM
,'|' || to_char(160,'FM000') || '|' as with_FM
from dual
+--------+---------+
| NO_FM | WITH_FM |
+--------+---------+
| | 160| | |160| |
+--------+---------+
Interesting. Tried this:
select to_char(160, '000')
,substr(to_char(160, '000'), 1, 3)
,substr('160', 1, 3)
,length(to_char(160, '000'))
from dual;
Which gave me 160, 16 , 160, 4
The substring gives you the first 3 characters of the string. The fist character of to_char(160,'000') is a space. A place is reserved for a minus sign.
So the value of substr(to_char(160, '000'), 1, 3) is not 16 but space16.
if you try like this
select substr(TRIM(TO_CHAR (160, '000')),1,3) from dual;
Output -> 160
instead of this:
select TRIM(substr(TO_CHAR (160, '000'),1,3)) from dual;

show value from text..if delimited by ; show only first value

I have table with values. It is ntext because of ; delimited. Values can be empty, 1 number and numbers delimited by semicolon (as shown)
+-----------+
| room |
+-----------+
| 64 |
+-----------+
| 60008 |
+-----------+
| |
+-----------+
| 127;50047 |
+-----------+
I have this code. Substring is looking for ; and show first value. It is working only where the values are delimited. So how can I change it, that it will show first value when ; and single value also. So from table bellow I will get 64,60008, ,127.
SELECT
T0.U_Scid as 'id',
T3.U_Boarding as 'start',
T3.U_Boarding as 'end',
SUBSTRING(T5.U_Partner, 0, CHARINDEX(';', T5.U_Partner)) AS 'room_id',
CASE WHEN datalength(T5.U_Partner)=0 THEN '9999' ELSE T5.U_Partner END AS 'room_id' ,
CASE WHEN datalength(T5.U_Partner) > 4 THEN T5.U_Partner ELSE '9999' END AS 'partners_id' ,
This is just bonus question. CASE are looking for length of value, if the value is longer than 4 ( 600008 ) write to room_id 9999 and save 600008 to partners_id. If it is empty write 9999 to room_id.
How to make it works together?.so getting value from T_Partner..save it into temporary table T1.TempRoom ( I suppose ).. so T1.TempRoom (is filled with numbers like 64, ,60008,127) then CASE is checking T1.TempRoom for values and save it into room_id and partners_id.
Am I right?
Here is a simple method:
SUBSTRING(T5.U_Partner, 1, CHARINDEX(';', T5.U_Partner + ';')) AS room_id,
That is, concatenate the semicolon to the argument for CHARINDEX(). That will prevent any error occurring.
In addition, indexing for SUBSTRING() starts at 1, not 0.
And, don't use single quotes for column names. Only use them for string and date literals.
EDIT:
You can always use the verbose form:
(CASE WHEN T5.U_Partner LIKE '%;%'
THEN SUBSTRING(T5.U_Partner, 1, CHARINDEX(';', T5.U_Partner + ';'))
ELSE T5.U_Partner
END) AS room_id