Search and extract data from SQL column having some delimiter - sql

I have column in table having data in below format:
(DeliveryMethod+NON;Installation_Method+NoInstallation;Services_Reference_ID+100118547,44444,33333;Service_ID+2222)
(key+value;key+value;key+value;key+value;key+value;key+value;key+value;)
I want to search and extract a particular "value" from this column based on specific "key" and "key+value" can be on any position, how to do this using a SQL query?

Here's one way to approach it in Oracle as I answered in this post: Oracle 11gR2: split string with multiple delimiters(add). Hopefully you can apply the logic to your RDBMS. Note that this answer doesn't just get the value from the string, but attempts to parse the string and return values so they can be processed like rows in a query's result set. This may be overkill for your scenario. At any rate, it's just one way to look at it.
-- Original data with multiple delimiters and a NULL element for testing.
with orig_data(str) as (
select 'DeliveryMethod+NON;Installation_Method+NoInstallation;;Services_Reference_ID+100118547,44444,33333;Service_ID+2222' from dual
),
--Split on first delimiter (semi-colon)
Parsed_data(rec) as (
select regexp_substr(str, '(.*?)(;|$)', 1, LEVEL, NULL, 1)
from orig_data
CONNECT BY LEVEL <= REGEXP_COUNT(str, ';') + 1
)
-- For testing-shows records based on 1st level delimiter
--select rec from parsed_data;
-- Split the record into columns
select regexp_replace(rec, '^(.*)\+.*', '\1') col1,
regexp_replace(rec, '^.*\+(.*)', '\1') col2
from Parsed_data;
Result:
To specifically answer your question, in order to get a value based on a key, change the last query to this in order to get the value where the key is 'Service_ID':
select value
from (
select regexp_replace(rec, '^(.*)\+.*', '\1') key,
regexp_replace(rec, '^.*\+(.*)', '\1') value
from Parsed_data )
where key = 'Service_ID';
Result:
Or to just extract it out of the string using a regular expression:
with orig_data(str) as (
select 'Service_ID+2222;DeliveryMethod+NON;Installation_Method+NoInstallation;;Services_Reference_ID+100118547,44444,33333' from dual
)
select regexp_substr(str, '(.*?)Service_ID\+(.+?)(;|$)', 1, 1, NULL, 2) value
from orig_data;

Related

How to extract numbers from a string SQL

Ts there a way to extract numbers from a string?
In my database there is a column called Reference and it contains multiple numbers
CM|319|14163|||109|405000
Is there a way to get the first number like this?
select CM|%s|... as ParentId from table
So the output should be 319
And maybe even multiple like
select CM|...|%s|... as SiblingId, CM|%s|... as ParentId from table
14163 319
We might be able to use PATINDEX here along with a substring operation. We can find the position of the first number in the string, and then take a substring until one position past the first occurrence of a number followed by a pipe character.
SELECT SUBSTRING(val,
PATINDEX('%[0-9]%', val),
PATINDEX('%[0-9]|%', val) - PATINDEX('%[0-9]%', val) + 1)
FROM yourTable;
Demo
Data:
WITH yourTable AS (
SELECT 'CM|319|14163|||109|405000' AS val
)
if using sql2017 or later...
maybe?
CREATE TABLE #yourtable(
reference VARCHAR(50) NOT NULL PRIMARY KEY
);
INSERT INTO #yourtable(reference) VALUES ('CM|319|14163|||109|405000');
INSERT INTO #yourtable(reference) VALUES ('CMff123|14163|||109AA|4ZXC05000');
SELECT reference,
TRIM(REPLACE(TRANSLATE(reference,'abcdefghijklmnopqrstuvwxyz',REPLICATE('|',26)),'|',' ')) AS extracted
FROM #yourtable

query json data from oracle 12.1 having fields value with "."

I have a table that has JSON data stored and I'm using json_exists functions in the query. Below is my sample data from the column for one of the rows.
{"fields":["query.metrics.metric1.field1",
"query.metrics.metric1.field2",
"query.metrics.metric1.field3",
"query.metrics.metric2.field1",
"query.metrics.metric2.field2"]}
I want all those rows which have a particular field. So, I'm trying below.
SELECT COUNT(*)
FROM my_table
WHERE JSON_EXISTS(fields, '$.fields[*]."query.metrics.metric1.field1"');
It does not give me any results back. Not sure what I'm missing here. Please help.
Thanks
You can use # operator which refers to an occurrence of the array fields such as
SELECT *
FROM my_table
WHERE JSON_EXISTS(fields, '$.fields?(#=="query.metrics.metric1.field1")')
Demo
Edit : The above case works for 12R2+, considering that it doesn't work for your version(12R1), try to use JSON_TABLE() such as
SELECT fields
FROM my_table,
JSON_TABLE(fields, '$.fields[*]' COLUMNS ( js VARCHAR2(90) PATH '$' ))
WHERE js = 'query.metrics.metric1.field1'
Demo
I have no idea how to "pattern match" on the array element, but just parsing the whole thing and filtering does the job.
with t(x, json) as (
select 1, q'|{"fields":["a", "b"]}|' from dual union all
select 2, q'|{"fields":["query.metrics.metric1.field1","query.metrics.metric1.field2","query.metrics.metric1.field3","query.metrics.metric2.field1","query.metrics.metric2.field2"]}|' from dual
)
select t.*
from t
where exists (
select null
from json_table(
t.json,
'$.fields[*]'
columns (
array_element varchar2(100) path '$'
)
)
where array_element = 'query.metrics.metric1.field1'
);
In your code, you are accessing the field "query.metrics.metric1.field1" of an object in the fields array, and there is no such object (the elements are strings)...

SQL query to fetch rows based on a column

I have a table Employee as below. The Change column contains names of columns whose values are modified through my application. Data in this column is separated by comma. I need to query this table in such a way that the result will have one change per row. ie split the data in the Change column by comma and fetch the corresponding row. I don't have an idea where to start ! Please help.
Let's see, you could use Oracle's regexp_substr function:
select distinct Id, Name, Address, trim(regexp_substr(Change,'[^,]+', 1, level))
from Employee
connect by regexp_substr(Change, '[^,]+', 1, level) is not null;
This should work for any number of comma-separated values in your Change column.
See doc on the rexexp_substr function here: https://docs.oracle.com/cd/B12037_01/server.101/b10759/functions116.htm
here i have tried with the use of regexp_substr included multiset level
with temp as
(
select id, name, address, change from testemp
)
select id,name,address,trim(regexp_substr(change, '[^,]+', 1, levels.column_value)) change
from temp t,
table(cast(multiset(select level from dual
connect by level <= length (regexp_replace(change, '[^,]+')) + 1)
as sys.OdciNumberList)) levels;

SQL Server using a sequence in conjunction with union selects

CREATE SEQUENCE id AS INTEGER START WITH 1 INCREMENT BY 1;
WITH source (Id, SomeColumn) AS
(
SELECT NEXT VALUE FOR id, 'some value 1'
UNION
SELECT NEXT VALUE FOR id, 'some value 2'
)
SELECT * FROM source;
DROP SEQUENCE id;
It throws the following error:
NEXT VALUE FOR function is not allowed in check constraints, default
objects, computed columns, views, user-defined functions, user-defined
aggregates, user-defined table types, sub-queries, common table
expressions, derived tables or return statements.
Why does the NEXT VALUE FOR function not work with unions?
What is a good alternative?
I am using a similar CTE to seed a table with default values using a merge statement. I am trying to avoid manually typing in a sequence of numbers.
I need something like UNION SELECT id++
Based on Alex's comment and link to a similar problem here is the solution:
WITH source (SomeColumn) AS
(
SELECT 'some value 1'
UNION SELECT 'some value 2'
)
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), SomeColumn FROM source;
For future reference: ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) is the way to get row numbers without ordering a set

Oracle Query with ID in Substring of another Table Column [duplicate]

I have (and don't own, so I can't change) a table with a layout similar to this.
ID | CATEGORIES
---------------
1 | c1
2 | c2,c3
3 | c3,c2
4 | c3
5 | c4,c8,c5,c100
I need to return the rows that contain a specific category id. I starting by writing the queries with LIKE statements, because the values can be anywhere in the string
SELECT id FROM table WHERE categories LIKE '%c2%';
Would return rows 2 and 3
SELECT id FROM table WHERE categories LIKE '%c3%' and categories LIKE '%c2%'; Would again get me rows 2 and 3, but not row 4
SELECT id FROM table WHERE categories LIKE '%c3%' or categories LIKE '%c2%'; Would again get me rows 2, 3, and 4
I don't like all the LIKE statements. I've found FIND_IN_SET() in the Oracle documentation but it doesn't seem to work in 10g. I get the following error:
ORA-00904: "FIND_IN_SET": invalid identifier
00904. 00000 - "%s: invalid identifier"
when running this query: SELECT id FROM table WHERE FIND_IN_SET('c2', categories); (example from the docs) or this query: SELECT id FROM table WHERE FIND_IN_SET('c2', categories) <> 0; (example from Google)
I would expect it to return rows 2 and 3.
Is there a better way to write these queries instead of using a ton of LIKE statements?
You can, using LIKE. You don't want to match for partial values, so you'll have to include the commas in your search. That also means that you'll have to provide an extra comma to search for values at the beginning or end of your text:
select
*
from
YourTable
where
',' || CommaSeparatedValueColumn || ',' LIKE '%,SearchValue,%'
But this query will be slow, as will all queries using LIKE, especially with a leading wildcard.
And there's always a risk. If there are spaces around the values, or values can contain commas themselves in which case they are surrounded by quotes (like in csv files), this query won't work and you'll have to add even more logic, slowing down your query even more.
A better solution would be to add a child table for these categories. Or rather even a separate table for the catagories, and a table that cross links them to YourTable.
You can write a PIPELINED table function which return a 1 column table. Each row is a value from the comma separated string. Use something like this to pop a string from the list and put it as a row into the table:
PIPE ROW(ltrim(rtrim(substr(l_list, 1, l_idx - 1),' '),' '));
Usage:
SELECT * FROM MyTable
WHERE 'c2' IN TABLE(Util_Pkg.split_string(categories));
See more here: Oracle docs
Yes and No...
"Yes":
Normalize the data (strongly recommended) - i.e. split the categorie column so that you have each categorie in a separate... then you can just query it in a normal faschion...
"No":
As long as you keep this "pseudo-structure" there will be several issues (performance and others) and you will have to do something similar to:
SELECT * FROM MyTable WHERE categories LIKE 'c2,%' OR categories = 'c2' OR categories LIKE '%,c2,%' OR categories LIKE '%,c2'
IF you absolutely must you could define a function which is named FIND_IN_SET like the following:
CREATE OR REPLACE Function FIND_IN_SET
( vSET IN varchar2, vToFind IN VARCHAR2 )
RETURN number
IS
rRESULT number;
BEGIN
rRESULT := -1;
SELECT COUNT(*) INTO rRESULT FROM DUAL WHERE vSET LIKE ( vToFine || ',%' ) OR vSET = vToFind OR vSET LIKE ('%,' || vToFind || ',%') OR vSET LIKE ('%,' || vToFind);
RETURN rRESULT;
END;
You can then use that function like:
SELECT * FROM MyTable WHERE FIND_IN_SET (categories, 'c2' ) > 0;
For the sake of future searchers, don't forget the regular expression way:
with tbl as (
select 1 ID, 'c1' CATEGORIES from dual
union
select 2 ID, 'c2,c3' CATEGORIES from dual
union
select 3 ID, 'c3,c2' CATEGORIES from dual
union
select 4 ID, 'c3' CATEGORIES from dual
union
select 5 ID, 'c4,c8,c5,c100' CATEGORIES from dual
)
select *
from tbl
where regexp_like(CATEGORIES, '(^|\W)c3(\W|$)');
ID CATEGORIES
---------- -------------
2 c2,c3
3 c3,c2
4 c3
This matches on a word boundary, so even if the comma was followed by a space it would still work. If you want to be more strict and match only where a comma separates values, replace the '\W' with a comma. At any rate, read the regular expression as:
match a group of either the beginning of the line or a word boundary, followed by the target search value, followed by a group of either a word boundary or the end of the line.
As long as the comma-delimited list is 512 characters or less, you can also use a regular expression in this instance (Oracle's regular expression functions, e.g., REGEXP_LIKE(), are limited to 512 characters):
SELECT id, categories
FROM mytable
WHERE REGEXP_LIKE('c2', '^(' || REPLACE(categories, ',', '|') || ')$', 'i');
In the above I'm replacing the commas with the regular expression alternation operator |. If your list of delimited values is already |-delimited, so much the better.