I've created an anonymous PLSQL block to test and I'm running into an issue with the formatting.
set serveroutput ON
BEGIN
FOR I IN (SELECT DISTINCT do.SUBOBJECT_NAME from dba_objects do WHERE do.object_name='MY_TABLE' AND do.OBJECT_TYPE='TABLE PARTITION') LOOP
dbms_output.put_line(I.subobject_name);
SELECT
t.field
INTO
some_var
FROM
MY_TABLE PARTITION(I.subobject_name) t;
END LOOP;
END;
However I get several compilation errors, which I believe are related to the fact that I.subobject_name is a string. I believe the PARTITION function wants an actual partition symbol(proper term for this?), but I can't give it in this loop.
Is there any kind of casting function that can perform what I'm looking for?
Partition IS NOT A FUNCTION. Partition is keyword
In your context your whole statement is static , thus you CANNOT pass partition name into it; partition name must be specified at compile time.
You can re-create your statement dynamically and then pass partition name in the loop -
a-la you are doing it. Just make sure you will concatenate string and not use bind variables, or your statement at run time won't be parsed and won't run.
Name for the symbol is table partition
Related
UPDATE - I have figured out how to add the stored procedures using code-first migration. 'All' I am wondering now is how I can apply a SELECT * FROM _tempTable1_ UNION SELECT * FROM _tempTable2, and then output this. I was thinking of implementing a third stored procedure which does this but I am not sure whether this is the right thing to do? Much appreciated help:)
I have a table connected to my ASP.NET MVC application, and I have created two stored procedures in SQL Server. Is it possible to implement these stored procedures in a way so that when the table is output, it can be grouped into these stored procedures?
MetBasicCriteria
ALTER PROCEDURE [dbo].[MetBasicCriteria]
-- Add the parameters for the stored procedure here
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT *
FROM dbo.Applications
WHERE NorwegianCitizen=1
AND CurrentlyInUni=1
AND Norwegian=1
AND English=1
END
MetMoreCriteria
ALTER PROCEDURE [dbo].[MetMoreCriteria]
-- Add the parameters for the stored procedure here
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
--EXEC [dbo.MetBasicCriteria]
SELECT *
FROM dbo.Applications
WHERE (Dutch=1
OR OtherCitizenships='Dutch'
OR (Course_bachelor='Political Science' OR Course_bachelor='Economy' OR Course_bachelor='International Relations')
OR (Grade_Bachelor='a' OR Grade_Bachelor='A' OR Grade_Bachelor='b' OR Grade_Bachelor='B'))
--AND MetBasicCriteria=1
END
In other words --> can I use say 3 different stored procedures, save the outcomes of these 3 into different temporary tables, and the using UNION ALL combine them into 1 temporary table?
Thanks in advance
I just take your second procedure code and modify it as a simple POC. I have no idea how you intend to merge or alter or combine the resultsets from your first procedure with that of your second procedure. So I leave that for you. I will also remove the meaningless comments you leave in the code since you seem to be using some sort of template.
ALTER PROCEDURE [dbo].[MetMoreCriteria]
AS
BEGIN
SET NOCOUNT ON;
create table #basic (
id int not null,
...
);
Insert #basic (id) --column list to be defined
EXEC [dbo.MetBasicCriteria];
SELECT appl.id, ...
FROM dbo.Applications as appl
WHERE (appl.Dutch=1
OR appl.OtherCitizenships='Dutch'
OR (appl.Course_bachelor in ('Political Science', 'Economy', 'International Relations'))
OR (appl.Grade_Bachelor in ('A', 'a', 'B', 'b'))
)
AND EXISTS (SELECT * FROM #basic as basic where basic.id = appl.id)
ORDER BY ...;
END
Hopefully I used the parentheses correctly but I can't test it and this edit widget does not auto-count or correct typos or mistakes. Some better coding points:
define an alias for each table and use it for every column reference.
don't use "*" generally as a column list
Using IN rather than a series of logical OR comparisons makes your code more readable and less prone to error
The column list in the SELECT and INSERT statements should be specific - don't use the lazy asterisk to select all columns since you typically don't need them all. If you do need them all, then specify each column to avoid future problems if the schema of the tables changes. The only place where "*" is acceptable as a column list is inside an EXISTS clause.
I also added the ORDER BY clause since the consumer of a resultset usually wants those rows ordered. But perhaps your application/consumer does not care? If so, remove it and save some processor cycles.
Lastly this is just one approach. You could also make your first procedure a view or a UDF since it does nothing complicated. And that comment can also apply to this procedure. Perhaps you are just over-complicating a rather simple set of logic? NB that I omit any error-handling and leave that to you. And let me re-iterate that this is NOT a recommendation. Simply one implementation option.
I have a scenario where I have to perform dynamic assignment to PLSQL type variable.
For example, below is a simple assignment of value to type my_tab_type. fn_get_value(varchar2) function returns value based on string passed.
my_tab_type.table_name.LEAD := fn_get_value('LEAD_ID');
Like these there will be hundreds of assignments. I want to store the mapping (LEAD,LEAD_ID) in a table, fetch these mapped values in a cursor and dynamically create assignment statement.
I want to avoid execute immediate or dbms_sql.execute for each dynamic assignment created as performance will be affected. Please help me come up with an feasible and effective approach.
(You might have a XY-problem.)
You don't tell us the types in your question so I assume nested table of records.
You can't assign a PL/SQL record "dynamically" without using dynamic PL/SQL (i.e. running PL/SQL with execute immediate). This also means that all uses of such "dynamically" created record have to take place dynamically. That's not the way PL/SQL is supposed to be used.
However if you can change your data type to associative array you can archive your goal with a plain boring static well-mannered PL/SQL !
Below you'll find an example that should get you started.
Example
create table so60 (
from_ varchar2(20)
,to_ varchar2(20)
);
insert into so60 values('A_FROM', 'A_TO');
insert into so60 values('B_FROM', 'B_TO');
insert into so60 values('C_FROM', 'C_TO');
declare
-- foo_t is an associative array
type foo_t is table of varchar2(32767) index by varchar2(32767);
v_foo foo_t;
function f(p_x in varchar2) return varchar2 is
begin
return lower(p_x);
end;
begin
-- initialize from table
for i in (select * from so60)
loop
v_foo(i.to_) := f(i.from_);
end loop;
dbms_output.put_line(v_foo('B_TO'));
end;
/
Example run
SQL> #so60
b_from
PL/SQL procedure successfully completed.
SQL>
I have been asked to create an SP which creates temporary table and insert some records.
I am preparing some sample code for the same as mentioned below but the output is not displayed.
create or replace procedure Test
is
stmt varchar2(1000);
stmt2 varchar2(1000);
begin
stmt := 'create global temporary table temp_1(id number(10))';
execute immediate stmt;
insert into temp_1(id) values (10);
execute immediate 'Select * from temp_1';
execute immediate 'Drop table temp_1';
commit;
end;
When i am executing the SP by (Exec Test) desired O/P is not displayed.
I am expecting O/P of "Select * from temp_1" to be displayed. But it is not happening. Please suggest where i am doing wrong.
But i am interesting in knowing why ( execute immediate 'Select * from temp_1';) do not yield any result
For two reasons. Firstly because as #a_horse_with_no_name said PL/SQL won't display the result of a query. But more importantly here, perhaps, the query is never actually executed. This behaviour is stated in the documentation:
If dynamic_sql_statement is a SELECT statement, and you omit both into_clause and bulk_collect_into_clause, then *execute_immediate_statement( never executes.
You would have to execute immediate into a variable, or more likely a collection if your real scenario has more than one row, and then process that data - iterating over the collection in the bulk case.
There is not really a reliable way to display anything from PL/SQL; you can use dbms_output but that's more suited for debugging than real output, and you usually have no guarantee that the client will be configured to show whatever you put into its buffer.
This is all rather academic since creating and dropping a GTT on the fly is not a good idea and there are better ways to accomplish whatever it is you're trying to do.
The block you showed shouldn't actually run at all; as you're creating temp_1 dynamically, the static SQL insert into temp_1 will error as that table does not yet exist when the block is compiled. The insert would have to be dynamic too. Any dynamic SQL is a bit of a warning sign you're maybe doing something wrong, though it is sometimes necessary; having to do everything dynamically suggests the whole approach needs a rethink, as does creating objects at runtime.
Please help me in resolving below issue i am facing, i have to insert data into a table(table name genereted using variable value and table is created already) within FORALL..
Declare
TYPE dept_data_rec IS RECORD
(
Dept_no number,
Dept_name varchar2(100),
Dept_loc Varchar2(20)
);
TYPE nt_dept_data IS TABLE OF dept_data_rec ;
l_dept_data_nt nt_dept_data;
BEGIN
FORALL j IN 1..l_dept_data_nt.COUNT SAVE EXCEPTIONS
EXECUTE IMMEDIATE 'INSERT INTO '||l_get_dept_rec.dept_seq_no||'_Dept_Data VALUES '||
l_dept_data_nt(j);
COMMIT;
while compiling this code i am getting below error:
PLS-00306: wrong number or types of arguments in call to '||'
However when code using actual table name it works
FORALL j IN 1..l_dept_data_nt.COUNT SAVE EXCEPTIONS
INSERT INTO A1_dept_data VALUES
l_dept_data_nt(j);
COMMIT;
Oracle 10g -
In versions of Oracle prior to 11g, you can't use FORALL with EXECUTE IMMEDIATE, only with INSERT, UPDATE, or DELETE.
See http://docs.oracle.com/cd/B13789_01/appdev.101/b10807/13_elems021.htm
It's a special syntax
that reads like a FOR loop but isn't, and
is used by PL/SQL to perform bulked DML operations and only with the exact keyword, not with dynamic SQL or any other code.
Oracle 11g +
In 11g, the restriction on using EXECUTE IMMEDIATE was lifted. See http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/forall_statement.htm
However, the only variables allowed in the string are subscripted elements from a single array in the USING clause.
The documentation is unclear whether you can dynamically "change" the table per row using the FORALL syntax. Remember that the FORALL is used by PL/SQL to perform a bulk DML operation and that needs to go to one table for this to yield any performance benefit.
Best performance solution for the above problem
You should make two levels of arrays, the first defines which table and the second defines the data for that table.
Use an ordinary FOR loop over the table array and inside that loop use the special FORALL syntax to perform all the DML for the one table.
I need a little stored procedure to do the following logic?
procedure_name(seq_name IN varchar2(50), block_count IN int, return_ids OUT)
loop from 1 to block_count
return_ids := select 'seq_name'||.nextVal from dual;
end loop
return return_ids
Basically what I want to do is have a stored procedure that lets me pass in a sequence name, how many IDs I need and return to me the generated listed of IDs that I can use in JAVA. The reason to do this for me is to return a list of IDs that I can use in JAVA and no one else is using those sequence IDs. Where they will be used in some other bulk inserts later down the line. In essence, reserve a block of sequence IDs.
Here is one way to return an array from PL/SQL procedure.
Create a collection type of numbers, initialize it in your procedure and populate it with numbers to return. For example:
create or replace type narray as table of number;
create or replace procedure get_seq_ids(seq_name in varchar2,
block_count in number, return_ids out narray)
as
begin
return_ids := narray();
return_ids.extend(block_count);
for i in 1 .. block_count
loop
execute immediate 'select ' || seq_name || '.nextval from dual'
into return_ids(i);
end loop;
end;
/
I'd be concerned over the logic that requires IDs to be generated before records are inserted into the database.
Alternatively you may want to consider inserting rows first, selecting the ids from the rows, and then using an update statement to do your bulk operation. This however is still not as preferable as having the Java code not depend on ids until after the actual information is ready to be inserted.
You can push your information into XML (or any other data format your database can understand) and then call a stored procedure to do the bulk inserts.
Another option may be to use the RETURNING clause to return the sequence values automatically after the insert.
IMHO, the best thing you can do is just referencing sequence_name.nextval right in your INSERT INTO, in the VALUES clause.
You said you want to avoid others using the same IDs. Referencing this site:
The sequence (or Oracle, for that matter) ensures that no other session or other call to nextval within the same session gets the same number from the sequence.
So, the uniqueness of a sequence' numbers are guaranteed in Oracle.
Here's what I do for the Java application I support (which also uses bulk inserts into deeply hierarchical tables)
PROCEDURE get_nextvals
(
p_values OUT SYS_REFCURSOR,
p_count IN PLS_INTEGER
)
IS
-- return the next p_count values from the PK sequence
BEGIN
OPEN p_values FOR
SELECT
<schema>.<sequence>.nextval
FROM
dual
CONNECT BY
LEVEL <= p_count
;
END;
It was easier to just pass a cursor out to java than having the app use a table type defined in the DB.