How to pass many string values in a variable in oracle?
This is what Im trying, but this returns nothing.
SET DEFINE ON;
DEFINE column_name ='"column_1","column_2","column_3","column_4","column_5"';
SELECT * FROM SYS.all_tab_columns WHERE column_name in ('&column_name');
For one value in variable it works fine, but how to pass many string value?
All the examples that I've seen here did not help me
You have:
the wrong quotes in the substitution variable; and
don't need the quotes around the substitution variable in the query if they are present in the replacement text of the substitution variable.
Like this:
SET DEFINE ON;
DEFINE column_name='column_1','column_2','column_3','column_4','column_5'
SELECT * FROM SYS.all_tab_columns WHERE column_name in (&column_name);
The client application (i.e. SQL*Plus or SQL Developer, which are some of the few clients that support this syntax) will effectively do a find-replace on &column_name and replace it with the string assigned in your DEFINE command.
The documentation for substitution variables is here.
Here's one option (if it must be DEFINE):
SQL> define column_name = 'EMPNO,DEPTNO'
SQL> select table_name, column_name from user_tab_columns where column_name in
2 (select regexp_substr('&&column_name', '[^,]+', 1, level) from dual
3 connect by level <= regexp_count('&&column_name', ',') + 1
4 );
TABLE_NAME COLUMN_NAME
------------------------------ ------------------------------
EMP EMPNO
DEPT DEPTNO
EMP DEPTNO
SQL>
Though, why bother? What's wrong with simple
SQL> select table_name, column_name from user_tab_columns where column_name in ('EMPNO', 'DEPTNO');
TABLE_NAME COLUMN_NAME
------------------------------ ------------------------------
DEPT DEPTNO
EMP EMPNO
EMP DEPTNO
SQL>
If you are looking for dynamism to procedure column list to be added to IN clause then something like this can be useful -
Idea is to use LISTAGG.
select listagg('''column'||'_'||level||'''',',') as column_list from dual connect by level<5;
COLUMN_LIST
--------------------------------------------------------------------------------
'column_1','column_2','column_3','column_4'
Use above generated column list in your IN query.
Else, if you are only concerned about all_tab_columns then you are better off specifying column names, as mentioned by #Littlefoot.
Related
I am a developer. In my table, I had a date range partitions.
I want to get partition names which are defined for a table between two dates.
I tried with below query and it is returning all the partitions on a table.
select * from USER_TAB_PARTITIONS WHERE TABLE_NAME = 'TABLE NAME' ORDER BY PARTITION_NAME;
My requirement is , I will pass two dates as inputs and between those two dates i want to get partition names.
Please suggest query.
That's not very simple; the major obstacle is user_tab_partitions.high_value datatype, which is long, and it is difficult to work with. Usually you get
ORA-00932: inconsistent datatypes: expected - got LONG
error.
However, using a few steps, it can be done. Have a look at this example.
Create a partitioned table and insert a few rows into it:
SQL> CREATE TABLE test_part
2 (
3 datum DATE,
4 text VARCHAR2 (10)
5 )
6 PARTITION BY RANGE (datum)
7 INTERVAL ( NUMTODSINTERVAL (1, 'day') )
8 (PARTITION p0 VALUES LESS THAN (TO_DATE ('01.01.2020', 'dd.mm.yyyy')));
Table created.
SQL> INSERT INTO test_part
2 SELECT DATE '2015-08-15', 'Little' FROM DUAL
3 UNION ALL
4 SELECT DATE '2020-03-26', 'Foot' FROM DUAL;
2 rows created.
What does user_tab_partitions say?
SQL> SELECT table_name, partition_name, high_value
2 FROM user_tab_partitions
3 WHERE table_name = 'TEST_PART';
TABLE_NAME PARTITION_NAME HIGH_VALUE
--------------- --------------- -----------------------------------
TEST_PART P0 TO_DATE(' 2020-01-01 00:00:00', 'SY
YYY-MM-DD HH24:MI:SS', 'NLS_CALENDA
R=GREGORIA
TEST_PART SYS_P63 TO_DATE(' 2020-03-27 00:00:00', 'SY
YYY-MM-DD HH24:MI:SS', 'NLS_CALENDA
R=GREGORIA
So, you'd want to extract date part from the high_value column. The first step is kind of a stupid one - create a new table; basically CTAS:
SQL> CREATE TABLE temp_utp
2 AS
3 SELECT table_name, partition_name, TO_LOB (high_value) high_value
4 FROM user_tab_partitions;
Table created.
For simplicity (in further steps), I'll create a view based on that table which will extract date value (line #5):
SQL> CREATE OR REPLACE VIEW v_utp
2 AS
3 SELECT table_name,
4 partition_name,
5 TO_DATE (SUBSTR (high_value, 12, 10), 'rrrr-mm-dd') datum
6 FROM temp_utp;
View created.
The rest is easy now:
SQL> SELECT *
2 FROM v_utp
3 WHERE datum < DATE '2020-02-15';
TABLE_NAME PARTITION_NAME DATUM
--------------- --------------- ----------
TEST_PART P0 2020-01-01
SQL>
OK, you'd use two date parameters which would then lead to between in the final query, but that's easy to modify.
Major drawback here is CTAS which creates temp_utp table; you'd have to recreate it as frequently as you add new partitions into the main table. One option is to do it in a scheduled manner, e.g. using a database job (see dbms_job and/or dbms_scheduler documentation if you don't know how) which would schedule a stored procedure which will then use dynamic SQL, i.e. execute immediate to create temp_utp. You don't have to recreate a view - it will become valid as soon as a new temp_utp table is created.
I was trying to find solution for the same problem and found that creating a function to convert high value to date works fine like below
CREATE OR REPLACE FUNCTION get_high_value_date(p_partition_name IN VARCHAR2)
RETURN DATE
IS
l_varchar_date VARCHAR2(4000);
l_date DATE;
BEGIN
EXECUTE IMMEDIATE 'SELECT high_value FROM all_tab_partitions WHERE partition_name = :1 ' INTO l_varchar_date
USING p_partition_name;
EXECUTE IMMEDIATE 'SELECT '||l_varchar_date||' FROM dual' INTO l_date;
RETURN l_date;
EXCEPTION WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ( 'Exc: '||SQLERRM );
RETURN NULL;
END;
Then you can use this for getting date value
SELECT get_high_value_date (partition_name) partition_date, partition_name
FROM all_tab_partitions
WHERE table_name = :table
I am using a SQL script to spool data and load data into XML
I want to check whether I can use variable in the SQL script which get it value from another SQL query.
Something like this:
var1= select count(1) from emp
spool filename
select * from dept where empcnt=var1
Will it work? Is this the best way or any other way of doing it?
If you are using SQLplus, and you only want SQL ( not PLSQL) you can do something like the following, with bind variables:
spool yourFile
variable var1 number
select count(1) into :var1 from dual;
select * from dual where rownum = :var1;
Another way could be with substitution variables:
spool yourFile
column var1 new_value valueFor_var1
select count(1) as valueFor_var1 from dual;
select * from dual where rownum = &var1;
If you can use a PLSQL block, you can do:
spool yourFile
declare
var1 number;
vDummy varchar2(100);
begin
select count(1) into var1 from dual;
select dummy into vDummy from dual where rownum = var1;
dbms_output.put_line(vDummy);
end;
/
I have a table like this:
table_name SQL_statement id_value result
employee select ... where ..= :1 101
customer select ....where ..= :1 903
there will be about 6-8 different tables
the ID_VALUE is supplied as parameter to SQL_Statement
the SQL statement will always return a single row.
the result of "c" above should be stored in result column
I know I can use EXECUTE IMMEDIATE, but that will create hard parsing in SGA.
What kind of BULK technique can be used here ?
If you can assume that the statement always extracts a single value with a fixed alias, you can try the following.
setup:
create table yourTable(table_name varchar2(30), SQL_statement varchar2(100), id_value number, result number);
insert into yourTable
(
select 'employee', 'select :1 *2 as result from dual', 1, null from dual union all
select 'customer', 'select :1 as result from dual', 10, null from dual
);
Given the alias result, you can use a single update statement.
update:
update yourTable
set result = extractvalue(xmltype(dbms_xmlgen.getxml(replace (SQL_statement, ':1', id_value))),'/ROWSET/ROW/RESULT')
result:
SQL> select *
2 from yourTable;
TABLE_NAME SQL_STATEMENT ID_VALUE RESULT
---------- ---------------------------------------- ---------- ----------
employee select :1 *2 as result from dual 1 2
customer select :1 as result from dual 10 10
I would like to have pl-sql like the following:
SELECT ID FROM some-table WHERE
(SELECT MAX(some-expression) FROM another-table = 1) OR
(some-table.ID IN SELECT (SELECT ID FROM one-more-table))
This pseudo-query would select all IDs from some-table if maximum value of some-expression equals 1 (or filter IDs by one-more-table values otherwise)
How do I properly implement this in PL-SQL ?
Thank you in advance!
Please find the below pl-sql:
DECLARE
v_Column1 NUMBER(4);
v_deptno NUMBER(2);
BEGIN
SELECT MAX(A.DEPTNO)
INTO v_deptno
FROM (SELECT DEPTNO FROM DEPT) A;
SELECT A.EMPNO
INTO v_Column1
FROM EMP A
WHERE A.DEPTNO=v_deptno
OR
A.EMPNO IN ( SELECT EMPNO FROM EMP_2);
END;
/
I am using with clause in my function where passing table name as parameter. So want use this table name in the query but giving table doesnot exits.Sample query
with EMP_A as(
select EMPNO, SAL
from EMP
where DEPTNO in (select DEPTNO from P_TABLE_NAME))
select * from EMP;
In your below posted query:
With emp_a as (
select empno,sal
from emp
where deptno in(select deptno from p_table_name)
)
select * from emp;
Firstly, you cannot have dynamic table name in SQL. Object names must be static. You need to (ab)use EXECUTE IMMEDIATE in PL/SQL to make it a dynamic SQL.
Secondly, your CTE and tablename are different. Your CTE is emp_a, while the table you are referring to is emp.
Thirdly, you use the WITH clause, i.e. subquery factoring when you have to use the subquery multiple times. Therefore, the CTE would act as a temporary taboe to hold the subquery resultset.
See the documentation to learn more about EXECUTE IMMEDIATE.
http://docs.oracle.com/cd/B19306_01/appdev.102/b14261/executeimmediate_statement.htm
UPDATE An example
You could use a string variable to assign the dynamic query.
DECLARE
p_table_name VARCHAR2(30);
l_sql VARCHAR2(32767);
l_value. NUMBER;
BEGIN
p_table_name := 'DEPARTMENT';
l_sql := 'WITH clause...' || p_table_name || ' with clause contunied';
EXECUTE IMMEDIATE l_sql INTO l_value;