Related
I have a large string stored in table as a single line. I need a select query to split the large string to rows after every 100 characters and it should not split in middle of the word. Basically, the query should find a space after 100 characters and split into new line.
I have used this query, it is splitting after 100 lines, but it is breaking in the middle of words.
SELECT REGEXP_REPLACE ( col_large_string , '(.{100})' , '\1' || CHR (10) ) AS split_to_rows
FROM tab_large_string where string_id = 1;
You do not need (slow) regular expressions and can do it with simple (quicker) string functions.
If you want to replace spaces with newlines then:
WITH bounds ( str, end_pos ) AS (
SELECT col_large_string,
INSTR(col_large_string, ' ', 101)
FROM tab_large_string
UNION ALL
SELECT SUBSTR(str, 1, end_pos - 1)
|| CHR(10)
|| SUBSTR(str, end_pos + 1),
INSTR(str, ' ', end_pos + 101)
FROM bounds
WHERE end_pos > 0
)
SELECT str AS split_to_lines
FROM bounds
WHERE end_pos = 0;
and if you want to have each line in a new row then:
WITH bounds ( str, start_pos, end_pos ) AS (
SELECT col_large_string,
1,
INSTR(col_large_string, ' ', 101)
FROM tab_large_string
UNION ALL
SELECT str,
end_pos + 1,
INSTR(str, ' ', end_pos + 101)
FROM bounds
WHERE end_pos > 0
)
SELECT CASE end_pos
WHEN 0
THEN SUBSTR(str, start_pos)
ELSE SUBSTR(str, start_pos, end_pos - start_pos)
END AS split_to_rows
FROM bounds;
If you do want to use regular expressions then:
SELECT REGEXP_REPLACE(
col_large_string,
'(.{100,}?) ',
'\1' || CHR (10)
) AS split_to_lines
FROM tab_large_string
WHERE string_id = 1;
db<>fiddle here
You can use this regular expression:
SELECT REGEXP_REPLACE ( col_large_string , '((\w+\s+){100})' , '\1' || CHR (10) ) AS split_to_rows
FROM tab_large_string where string_id = 1;
\w+ matches one or more occurrence of word character.
\s+ matches one or more occurrence of space character.
(\w+\s+) matches a word followed by space
(\w+\s+){100} then matches (a word followed by space) x100.
All -
I'm looking at a large volume of sql query history data. Ultimately I need to create a distinct list of tables used by each from a list of their executed queries. Let's say my simplified table for this example is:
create table zwork_example (
username varchar2(50),
sql_text clob);
insert into zwork_example (username, sql_text)
values ('user1', 'schema1.table1, schema1.table2, schema2.table1, schema1.table1');
insert into zwork_example (username, sql_text)
Values ('user2', 'schema1.table3, schema1.table2, schema2.table1, schema1.table6');
Does anyone have any ideas on how I can search for schema1* and return N number of table names that belong to schema1? In this example I have a particular schema I'm interested in, so I can explicitly state that schema1 is the only schema I'm interested in returning the table names from.
The output I would look for given this example is:
User Schema Tables
-------- -------- --------
User1 schema1 table1, table2
User2 schema1 table3, table2, table6
CLOB, large data set ... doesn't sound promising. Read: it'll probably take some time to get the result. For such a small data set, see if this helps.
SQL> with
2 search_for (schema) as
3 (select 'schema1' from dual),
4 temp as
5 (select distinct
6 username,
7 trim(regexp_substr(dbms_lob.substr(sql_text, 32767), '[^,]+', 1, column_value)) col
8 from zwork_example cross join
9 table(cast(multiset(select level from dual
10 connect by level <= regexp_count(sql_text, ',') + 1
11 ) as sys.odcinumberlist))
12 )
13 select
14 t.username,
15 s.schema,
16 listagg(trim(replace(t.col, s.schema ||'.', null)), ', ') within group (order by null) tables
17 from temp t join search_for s on instr(t.col, s.schema) > 0
18 group by t.username, s.schema;
USERNAME SCHEMA TABLES
---------- ------- --------------------------------------------------
user1 schema1 table1, table2
user2 schema1 table2, table3, table6
SQL>
What does it do?
search_for CTE contains a schema you're searching for (schema1, right?)
temp CTE splits the sql_text into rows
moreover, in order to get distinct list of tables, I applied dbms_lob.substr to the column, hoping that you don't actually have strings longer than that (32767, that is)
final query just aggregates tables it found - rows where schemas match
For a large dataset you can use:
WITH search_string ( schema_name ) AS (
SELECT 'schema1' FROM DUAL
),
matches ( username, sql_text, schema_name, end_pos, matches ) AS (
SELECT username,
sql_text,
schema_name,
CASE
WHEN sql_text LIKE schema_name || '%'
THEN INSTR( sql_text, ',' )
WHEN sql_text NOT LIKE ', ' || schema_name || '.'
THEN 0
ELSE INSTR(
sql_text,
', ' || schema_name || '.',
INSTR( sql_text, ', ' || schema_name || '.' ) + 3
)
END,
CASE
WHEN sql_text LIKE schema_name || '%'
THEN EMPTY_CLOB() || SUBSTR(
sql_text,
LENGTH(schema_name || '.') + 1,
INSTR( sql_text, ',' ) - LENGTH(schema_name || '.') - 1
)
WHEN INSTR( sql_text, ', ' || schema_name || '.' ) = 0
THEN NULL
WHEN INSTR( sql_text, ',', INSTR( sql_text, ', ' || schema_name || '.' ) + 3 ) = 0
THEN EMPTY_CLOB() || SUBSTR(
sql_text,
INSTR( sql_text, ', ' || schema_name || '.' )
+ LENGTH(', ' || schema_name || '.')
)
ELSE EMPTY_CLOB() || SUBSTR(
sql_text,
INSTR( sql_text, ', ' || schema_name || '.' )
+ LENGTH(', ' || schema_name || '.'),
INSTR( sql_text, ',', INSTR( sql_text, ', ' || schema_name || '.' ) + 3 )
- INSTR( sql_text, ', ' || schema_name || '.' )
- LENGTH(', ' || schema_name || '.')
)
END
FROM zwork_example
CROSS JOIN search_string
UNION ALL
SELECT username,
sql_text,
schema_name,
INSTR(
sql_text,
', ' || schema_name || '.',
end_pos + 3
),
matches
|| ', '
|| CASE
WHEN INSTR( sql_text, ',', end_pos + 1 ) = 0
THEN SUBSTR(
sql_text,
end_pos + LENGTH(', ' || schema_name || '.')
)
ELSE SUBSTR(
sql_text,
end_pos + LENGTH(', ' || schema_name || '.'),
INSTR( sql_text, ',', end_pos + 1 )
- end_pos - LENGTH(', ' || schema_name || '.')
)
END
FROM matches
WHERE end_pos > 0
)
SELECT username,
matches
FROM matches
WHERE end_pos = 0;
Which, for the sample data:
create table zwork_example (
username varchar2(50),
sql_text clob
);
DECLARE
v_text CLOB;
BEGIN
insert into zwork_example (username, sql_text)
values ('user1', 'schema1.table1, schema1.table2, schema2.table1, schema1.table1');
insert into zwork_example (username, sql_text)
Values ('user2', 'schema1.table3, schema1.table2, schema2.table1, schema1.table6');
insert into zwork_example (username, sql_text)
Values ('user3', 'schema2.table3, schema3.table2, schema4.table1, schema2.table6');
insert into zwork_example (username, sql_text)
Values ('user4', 'schema2.table3, schema3.table2, schema4.table1, schema1.table6');
v_text := 'schema1.table1';
FOR i IN 2 .. 250 LOOP
v_text := v_text || ', schema1.table' || i;
END LOOP;
insert into zwork_example (username, sql_text) values ( 'user5', v_text );
END;
/
Outputs:
USERNAME
MATCHES
user3
user4
table6
user1
table1, table2, table1
user2
table3, table2, table6
user5
table1, table2, table3, table4, table5, table6, table7, table8, table9, table10, table11, table12, table13, table14, table15, table16, table17, table18, table19, table20, table21, table22, table23, table24, table25, table26, table27, table28, table29, table30, table31, table32, table33, table34, table35, table36, table37, table38, table39, table40, table41, table42, table43, table44, table45, table46, table47, table48, table49, table50, table51, table52, table53, table54, table55, table56, table57, table58, table59, table60, table61, table62, table63, table64, table65, table66, table67, table68, table69, table70, table71, table72, table73, table74, table75, table76, table77, table78, table79, table80, table81, table82, table83, table84, table85, table86, table87, table88, table89, table90, table91, table92, table93, table94, table95, table96, table97, table98, table99, table100, table101, table102, table103, table104, table105, table106, table107, table108, table109, table110, table111, table112, table113, table114, table115, table116, table117, table118, table119, table120, table121, table122, table123, table124, table125, table126, table127, table128, table129, table130, table131, table132, table133, table134, table135, table136, table137, table138, table139, table140, table141, table142, table143, table144, table145, table146, table147, table148, table149, table150, table151, table152, table153, table154, table155, table156, table157, table158, table159, table160, table161, table162, table163, table164, table165, table166, table167, table168, table169, table170, table171, table172, table173, table174, table175, table176, table177, table178, table179, table180, table181, table182, table183, table184, table185, table186, table187, table188, table189, table190, table191, table192, table193, table194, table195, table196, table197, table198, table199, table200, table201, table202, table203, table204, table205, table206, table207, table208, table209, table210, table211, table212, table213, table214, table215, table216, table217, table218, table219, table220, table221, table222, table223, table224, table225, table226, table227, table228, table229, table230, table231, table232, table233, table234, table235, table236, table237, table238, table239, table240, table241, table242, table243, table244, table245, table246, table247, table248, table249, table250
db<>fiddle here
I created a query and it's properly working.. but I'm not yet satisfied because my code is too long, is there way that I can simplify or shorten my select statement ?
select
/*GenInfo*/
id ,name name,
replace(regexp_substr(properties, 'EntityID=[^;]*'), 'EntityID=', '') as EntityID,
replace(regexp_substr(properties, 'deployed=[^;]*'), 'deployed=', '') as deployed,
replace(regexp_substr(properties, 'type=[^;]*'), 'type=', '') as type,
replace(regexp_substr(properties, 'level=[^;]*'), 'level=', '') as "LEVEL",
replace(regexp_substr(properties, 'description=[^;]*'), 'description=', '') as description,
replace(regexp_substr(properties, 'indicator=[^;]*'), 'indicator=', '') as indicator,
replace(regexp_substr(properties, 'Agreement=[^;]*'), 'Agreement=', '') as Agreement,
replace(regexp_substr(properties, 'Activation date to charge=[^;]*'), 'Activation date to charge=', '') as Activationdatetocharge,
replace(regexp_substr(properties, 'id=[^;]*'), 'id=', '') as id,
replace(regexp_substr(properties, 'name=[^;]*'), 'name=', '') as name,
replace(regexp_substr(properties, 'currencyCode=[^;]*'), 'currencyCode=', '') as currencyCode,
replace(regexp_substr(properties, 'saleExpirationDate=[^;]*'), 'saleExpirationDate=', '') as saleExpirationDate,
replace(regexp_substr(properties, 'Product type=[^;]*'), 'Product type=', '') as Producttype,
replace(regexp_substr(properties, 'saleEffectiveDate=[^;]*'), 'saleEffectiveDate=', '') as saleEffectiveDate,
replace(regexp_substr(properties, 'Deactivation date to charge=[^;]*'), 'Deactivation date to charge=', '') as Deactivationdatetocharge
.
.
.
.
.
.
from OFFER
where name = 'PLAN 599'
;
You have to double-split and generate a query:
Split Row into "ColName=Value"-fields
Split fields into two seperate vars
Create a from-dual-query
DECLARE
inputstring VARCHAR2 (2000) := 'EntityID=1;deployed=2018-01-01;type=app';
myquery VARCHAR2 (2000) := 'SELECT'; -- result-var
tmpValue VARCHAR2 (2000);
tmpName VARCHAR2 (2000);
BEGIN
FOR i IN
(
SELECT TRIM (REGEXP_SUBSTR (inputstring, -- Split input and loop through
'[^;]+',
1,
LEVEL))
l
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT (inputstring, ';') + 1
)
LOOP
tmpName := REGEXP_SUBSTR (i.l, '[^=]+', 1, 1); -- Split column into value and name
tmpValue := REGEXP_SUBSTR (i.l, '[^=]+', 1, 2);
myquery := myquery || ' ''' || tmpValue || ''' as ' || tmpName || ','; -- build some query
END LOOP;
myQuery := SUBSTR (myQuery, 0, LENGTH (myQuery) - 1) || ' FROM DUAL'; -- complete query
DBMS_OUTPUT.put_line (myQuery); --output result
-- Result: SELECT '1' as EntityID, '2018-01-01' as deployed, 'app' as type FROM DUAL
END;
Anyway you'll get a problem with this query if you want to read out the values by code.
Perhaps you could tell us, what you want to do with the data.
I hope, you only want to convert some data from a file. If so you can add another split-loop to split rows.
Example-Function
CREATE OR REPLACE FUNCTION MakeSQL (inputstring VARCHAR2)
RETURN VARCHAR2
IS
myquery VARCHAR2 (2000); -- result-var
tmpValue VARCHAR2 (2000);
tmpName VARCHAR2 (2000);
BEGIN
FOR x IN ( SELECT TRIM (REGEXP_SUBSTR (inputstring, -- Split input and loop through
'[^' || CHR (10) || ']+',
1,
LEVEL))
tmpRow
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT (inputstring, CHR (10)) + 1)
LOOP
myquery := myquery || 'SELECT';
FOR i IN ( SELECT TRIM (REGEXP_SUBSTR (x.tmpRow, -- Split input and loop through
'[^;]+',
1,
LEVEL))
l
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT (x.tmpRow, ';') + 1)
LOOP
tmpName :=
REGEXP_SUBSTR (i.l,
'[^=]+',
1,
1); -- Split column into value and name
tmpValue :=
REGEXP_SUBSTR (i.l,
'[^=]+',
1,
2);
myquery :=
myquery || ' ''' || tmpValue || ''' as ' || tmpName || ','; -- build some query
END LOOP;
myQuery :=
SUBSTR (myQuery, 0, LENGTH (myQuery) - 1)
|| ' FROM DUAL UNION ALL'
|| CHR (10); -- complete row-select
END LOOP;
myQuery := SUBSTR (myQuery, 0, LENGTH (myQuery) - 11); -- complete query
DBMS_OUTPUT.put_line (myQuery); --output result
RETURN myQuery;
END MakeSQL;
Example call
SELECT MakeSQL('EntityID=1;deployed=2018-01-01;type=app
EntityID=2;deployed=2018-02-02;type=app') FROM DUAL;
Example-Result
SELECT '1' as EntityID, '2018-01-01' as deployed, 'app' as type FROM DUAL UNION ALL
SELECT '2' as EntityID, '2018-02-02' as deployed, 'app' as type FROM DUAL
I found a problem in ORACLE-CERTIFICATE about substring (I guess) that I don't have any idea how to do that. Does anyone understand this??
Create table Bonus and do DML insert to Bonus table for the following script:
Hint: you should be able to ran this script against your table without any error and generate the output "Here is the result".
select (substr(
substr(
substr(
l.object,
instr(l.object,',')+1
),
instr(
substr(
l.object,
instr(l.object,',')+1
),
','
)+1
),
instr(
substr(
substr(l.object,instr(l.object,',')+1),
instr(
substr(
l.object,
instr(l.object,',')+1
),
','
)+1
),
','
)+1
)
) ref
from Bonus l
where l.l_index = 't234'
Here is the output:
REF
------------------------------------------------------------
Here is the result
Yes, it is looking for the remainder of the string that follows the 3rd comma:
Oracle Setup:
CREATE TABLE Bonus ( l_index, object ) AS
SELECT 't234', ',,,Here is the result' FROM DUAL;
Query:
select (substr(
substr(
substr(
l.object,
instr(l.object,',')+1
),
instr(
substr(
l.object,
instr(l.object,',')+1
),
','
)+1
),
instr(
substr(
substr(
l.object,
instr(l.object,',')+1
),
instr(
substr(
l.object,
instr(l.object,',')+1
),
','
)+1
),
','
)+1
)
) ref
from Bonus l
where l.l_index = 't234'
Output:
REF
---------------------
Here is the result
A simpler version of this query would be:
SELECT SUBSTR( object, INSTR( object, ',', 1, 3 ) + 1 ) AS ref
FROM Bonus
WHERE l_index = 't234';
I'm looking for a way to achieve this in a SELECT statement.
FROM
Column1 Column2 Column3
A,B,C 1,2,3 x,y,z
TO
Result
A|1|x,B|2|y,C|3|z
The delimiters don't matter. I'm just trying to to get all the data in one single column. Ideally I am looking to do this in DB2. But I'd like to know if there's an easier way to get this done in Oracle.
Thanks
You can do it like this using INSTR and SUBSTR:
select
substr(column1,1,instr(column1,',',1)-1) || '|' ||
substr(column2,1,instr(column2,',',1)-1) || '|' ||
substr(column3,1,instr(column3,',',1)-1) || '|' ||
',' ||
substr(column1 ,instr(column1 ,',',1,1)+1,instr(column1 ,',',1,2) - instr(column1 ,',',1)-1) || '|' ||
substr(column2 ,instr(column2 ,',',1,1)+1,instr(column2 ,',',1,2) - instr(column2 ,',',1)-1) || '|' ||
substr(column3 ,instr(column3 ,',',1,1)+1,instr(column3 ,',',1,2) - instr(column3 ,',',1)-1) || '|' ||
',' ||
substr(column1 ,instr(column1 ,',',1,2)+1) || '|' ||
substr(column2 ,instr(column2 ,',',1,2)+1) || '|' ||
substr(column3 ,instr(column3 ,',',1,2)+1)
from yourtable
i tried some thing. just look into link
first i created a table called t_ask_test and inserted the data based on the above question. Achieved the result by using the string functions
sample table
create table t_ask_test(column1 varchar(10), column2 varchar(10),column3 varchar(10));
inserted a row
insert into T_ASK_TEST values ('A,B,C','1,2,3','x,y,z');
the following query will be in dynamic way
select substr(column1,1,instr(column1,',',1,1)-1)||'|'||substr(column2,1,instr(column1,',',1,1)-1)||'|'||substr(column3,1,instr(column1,',',1,1)-1) ||','||
substr(column1,instr(column1,',',1,1)+1,instr(column1,',',1,2)-instr(column1,',',1,1)-1)||'|'||substr(column2,instr(column2,',',1,1)+1,instr(column2,',',1,2)-instr(column2,',',1,1)-1)||'|'||substr(column3,instr(column3,',',1,1)+1,instr(column3,',',1,2)-instr(column3,',',1,1)-1) ||','||
substr(column1,instr(column1,',',1,2)+1,length(column1)-instr(column1,',',1,2))||'|'||substr(column2,instr(column2,',',1,2)+1,length(column2)-instr(column2,',',1,2))||'|'||substr(column3,instr(column3,',',1,2)+1,length(column3)-instr(column3,',',1,2)) as test from t_ask_test;
output will be as follows
TEST
---------------
A|1|x,B|2|y,C|3|z
If you have a dynamic number of entries for each row then:
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE TEST ( Column1, Column2, Column3 ) AS
SELECT 'A,B,C', '1,2,3', 'x,y,z' FROM DUAL
UNION ALL SELECT 'D,E', '4,5', 'v,w' FROM DUAL;
Query 1:
WITH ids AS (
SELECT t.*, ROWNUM AS id
FROM TEST t
)
SELECT LISTAGG(
REGEXP_SUBSTR( i.Column1, '[^,]+', 1, n.COLUMN_VALUE )
|| '|' || REGEXP_SUBSTR( i.Column2, '[^,]+', 1, n.COLUMN_VALUE )
|| '|' || REGEXP_SUBSTR( i.Column3, '[^,]+', 1, n.COLUMN_VALUE )
, ','
) WITHIN GROUP ( ORDER BY n.COLUMN_VALUE ) AS value
FROM ids i,
TABLE(
CAST(
MULTISET(
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= GREATEST(
REGEXP_COUNT( i.COLUMN1, '[^,]+' ),
REGEXP_COUNT( i.COLUMN2, '[^,]+' ),
REGEXP_COUNT( i.COLUMN3, '[^,]+' )
)
)
AS SYS.ODCINUMBERLIST
)
) n
GROUP BY i.ID
Results:
| VALUE |
|-------------------|
| A|1|x,B|2|y,C|3|z |
| D|4|v,E|5|w |
You need to use:
SUBSTR
INSTR
|| concatenation operator
It would be easy if you break your output, and then understand how it works.
SQL> WITH t AS
2 ( SELECT 'A,B,C' Column1, '1,2,3' Column2, 'x,y,z' Column3 FROM dual
3 )
4 SELECT SUBSTR(column1, 1, instr(column1, ',', 1) -1)
5 ||'|'
6 || SUBSTR(column2, 1, instr(column2, ',', 1) -1)
7 ||'|'
8 || SUBSTR(column3, 1, instr(column1, ',', 1) -1)
9 ||','
10 || SUBSTR(column1, instr(column1, ',', 1, 2) +1 - instr(column1, ',', 1),
11 instr(column1, ',', 1) -1)
12 ||'|'
13 || SUBSTR(column2, instr(column2, ',', 1, 2) +1 - instr(column2, ',', 1),
14 instr(column2, ',', 1) -1)
15 ||'|'
16 || SUBSTR(column3, instr(column3, ',', 1, 2) +1 - instr(column3, ',', 1),
17 instr(column3, ',', 1) -1)
18 ||','
19 || SUBSTR(column1, instr(column1, ',', 1, 3) +1 - instr(column1, ',', 1),
20 instr(column1, ',', 2) -1)
21 as "new_column"
22 FROM t;
new_column
-------------
A|1|x,B|2|y,C
On a side note, you should avoid storing delimited values in a single column. Consider normalizing the data.
From Oracle 11g and above, you could create a VIRTUAL COLUMN using the above expression and use it instead of executing the SQL frequently.
Its very simple in oracle. just use the concatenation operatort ||.
In the below solution, I have used underscore as the delimiter
select Column1 ||'_'||Column2||'_'||Column3 from table_name;