Use Output of Oracle SQL Function in WHERE condition of SQL query - sql

I wrote a Oracle SQL Function that will return EMP IDs in the following format.
Query: select CUSFNC_GETEMPID('HIGH-RATED') from dual;
Output: ('1436','1444','1234')
I want to use this Output in SQL Query as follows:
SELECT ID, NAME, SAL
FROM EMP
WHERE ID IN CUSFNC_GETEMPID('HIGH-RATED')
I am expecting this query returns records with IDs equal to values mentioned in the above query.
But it is not working as expected.
How to achieve this by modifying output of function or any other way?

You could use a join instead of a IN clause
SELECT ID, NAME, SAL
FROM EMP
inner join (
select CUSFNC_GETEMPID('HIGH-RATED') hrated from dual;
) t on t.hrated = emp.id
and could be that your emp.id is a number so you should convert to char
SELECT ID, NAME, SAL
FROM EMP
inner join (
select CUSFNC_GETEMPID('HIGH-RATED') hrated from dual;
) t on t.hrated = to_char(emp.id)

You can use a PIPELINED TABLE FUNCTION, like this:
CREATE FUNCTION CUSFNC_GETEMPID(param1 varchar2) RETURN DBMSOUTPUT_LINESARRAY PIPELINED AS
BEGIN
FOR a_row IN
(
-- YOUR SQL WHICH RETURNS ROWS WITH ID VALUES HERE...
SELECT id
FROM
(
SELECT '1' id, 'HIGH-RATED' col FROM dual UNION
SELECT '2' id, 'HIGH-RATED' col FROM dual UNION
SELECT '3' id, 'LOW-RATED' col FROM dual
)
WHERE col = param1 -- FILTER BASED ON INPUT PARAM...
)
LOOP
PIPE ROW(a_row.id); -- PUT THE ID COLUMN HERE...
END LOOP;
END;
/
And then call it in your SQL like this:
SELECT ID, NAME, SAL
FROM EMP
WHERE ID IN (SELECT column_value FROM TABLE(CUSFNC_GETEMPID('HIGH-RATED')))

SELECT ID, NAME, SAL
FROM EMP
WHERE ID IN (SELECT CUSFNC_GETEMPID('HIGH-RATED')AS ID FROM DUAL)

Related

Oracle SQL select greatest value of the result of two different select statements

I'm running select statements in a loop with a cursor to collect data from different tables;
Quick (NOT WORKING) example;
select DISTINCT(ordernr) from orders
INSERT INTO newtable (select Crs.ordernr as ordernr, value1 as value from table2 where table2.ordernr = Crs.ordernr );
INSERT INTO newtable (select Crs.ordernr as ordernr, value2 as value from table3 where tabel3.ordernr=Crs.ordernr ;
)
LOOP
END LOOP;
END;
What I could like is just one insert statement which insert just the biggest value of boths select statements.
I've tried to work with greatest function in my loop but i'm running stuck. Both datatypes are the same for value1 and value2
insert into newtable(select crs.ordernr as ordernr, greatest
(
select value1 as value from table1 where condition=1, select value2 as value from table2 where condition=1)
)
);
Is it possible with a select case or other way to return only one value based on conditions? For example the biggest value of value1 or value2?
You can use the greatest function and provide subqueries to it, but - follow the syntax, i.e. enclose each of them (select statements) into its own parenthesis.
Something like this:
SQL> select greatest ( (select max(sal) from emp where job = 'CLERK'),
2 (select max(sal) from emp where job = 'ANALYST')
3 ) greatest_salary
4 from dual;
GREATEST_SALARY
---------------
3450
SQL>
I have no idea what is this:
insert into newtable(select crs.ordernr as ordernr
-----------------------------
supposed to do; what is crs? Where's the from clause?
From Oracle 12, you can use:
INSERT INTO newtable (ordernr, value)
SELECT *
FROM (
SELECT Crs.ordernr as ordernr,
value1 as value
FROM table2
WHERE table2.ordernr = Crs.ordernr
UNION ALL
SELECT Crs.ordernr,
value2
FROM table3
WHERE table3.ordernr=Crs.ordernr
)
ORDER BY value DESC
FETCH FIRST ROW ONLY;
This will mean that if you extend the query to extract more columns and you want the highest of one column and the corresponding values of the other columns then the values will all come from that one row with the highest value.
db<>fiddle here

bigquery transpose and concatenate for each record

I want to achieve the following transformation.
I have last_name stored in a repeated record as follows.
data before transformation
I want to achieve the following.
data after transformation
Example with sample data created.
create or replace table t1.cte1 as
WITH t1 AS (
SELECT 1 as id,'eren' AS last_name UNION ALL
SELECT 1 as id,'yilmaz' AS last_name UNION ALL
SELECT 1 as id,'kaya' AS last_name
)
SELECT id,ARRAY_AGG(STRUCT(last_name)) AS last_name_rec
FROM t1
GROUP BY id;
with test as (
select x.id, x.lname_agg,y.last_name from
(
select id, STRING_AGG(h.last_name,' ') lname_agg FROM
t1.cte1
LEFT JOIN
UNNEST(last_name_rec) AS h
group by id
) x,
(select id,h.last_name last_name FROM
t1.cte1
LEFT JOIN
UNNEST(last_name_rec) AS h
group by last_name,id) y
) select id ,sp.string_flatten_dedup( lname_agg,' ') concat_last_name, last_name from test;
I'm not sure either if I should store it as an array instead of a concatenated field but it would be good to know how to achieve both.
storing the concat_last_name as an array
I have achieved the first transformation as follows but I had to dedup the concatenated field with a function I wrote.
I'm sure there is a much better way of achieving this.
with test as (
select x.id id, x.lname_agg,y.last_name from
(
select id, STRING_AGG(h.last_name,' ') lname_agg FROM
small_test
LEFT JOIN
UNNEST(last_name_rec) AS h
group by id
) x,
(select id,h.last_name last_name FROM
small_test
LEFT JOIN
UNNEST(last_name_rec) AS h group by last_name,id) y
) select id ,sp.string_flatten_dedup( lname_agg,' ') concat_last_name, last_name from test;
The function.
string_flatten_dedup
CREATE OR REPLACE FUNCTION
sp.string_flatten_dedup(string_value string,
delim string) AS
(
ARRAY_TO_STRING
(ARRAY(SELECT distinct string_value
FROM UNNEST(SPLIT(string_value, delim)) AS string_value
order by string_value desc, string_value),
delim)
);
before using function.
intermediate results.
Final result after applying dedup function.
final output
Updated table structure.
t1.ccte1
Yours works but I got the table structure incorrect when I first posted.
create or replace table t1.cte2 as
with your_table as (
select 1 id, ['brown', 'smith', 'jones'] last_name union all
select 2, ['ryan', 'murphy']
) select id, ln as last_name,
array_to_string(last_name, ',') as concat_last_name,
from your_table, unnest(last_name) ln;
select id, ln as last_name,
array_to_string(last_name, ',') as concat_last_name,
from t1.cte2, unnest(last_name) ln;
--fails as its not the structure I thought it was cte1 is different then cte2
select id, ln.last_name
--array_to_string(last_name, ',') as concat_last_name,
from t1.cte1, unnest(last_name_rec) ln;
Consider below approach
select id, ln as last_name,
array_to_string(last_name, ',') as concat_last_name,
from your_table, unnest(last_name) ln
if applied to sample data in your question data before transformation
with your_table as (
select 1 id, ['brown', 'smith', 'jones'] last_name union all
select 2, ['ryan', 'murphy']
)
output is
In case if you want last names as an array - you already have this array - see below for how to use it
select id, ln as last_name,
last_name as concat_last_name,
from your_table, unnest(last_name) ln
with output

Can we select count(query) from table

SELECT COUNT(ANOTHER SELECT QUERY) FROM DUAL.
Can we get the results this way or is there any other way?
An example could help:
SQL> create table tabTest as (select 1 x from dual);
Table created.
SQL> select count( select * from tabTest ) from dual;
select count( select * from tabTest ) from dual
*
ERROR at line 1:
ORA-00936: missing expression
SQL> select count(*) from (select * from tabTest);
COUNT(*)
----------
1
You can use a derived table (aka "sub-query")
select count(*)
from (
.... your query here ...
);

Order by clause with union in SQL select query

Is there a way to order the union of two select all statements based on table column values.
My sample code is:
SELECT * FROM emp WHERE mgr='7839'
UNION
SELECT * FROM emp WHERE NOT EXISTS (SELECT * FROM emp WHERE mgr='7839')
AND empno='7839'
order by ename;
This code is showing error as:invalid identifier 'ENAME'.
I am not using specific columns in select statement instead of * since there are more than 10 columns in the table and the code looks so big.
But ename is a column in emp.
will you consider small change in your query.
SELECT * FROM
(SELECT * FROM emp WHERE mgr='7839'
UNION
SELECT * FROM emp WHERE NOT EXISTS (SELECT * FROM emp WHERE mgr='7839')
AND empno='7839') AA
order by AA.ename;
Why would use use union for this? Just do:
SELECT e.*
FROM emp e
WHERE e.mgr = '7839' OR
(NOT EXISTS (SELECT 1 FROM emp e2 WHERE e2.mgr = '7839') AND
empno = '7839'
)
ORDER BY ename;

Join two sql queries side by side with no column common

I need to join the results of two queries horizontally.
Consider a query below which will return two rows:
Select *
from Salary
where sal > 10000
The result of the query above should be joined side by side with the result of the query below which will again return two rows.Or I need to just concatenate the two result sets:
Select 'xyz' from dual
union
Select 'abc' from dual
Please suggest how this can be done as I tried to do this with the query below but it returns a cartesian product:
Select *
from (Select *
from salary
where sal > 10000) TEMP1,
(Select 'xyz' from dual
union
Select 'abc' from dual) TEMP2
You can do that by joining on rownum like this:
SELECT *
FROM
(SELECT view_name, rownum AS r FROM all_views WHERE rownum <=10)
FULL OUTER JOIN (SELECT table_name, rownum AS r FROM all_tables WHERE rownum <=10) USING (r)
In your case this would look like (untested):
Select * from
(Select salary.*, rownum AS r from salary where sal>10000) TEMP1
FULL OUTER JOIN
(SELECT temp2.*, rownum r FROM
(Select 'xyz' from dual
union
Select 'abc' from dual) TEMP2)
USING (r)
you can introduce an artificial join column:
SELECT *
FROM (SELECT s.*, ROWNUM ID FROM Salary s WHERE sal > 10000) q1
JOIN (SELECT 'xyz' col1, 1 ID
FROM dual
UNION
SELECT 'abc' col1, 2 ID FROM dual) q2 ON q1.id = q2.id
Thank you very much for your help.
But what I need is a bit complicated. I have updated the previous query to be somewhat like below instead of simple query that I posted before (SELECT * FROM Salary WHERE sal > 10000):
SELECT name, sal, address
FROM (SELECT e1.name, s1.sal, s1.grade, s2.address FROM Emp e1, salary s1,
(Select empcode, address FROM Address WHERE empcode LIKE 'NY%') s2
WHERE e1.hiredate =201001
AND s1.sal>10000)
I know the above query does not make much of a relevance. However, this is similar to what I need actually. I am not posting the original one as that is very complicated but if this can be done for this query, then I will be able to replicate the same on the original one as well.
Thank you,
Sharon