how to select table name from a query in oracle - oracle

select SRI_PACK_INDEX_VAL
from (select TABLE_NAME
from all_tables
where table_name like '%SE_RAO_INDEX_04_12_2015%');
Hi,
The above query is not working in oracle.Can anyone help me to solve this

You can't dynamically select from tables like that as Oracle's parser can't figure it out before fetch to know what tables it needs to read. If you want to do a dynamic fetch you need to do dynamic sql. Something like (forgive any minor syntax errors-I'm away from my database at the moment
declare
index_val number; -- i am assuming it is a number, change as appropriate
begin
-- I'm doing this in a loop if there are multiple tables that meet your name mask.
-- If there will only be one, then just select it into a variable and use it that way instead
for tab_name in (SELECT TABLE_NAME
from all_tables
where table_name like '%SE_RAO_INDEX_04_12_2015%')
loop
execute immediate 'select SRI_PACK_INDEX_VAL from '||tab_name.table_name
into index_val;
-- do your stuff with it
end loop;
end;
now, that works if the select only brings back one row. if you are bringing back multiple rows, then you have to handle it differently. In that case you will either want to EXECUTE IMMEDIATE a pl/sql block and embed your processing of the results there, or execute immediate bulk collect into an array, an example on how to do that is here.

select
extractvalue(
xmltype(
dbms_xmlgen.getxml(
'select sri_pack_index_val a from '||owner||'.'||table_name
)
), '/ROWSET/ROW/A'
) sri_pack_index_val
from all_tables
where table_name like '%SE_RAO_INDEX_04_12_2015%';
This query is based on a post from Laurent Schneider. Here's a SQLFiddle demonstration.
This is a neat trick to create dynamic SQL in SQL, but it has a few potential issues. It's probably not as fast as the typical dynamic SQL approach as shown by Michael Broughton. And I've ran into some weird bugs when trying to use this for large production queries.
I recommend only using this approach for ad hoc queries.

Related

Simple query with variables as parameters in SQL Developer

Ok I have seen a bunch of questions attempt to answer this but all the answers seem horribly complicated or don't actually answer the question.
Is there a way to simply do something like this:
DECLARE
v_some_variable VARCHAR2(10) := 'Find Me';
BEGIN
select column1, column2, column3
from someTable st
where st.columnTarget = v_some_variable
END;
And have this displayed in a grid?
This is so simple on SQL server and is maddeningly NOT simple in Oracle.
Every permutation or combination I try from various answers either does not work and gives an error, requires defining every column and datatype from the output prior to running the query or does not output to the grid and you have to roll your own text output. All of these are very poor solutions in my opinion. What am I missing? Oracle can't be this bad can it?
It is arguably possible to print query output from PL/SQL by returning a refcursor or printing it or something. Although like you, I don't find any of those solutions very easy or intuitive.
I do a lot of SQL development in Oracle, and generally - if you want to get a result grid back, don't use a PL/SQL block. Just use plain SQL statements.
select column1, column2, column3
from someTable st
where st.columnTarget = :v_some_variable; -- when you run this, it'll prompt for the value
If you don't like the prompt popup, you can also declare bind variables in SQL*Plus (the backend behind SQL Developer) like this:
var v_some_variable VARCHAR2(10)
exec :v_some_variable := 'Find Me';
select column1, column2, column3
from someTable st
where st.columnTarget = :v_some_variable;
If you run that as a script (F5), it won't prompt you for a value. Note that this still isn't using a PL/SQL block.
If you just want to use SQLPlus to write a query and execute it to see the results in a grid, then the simplest way to go is the following:
Start by defining the parameter:
define p = 1000;
Then you can use it:
select *
from table1 t1
join etc on etc.id = t1.etc_id
where etc.p = &p
You need to learn to do things the Oracle way, which is not the same as the SQL Server way. It's also incredibly simple in Oracle - if you know the way Oracle does it.
First we create the database table and add some data.
create table some_table (first varchar2(10), second varchar2(10));
insert into some_table values ('First', 'Find me');
Now for the PL/SQL.
declare
v_var varchar2(10);
v_var2 varchar2(10);
begin
v_var := 'Find me';
select first into v_var2 from some_table where second = v_var;
end;
/
Compare my PL/SQL with yours. Hint: Note that mine contains into
Refer to this db fiddle

How to merge two or more unknown tables into one table in Oracle

I'm trying to merge x number of identical tables into one table. The reason we did this is because we want to have, for example 50 columns per table in the database. Tables are created externally via SCADA software called Ignition.
Every time table is created in the database, we want to view the data as one regardless of how many tables the data came from provided that all tables will have the same first three letters for example, Table_1, Table_2, Table_3....so on.
The query/procedure we want to have is like:
step 1: since the tables are unknown we can't do it by simple union, merge insert etc., so we must find all table_name with 'Table' prefix.
SELECT table_name FROM all_tables where table_name like 'Table%'
step 2: this is where the magic begins, it should query one by one each listed table_name in first step, then collect all the data and merge into one table or view.
I tried many ways using PL/SQL but don't know how to proceed with step 2. Is there any way to get what we want to achieve? any possible solutions would be great! :)
Thanks!
Assuming that you are selecting only the common columns from all the tables, you could create a dynamic view, which does a UNION ALL of all the tables starting with "Table" prefix.
DECLARE
v_select CLOB;
BEGIN
SELECT
LISTAGG('SELECT col1,col2,col3 FROM ' || table_name,
' UNION ALL ' || CHR(10) ) WITHIN GROUP
(
ORDER BY table_name
)
INTO v_select
FROM user_tables WHERE table_name LIKE 'TABLE_%';
IF
v_select IS NOT NULL
THEN
EXECUTE IMMEDIATE ' CREATE OR REPLACE VIEW v_all_tabs as ' || v_select;
END IF;
END;
/
Then select from the view by executing the above block(or put it into a procedure ) each time there's a new table added.
select * from v_all_tabs;
If there's a chance of your SQL string exceeding 4000 characters, instead of a single LISTAGG, you could append each select through a simple assignment in PL/SQL in a cursor loop.

How to find the column used in the dynamic query without executing whole query

Problem Statement
I have a dynamic SQL which i need to store in a table ,but before
storing the sql i need to validate the sql with the list of columns
stored in another table.
Without executing the query , is it possible to find name of columns in the select ?
Approach1
Only option i can think of is ,try to use explain plan of the query and read the meta data in the data dictionaries table .But unfortunately i am not able to find any table with such data.Please let me know if you know such views?
Approach2
Use DBMS_SQL.DESCRIBE_COLUMNS package to find the column name ,but i believe this will execute the whole query.
You don't need to execute the query to get the column names, you just need to parse it; e.g. as a simple example:
set serveroutput on
declare
l_statement varchar2(4000) := 'select * from employees';
l_c pls_integer;
l_col_cnt pls_integer;
l_desc_t dbms_sql.desc_tab;
begin
l_c := dbms_sql.open_cursor;
dbms_sql.parse(c=>l_c, statement=>l_statement, language_flag=>dbms_sql.native);
dbms_sql.describe_columns(c=>l_c, col_cnt=>l_col_cnt, desc_t=>l_desc_t);
for i in 1..l_col_cnt loop
dbms_output.put_line(l_desc_t(i).col_name);
end loop;
dbms_sql.close_cursor(l_c);
exception
when others then
if (dbms_sql.is_open(l_c)) then
dbms_sql.close_cursor(l_c);
end if;
raise;
end;
/
which outputs:
EMPLOYEE_ID
FIRST_NAME
LAST_NAME
EMAIL
PHONE_NUMBER
HIRE_DATE
JOB_ID
SALARY
COMMISSION_PCT
MANAGER_ID
DEPARTMENT_ID
PL/SQL procedure successfully completed.
You can do whatever validation you need on the column names inside the loop.
Bear in mind that you'll only see (and validate) the column names or aliases for column expressions, which won't necessarily reflect the data that is actually being retrieved. Someone could craft a query that pulls any data from anywhere it has permission to access, but then gives the columns/expression aliases that are considered valid.
If you're trying to restrict access to specific data then look into other mechanisms like views, virtual private database, etc.
DBMS_SQL.PARSE will not execute a SELECT statement but it will execute a DDL statement. If the string 'select * from employees' is replaced by 'drop table employees' the code will fail but the table will still get dropped.
If you're only worried about the performance of retrieving the metadata then Alex Poole's answer will work fine.
If you're worried about running the wrong statement types then you'll want to make some adjustments to Alex Poole's answer.
It is surprisingly difficult to tell if a statement is a SELECT instead of something else. A simple condition checking that the string begins with select will work 99% of the time but getting from 99% to 100% is a huge amount of work. Simple regular expressions cannot keep up with all the different keywords, comments, alternative quoting format, spaces, etc.
/*comment in front -- */ select * from dual
select * from dual
with asdf as (select * from dual) select * from asdf;
((((((select * from dual))))));
If you need 100% accuracy I recommend you use my open source PLSQL_LEXER. Once installed you can reliably test the command types like this:
select
statement_classifier.get_command_name(' /*comment*/ ((select * from dual))') test1,
statement_classifier.get_command_name('alter table asdf move compress') test2
from dual;
TEST1 TEST2
----- -----
SELECT ALTER TABLE

Delete duplicated records using procedure in Oracle/PLSQL

As the title, I wanna create a procedure in Oracle/PLSQL to delete rows which share same values in some columns. I know how to implement it using Query, but how to do it using procedure? Do I have to use any loop? I am very new to PLSQL
Please help, thank you a lot!
If you want a simple procedure to delete from a particular table you can use the below piece of code:
CREATE OR REPLACE PROCEDURE DELETE_DUPLICATE AS
BEGIN
FOR I IN (SELECT TAB.A, TAB.B, MIN(ROWID) RID
FROM DUPLICATE_TABLE TAB
GROUP BY TAB.A, TAB.B
HAVING COUNT(*) > 1) LOOP
DELETE FROM DUPLICATE_TABLE TAB
WHERE I.RID <> TAB.ROWID
AND TAB.A = I.A
AND TAB.B = I.B;
COMMIT;
END LOOP;
END;
Here DUPLICATE_TABLE is the table having duplicate values. We are deleting rows having same values in columns A and B.
Hey. As per your question, although it is not advicable to create
procedure for this simpler task which can be easily done via Pure SQL.
But if its really imp to make it as a stored procedure then i would
suggest to use PURE SQL logic than using any kind of loop as there
will be Context Switching which will have a toll on the database.
Below is a snippet which i think will be useful also incorporated
Analytical function to suffice your issue. Let me know if it helps.
CREATE OR REPLACE PROCEDURE Dup_DELETE
AS
BEGIN
DELETE
FROM EMP
WHERE EMP.ROWID IN
-- Assuming that i am trying to segregate the duplicate values on Empno and ename
(SELECT A.ROWID
FROM
(SELECT ROW_NUMBER() OVER(PARTITION BY EMPNO,ENAME ORDER BY JOB DESC) RNK,
empno,
ename,
rowid
FROM EMP
)A
WHERE A.RNK <> 1
);
END;
Just put your SQL statement in a procedure. There's no rule that says you have to change the approach because it's PL/SQL. For example,
create or replace procedure dedupe_sometable
as
begin
delete sometable
where rowid in
( select lag(rowid) over (partition by id order by null)
from sometable );
end dedupe_sometable;
Add logging etc as needed.
(Ideally this would be within a package and not a standalone procedure.)
If you know how to do it in SQL, better to do it in sql. PL/SQL should be used only when you cannot write specific task in SQL statement or if there is performance issues in the query and can improve by writing the logic in PL/SQL (second scenario is very rare).
If you want to write PL/SQL procedure to parameterize so that any table can be passed to delete the duplicates from it, then it makes sense. You need to dynamically generate delete statement in the procedure and execute using execute immediate.
If your intention is to learn PL/SQL, then it is programming language and you need to spend some time as if you are learning new programming language.
It is not recommended to use plsql for something that can be done using plain sql.
Whenever you have a combination of sql and plsql, you are switching between sql and plsql engine. So it does not make sense to incur this overhead without proper requirement.
If for some reason there is still a need for doing this, you can atleast implement bulk delete to reduce some overhead. Please refer to the code below to find out how to do that -
DECLARE
TYPE t_del IS TABLE OF VARCHAR2(100);
l_del t_del;
CURSOR c IS
SELECT MIN(ROWID) RID
FROM test_tbl TAB
GROUP BY TAB.age, TAB.gender
HAVING COUNT(*) > 1;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO l_del;
EXIT WHEN l_del.COUNT = 0;
FORALL i IN l_del.FIRST..l_del.last
DELETE FROM test_tbl WHERE ROWID = l_del(i);
END LOOP;
END;

How do I check for a IN condition against a dynamic list in Oracle?

EDIT: changed the title to fit the code below.
I'm trying to retrieve a list of acceptable values from an Oracle table, then performing a SELECT against another while comparing some fields against said list.
I was trying to do this with cursors (like below), but this fails.
DECLARE
TYPE gcur IS REF CURSOR;
TYPE list_record IS TABLE OF my_table.my_field%TYPE;
c_GENERIC gcur;
c_LIST list_record;
BEGIN
OPEN c_GENERIC FOR
SELECT my_field FROM my_table
WHERE some_field = some_value;
FETCH c_GENERIC BULK COLLECT INTO c_LIST;
-- try to check against list
SELECT * FROM some_other_table
WHERE some_critical_field IN c_LIST;
END
Basically, what I'm trying to do is to cache the acceptable values list into a variable, because I will be checking against it repeatedly later.
How do you perform this in Oracle?
We can use collections to store values to suit your purposes, but they need to be declared as SQL types:
create type list_record is table of varchar2(128)
/
This is because we cannot use PL/SQL types in SQL statements. Alas this means we cannot use %TYPE or %ROWTYPE, because they are PL/SQL keywords.
Your procedure would then look like this:
DECLARE
c_LIST list_record;
BEGIN
SELECT my_field
BULK COLLECT INTO c_LIST
FROM my_table
WHERE some_field = some_value;
-- try to check against list
SELECT * FROM some_other_table
WHERE some_critical_field IN ( select * from table (c_LIST);
END;
"I see that you still had to perform a
SELECT statement to populate the list
for the IN clause."
If the values are in a table there is no other way to get them into a variable :)
"I'm thinking that there's a
significant performance gain using
this over a direct semi-join"
Not necessarily. If you're only using the values once then the sub-query is certainly the better approach. But as you want to use the same values in a number of discrete queries then populating a collection is the more efficient approach.
In 11g Enterprise Edition we have the option to use result set caching. This is a much better solution, but one which is not suited for all tables.
Why pull the list instead of using a semi-join?
SELECT *
FROM some_other_table
WHERE some_critical_field IN (SELECT my_field
FROM my_table
WHERE some_field = some_value);

Resources