How to parametrize SQL query, by passing multiple value - sql

I have a SQL query, in where clause "MID = 123". I want to use a parameter for the VALUE and pass multiple values.
Example: now I am passing one VALUE of 123, but I want to pass multiple value like 123, 124, 125 etc. from which the SQL query will take values one by one and pass those to the where clause, and produce the result one by one, like first use value = 123, get the result, when use value = 124 and get results, and last use value = 125 to get results.
I need help with this parameter.
It will be great if we don't change the SQL query but we can parameter the where clause.
NOTE: I am using SQL Developer.
SQL query:
SELECT
'ABC' AS COLUMN_NAME,
(CASE
WHEN to_char(count(ABC)) > 1
AND to_char(max(ABC)) = to_char(min(ABC))
AND to_char(count(ABC)) = count(*)
AND to_char(max(ABC)) IS NULL
THEN 'same'
ELSE 'Diff'
END) AS COMPARISON_VALUE,
(CASE
WHEN to_char(COUNT(ABC)) = 1 OR to_char(min(ABC)) IS NULL
THEN 'No Values'
ELSE to_char(max(ABC))
END) AS TRANSACTION1,
to_char(min(ABC)) AS TRANSACTION2
FROM
ADVICES
WHERE
MID = '123';

One approach would be to specify the test data in a WITH clause and feed it to the query via an outer join. We include the test value as the first column in the result set. This means that every row in the result set will have the associated test value.
WITH testdata(testval) AS
(
SELECT '123' FROM DUAL UNION ALL
SELECT '124' FROM DUAL UNION ALL
SELECT '125' FROM DUAL
)
SELECT
testdata.testval,
...
...
FROM
ADVICES, testdata
WHERE
MID (+) = testdata.testval;
...i am trying to parameter the where clause, for which i want to fetch
parameter data from text or CSV should be fine.
Alternatively, testdata could be a table that's loaded from a CSV file with test values.

You may pass a CSV and use LIKE.
where ','||:var||',' like '%,'||MID||',%'
Example using HR schema
var myvar VARCHAR2
exec :myvar := '101,102,103' --parameter
select employee_id,department_id from employees where
','||:myvar||',' like '%,'||employee_id||',%';
Result
EMPLOYEE_ID DEPARTMENT_ID
----------- -------------
101 90
102 90
103 60

You can write your WHERE clause like the following:
WHERE MID IN
(SELECT
REGEXP_SUBSTR('YOUR_VALUE', '[^,]+', 1, LEVEL)
FROM
DUAL
CONNECT BY
REGEXP_SUBSTR('YOUR_VALUE', '[^,]+', 1, LEVEL) IS NOT NULL);
For checking the solution:
-- With multiple values
DEFINE YOUR_VALUE= '123,124,125';
SELECT
REGEXP_SUBSTR('&YOUR_VALUE', '[^,]+', 1, LEVEL)
FROM
DUAL
CONNECT BY
REGEXP_SUBSTR('&YOUR_VALUE', '[^,]+', 1, LEVEL) IS NOT NULL);
-- OUTPUT --
REGEXP_SUBS
-----------
123
124
125
-- With single value
DEFINE YOUR_VALUE = '123';
SELECT
REGEXP_SUBSTR('&YOUR_VALUE', '[^,]+', 1, LEVEL)
FROM
DUAL
CONNECT BY
REGEXP_SUBSTR('&YOUR_VALUE', '[^,]+', 1, LEVEL) IS NOT NULL;
-- OUTPUT --
REGEXP_SUBS
-----------
123
Cheers!!

You can change your where clause to -
WHERE
MID IN (YOUR_PARAMETER);
When you will pass multiple values, it will simply converted to OR condition and will fetch the results.

Related

How to find each case of matching pattern within a string and return as rows

I'm trying to identify reference numbers contained in strings in a column. The table looks something like this:
col1 col2
1 fgREF1234fhjdREF1235hgkjREF1236
2 hREF1237hjdfREF1238djhfhs
Need to write an SQL query that identifies the 'REF' followed by the 4 digits and returns each in its own row.
The output should look like this:
col1 ref
1 REF1234
1 REF1235
1 REF1236
2 REF1237
2 REF1238
I have tried:
select
case when substr(substr(col2, instr(col2, 'REF'), 7), 1, 1) like 'R'
then substr(col2, instr(col2, 'R'), 7) else null end ref
from table
...but this will only identify the first match in the string.
I am using Oracle SQL but ideally the solution would be able to be converted to other SQL variants.
Any help would be much appreciated!
You can use regexp_substr delimited by connect by level <= regexp_count(col2,'REF') ( the appearance time of the pattern string REF within the strings col2 )
with t(col1,col2) as
(
select 1,'fgREF1234fhjdREF1235hgkjREF1236' from dual union all
select 2,'hREF1237hjdfREF1238djhfhs' from dual
)
select col1,
regexp_substr(col2,'REF[0-9]+',1,level) as ref
from t
connect by level <= regexp_count(col2,'REF')
and prior col1 = col1
and prior sys_guid() is not null;
Demo
You can use the below code to get the desired result :-
select x.col1, explode(x.ref) as ref from (
select col1,split(trim(regexp_replace(col2,'[^REF0-9]',' ')),' ') as ref
from inp

Oracle SQL Min in Select Clause

Can some one please help me in writing a sql query that should do a oracle min function based on the following conditions.
For eg for column values
0,0,0,0 then output should be 0
0,null,0,null then output should be o
0,2,4,5,6 then output should be 2 (Note that we are excluding Zero here)
0,2,null,4,5 then output should be 2 (same here we are excluding zero)
null,null,null, null then output should be null.
I wrote query already that satisfies all the above cases but failing for last case when all the column values are null. Instead of returning null it is returning 0. Can some one modify the below query to fit for the last case as well?
select NVL(MIN(NULLIF(columnname,0)),0) from tablename;
Please also keep in mind that the query should be runnable in oracle as well as hsqldb as we are using hsql db for running junits.
If all 4 cases satisfied by your query then just a case will solve your problem.
SELECT CASE WHEN MIN(COLUMNNAME) IS NULL THEN NULL ELSE NVL(MIN(NULLIF(COLUMNNAME,0)),0) END FROM TABLENAME;
Note:- assuming all the cases satisfied by your query except 5th.
I will show below an input table with two columns, ID and VAL, to illustrate the various possibilities. You want a single result per ID (or even for the entire table), so this must be a job for GROUP BY and some aggregate function. You want to distinguish between three types of values: Greater than zero, zero, and null (in this order); you want to pick the "highest priority group" that exists for each ID (in this order of priority), and for that priority group only, you want to pick the min value. This is exactly what the aggregate FIRST/LAST function does. To order by the three "classes" of values, we use a CASE expression in the ORDER BY clause of the aggregate LAST function.
The WITH clause below is not part of the solution - I only include it to create test data (in your real life situation, use your actual table and column names and remove the entire WITH clause).
with
inputs ( id, val ) as (
select 1, 0 from dual union all
select 1, 0 from dual union all
select 1, 0 from dual union all
select 2, 0 from dual union all
select 2, null from dual union all
select 2, 0 from dual union all
select 3, 0 from dual union all
select 3, 2 from dual union all
select 3, 5 from dual union all
select 4, 0 from dual union all
select 4, 3 from dual union all
select 4, null from dual union all
select 5, null from dual union all
select 5, null from dual
)
select id,
min(val) keep (dense_rank last order by case when val > 0 then 2
when val = 0 then 1
else 0
end
) as min_val
from inputs
group by id
order by id
;
ID MIN_VAL
---------- ----------
1 0
2 0
3 2
4 3
5

Override Max Function to allow strings SQL?

Hello what I feel to be a simple question but cannot figure it out. I am trying to find the max number in relation to another column and group it, the issue that comes up is that one of the values is a string.
Name Value
Nate 0
Nate 1
Jeff 2
Nate 2
Nate 'Test'
For the data I actually want 'Test' to be equal to 1. However if I use the MAX() function here I will get:
Name Value
Nate 'Test'
Jeff 2
I can only think that maybe if I read 'Test' as 1 then use the max function (which I am not sure how to do) or possibly overload MAX() to my own definition somehow.
Thank you for any help you can give.
Storing mixed data in a string column is generally a bad idea.
You can convert a specific string to a fixed value with a case expression:
select max(case when value = 'Test' then '1' else value end) from ...
But you are still dealing with strings, so you probably want to convert them to numbers, to prevent '10' sorting before '2' for instance:
select max(to_number(case when value = 'Test' then '1' else value end)) from ...
or
select max(case when value = 'Test' then 1 else to_number(value) end) from ...
Using a CTE for your sample data:
-- CTE for dummy data
with your_table (name, value) as (
select 'Nate', '0' from dual
union all select 'Nate', '1' from dual
union all select 'Jeff', '2' from dual
union all select 'Nate', '2' from dual
union all select 'Nate', 'Test' from dual
)
-- actual query
select name,
max(case when value = 'Test' then 1 else to_number(value) end) as value
from your_table
group by name;
NAME VALUE
---- ----------
Nate 2
Jeff 2
But you have to cover all values that cannot be explicitly or implicitly converted to numbers.
If would be slightly easier if you wanted to ignore non-numeric values, or treat them all as the same fixed value, rather than mapping individual strings to their own numeric values. Then you could write a function that attempts to convert any string and if it gets any exception returns null (or some other fixed value).
From 12cR1 you can even do with with a PL/SQL declaration rather than a permanent standalone or packaged function, if this an occasional thing:
with
function hack_to_number(string varchar2) return number is
begin
return to_number(string);
exception
when others then
return 1;
end;
select name,
max(hack_to_number(value)) as value
from your_table
group by name;
NAME VALUE
---- ----------
Nate 2
Jeff 2
You'd probably be better off going back and redesigning the data model to prevent this kind of issue by using the correct data types.
As #DrYWit pointed out in a comment, from 12cR2 you don't even need to do that, as the to_number() function has this built in, if you call it explicitly:
select name,
max(to_number(value default 1 on conversion error)) as value
from your_table
group by name;
How about this regular expression "trick"?
SQL> with your_table (name, value) as (
2 select 'Nate', '0' from dual
3 union all select 'Nate', '1' from dual
4 union all select 'Jeff', '2' from dual
5 union all select 'Nate', '2' from dual
6 union all select 'Nate', 'Test' from dual
7 )
8 select name, max(to_number(value)) mv
9 from your_table
10 where regexp_like (value, '^\d+$')
11 group by name;
NAME MV
---- ----------
Nate 2
Jeff 2
SQL>

Search comma separated value in oracle 12

I have a Table - Product In Oracle, wherein p_spc_cat_id is stored as comma separated values.
p_id p_name p_desc p_spc_cat_id
1 AA AAAA 26,119,27,15,18
2 BB BBBB 0,0,27,56,57,4
3 BB CCCC 26,0,0,15,3,8
4 CC DDDD 26,0,27,7,14,10
5 CC EEEE 26,119,0,48,75
Now I want to search p_name which have p_spc_cat_id in '26,119,7' And this search value are not fixed it will some time '7,27,8'. The search text combination change every time
my query is:
select p_id,p_name from product where p_spc_cat_id in('26,119,7');
when i execute this query that time i can't find any result
I am little late in answering however i hope that i understood the question correctly.
Read further if: you have a table storing records like
1. 10,20,30,40
2. 50,40,20,70
3. 80,60,30,40
And a search string like '10,60', in which cases it should return rows 1 & 3.
Please try below, it worked for my small table & data.
create table Temp_Table_Name (some_id number(6), Ab varchar2(100))
insert into Temp_Table_Name values (1,'112,120')
insert into Temp_Table_Name values (2,'7,8,100,26')
Firstly lets breakdown the logic:
The table contains comma separated data in one of the columns[Column AB].
We have a comma separated string which we need to search individually in that string column. ['26,119,7,18'-X_STRING]
ID column is primary key in the table.
1.) Lets multiple each record in the table x times where x is the count of comma separated values in the search string [X_STRING]. We can use below query to create the cartesian join sub-query table.
Select Rownum Sequencer,'26,119,7,18' X_STRING
from dual
CONNECT BY ROWNUM <= (LENGTH( '26,119,7,18') - LENGTH(REPLACE( '26,119,7,18',',',''))) + 1
Small note: Calculating count of comma separated values =
Length of string - length of string without ',' + 1 [add one for last value]
2.) Create a function PARSING_STRING such that PARSING_STRING(string,position). So If i pass:
PARSING_STRING('26,119,7,18',3) it should return 7.
CREATE OR REPLACE Function PARSING_STRING
(String_Inside IN Varchar2, Position_No IN Number)
Return Varchar2 Is
OurEnd Number; Beginn Number;
Begin
If Position_No < 1 Then
Return Null;
End If;
OurEnd := Instr(String_Inside, ',', 1, Position_No);
If OurEnd = 0 Then
OurEnd := Length(String_Inside) + 1;
End If;
If Position_No = 1 Then
Beginn := 1;
Else
Beginn := Instr(String_Inside, ',', 1, Position_No-1) + 1;
End If;
Return Substr(String_Inside, Beginn, OurEnd-Beginn);
End;
/
3.) Main query, with the join to multiply records.:
select t1.*,PARSING_STRING(X_STRING,Sequencer)
from Temp_Table_Name t1,
(Select Rownum Sequencer,'26,119,7,18' X_STRING from dual
CONNECT BY ROWNUM <= (Select (LENGTH( '26,119,7,18') - LENGTH(REPLACE(
'26,119,7,18',',',''))) + 1 from dual)) t2
Please note that with each multiplied record we are getting 1 particular position value from the comma separated string.
4.) Finalizing the where condition:
Where
/* For when the value is in the middle of the strint [,value,] */
AB like '%,'||PARSING_STRING(X_STRING,Sequencer)||',%'
OR
/* For when the value is in the start of the string [value,]
parsing the first position comma separated value to match*/
PARSING_STRING(AB,1) = PARSING_STRING(X_STRING,Sequencer)
OR
/* For when the value is in the end of the string [,value]
parsing the last position comma separated value to match*/
PARSING_STRING(AB,(LENGTH(AB) - LENGTH(REPLACE(AB,',',''))) + 1) =
PARSING_STRING(X_STRING,Sequencer)
5.) Using distinct in the query to get unique ID's
[Final Query:Combination of all logic stated above: 1 Query to find them all]
select distinct Some_ID
from Temp_Table_Name t1,
(Select Rownum Sequencer,'26,119,7,18' X_STRING from dual
CONNECT BY ROWNUM <= (Select (LENGTH( '26,119,7,18') - LENGTH(REPLACE( '26,119,7,18',',',''))) + 1 from dual)) t2
Where
AB like '%,'||PARSING_STRING(X_STRING,Sequencer)||',%'
OR
PARSING_STRING(AB,1) = PARSING_STRING(X_STRING,Sequencer)
OR
PARSING_STRING(AB,(LENGTH(AB) - LENGTH(REPLACE(AB,',',''))) + 1) = PARSING_STRING(X_STRING,Sequencer)
You can use like to find it:
select p_id,p_name from product where p_spc_cat_id like '%26,119%'
or p_spc_cat_id like '%119,26%' or p_spc_cat_id like '%119,%,26%' or p_spc_cat_id like '%26,%,119%';
Use the Oracle function instr() to achieve what you want. In your case that would be:
SELECT p_name
FROM product
WHERE instr(p_spc_cat_id, '26,119') <> 0;
Oracle Doc for INSTR
If the string which you are searching will always have 3 values (i.e. 2 commas present) then you can use below approach.
where p_spc_cat_id like regexp_substr('your_search_string, '[^,]+', 1, 1)
or p_spc_cat_id like regexp_substr('your_search_string', '[^,]+', 1, 2)
or p_spc_cat_id like regexp_substr('your_search_string', '[^,]+', 1, 3)
If you cant predict how many values will be there in your search string
(rather how many commas) in that case you may need to generate dynamic query.
Unfortunately sql fiddle is not working currently so could not test this code.
SELECT p_id,p_name
FROM product
WHERE p_spc_cat_id
LIKE '%'||'&i_str'||'%'`
where i_str is 26,119,7 or 7,27,8
This solution uses CTE's. "product" builds the main table. "product_split" turns products into rows so each element in p_spc_cat_id is in it's own row. Lastly, product_split is searched for each value in the string '26,119,7' which is turned into rows by the connect by.
with product(p_id, p_name, p_desc, p_spc_cat_id) as (
select 1, 'AA', 'AAAA', '26,119,27,15,18' from dual union all
select 2, 'BB', 'BBBB', '0,0,27,56,57,4' from dual union all
select 3, 'BB', 'CCCC', '26,0,0,15,3,8' from dual union all
select 4, 'CC', 'DDDD', '26,0,27,7,14,10' from dual union all
select 5, 'CC', 'EEEE', '26,119,0,48,75' from dual
),
product_split(p_id, p_name, p_spc_cat_id) as (
select p_id, p_name,
regexp_substr(p_spc_cat_id, '(.*?)(,|$)', 1, level, NULL, 1)
from product
connect by level <= regexp_count(p_spc_cat_id, ',')+1
and prior p_id = p_id
and prior sys_guid() is not null
)
-- select * from product_split;
select distinct p_id, p_name
from product_split
where p_spc_cat_id in(
select regexp_substr('26,119,7', '(.*?)(,|$)', 1, level, NULL, 1) from dual
connect by level <= regexp_count('26,119,7', ',') + 1
)
order by p_id;
P_ID P_
---------- --
1 AA
3 BB
4 CC
5 CC
SQL>

Decode values of a column in plsql

I have table in which there is a column which contains values as 1:2 or 2:3 or 2:3:4 etc. I need to decode these values on the basis of their values mentioned in the different table. like 1 is x and 2 is y.
there are 5 values in the column. earlier only one value were there where name against that values was being fetched from other table by join condition. But now any combination of 5 values can be there separated by ":"-colon. Please suggest how to get names of these values for a column. Let me know if any other detail is required
Please suggest a way to implement this.
Hi here how you need to go;
first start with inner query it will give all values in you column like 1, 2,3, 4 etc. then need to create mapping table eg. 1 to 'x' , 2 to 'y' physically or logically as I have done and select from mapping table as per result from inner query which is your spitted column values.
with map_data as (
select 1 as d_value , 'X' as m_value from dual
union all
select 2 as d_value , 'Y' as m_value from dual
union all
select 3 as d_value , 'Z' as m_value from dual)
select * from map_data
where d_value in(
WITH data AS (
SELECT '1:2:3' AS "value" FROM DUAL
UNION ALL
SELECT '2:4' AS "value" FROM DUAL
)
SELECT REGEXP_SUBSTR( data."value", '[^:]+', 1, levels.COLUMN_VALUE )
FROM data,
TABLE(
CAST(
MULTISET(
SELECT LEVEL
FROM DUAL
CONNECT BY LEVEL <= LENGTH( regexp_replace( "value", '[^:]+')) + 1
) AS sys.OdciNumberList
)
) levels)