When I call the function, it works in SQL*Plus but doesn't work in PowerBI.
I opened PowerBI> Get Data> Oracle> Entered server name> Went to advanced options to enter sql below
SELECT * FROM TABLE(TESTPOWERBI);
Error: We encountered an error while trying to connect. SQL command not properly ended.
Anyone have experience in solving this?
DROP TYPE VW_PEOPLE_TABLE;
DROP TYPE VW_PEOPLE_TYPE;
CREATE OR REPLACE TYPE VW_PEOPLE_TYPE AS OBJECT(NAME VARCHAR2(70), ALIAS VARCHAR2(90));
/
CREATE OR REPLACE TYPE VW_PEOPLE_TABLE AS TABLE OF VW_PEOPLE_TYPEL
/
CREATE OR REPLACE FUNCTION TESTPOWERBI RETURN VW_PEOPLE_TABLE
PIPELINED
AUTHID CURRENT_USER
AS
VWT VW_PEOPLE_TABLE;
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
SELECT
VW_PEOPLE_TYPE(NAME, ALIAS)
BULK COLLECT
INTO VWT
FROM MYDATABASE;
FOR i in 1 .. VWT.COUNT
LOOP
PIPE ROW (VW_PEOPLE_TYPE(VWT(i).NAME, VWT(i).ALIAS));
END LOOP;
END TESTPOWERBI;
/
GRANT EXECUTE ON TESTPOWERBI TO PUBLIC;
You mixed your delimiters ; and /.
Clean it up and add one at the end behind GRANT EXECUTE ON TESTPOWERBI TO PUBLIC.
In addition you got a typo: for i in 1 .. vwt.count only two dots.
This should work:
DROP TYPE VW_PEOPLE_TABLE;
DROP TYPE VW_PEOPLE_TYPE;
CREATE OR REPLACE TYPE VW_PEOPLE_TYPE AS OBJECT
(
NAME VARCHAR2 (70),
ALIAS VARCHAR2 (90)
);
CREATE OR REPLACE TYPE VW_PEOPLE_TABLE AS TABLE OF VW_PEOPLE_TYPEL;
CREATE OR REPLACE FUNCTION TESTPOWERBI
RETURN VW_PEOPLE_TABLE
PIPELINED
AUTHID CURRENT_USER
AS
VWT VW_PEOPLE_TABLE;
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
SELECT VW_PEOPLE_TYPE (NAME, ALIAS)
BULK COLLECT INTO VWT
FROM MYDATABASE;
FOR i IN 1 .. VWT.COUNT
LOOP
PIPE ROW (VW_PEOPLE_TYPE (VWT (i).NAME, VWT (i).ALIAS));
END LOOP;
END TESTPOWERBI;
/
GRANT EXECUTE ON TESTPOWERBI TO PUBLIC;
Here some discussion about delimiters:
When do I need to use a semicolon vs a slash in Oracle SQL?
Related
I have a successfully compiled procedure under SYSTEM schema.
create or replace procedure get_file_list as
ns varchar2(1024);
cursor c_my_directory is
select directory_name, directory_path from all_directories where directory_path like '/home/oracle/EDI%';
begin
-- before generating the file list, the temporary table is deleted
delete from edi.temp_EDI_file_list;
for each_directory in c_my_directory loop
-- it reads the contents of my_directory into a table called X$FRBMSFT
sys.dbms_backup_restore.searchfiles (each_directory.directory_path, ns);
for each_file in (select fname_krbmsft as name from X$KRBMSFT) loop
insert into edi.temp_edi_file_list
values (each_directory.directory_name, each_file.name);
end loop;
end loop;
commit;
exception
when others then
raise_application_error (-20001,sqlcode || ' ' || sqlerrm);
end get_file_list;
[.. it was created under SYSTEM schema because I am not allowed to grant select on X$FRBMSFT to user "edi"].
I granted execute privilegies to user "edi" on this procedure.
[.. connected as SYSTEM, role SYSDBA, I executed grant execute on system.get_file_list to EDI;]
When I am trying to execute the procedure (execute system.get_file_list;) with user "edi" it return the error
PLS-00905: object SYSTEM.GET_FILE_LIST is invalid
Can someone, please, give me a hint about what am I doing wrong?
Thank you,
In the end I managed to create the procedure, with some help from the link provided by #APC.
... conected as SYSTEM
create or replace view file_list as select fname_krbmsft from X$KRBMSFT readonly;
create or replace procedure searchfiles (pattern in out nocopy varchar2, ns in out nocopy varchar2) authid definer as
begin
dbms_backup_restore.searchfiles(pattern, ns);
end searchfiles;
GRANT SELECT ON FILE_LIST TO EDI;
GRANT EXECUTE ON SEARCHFILES TO EDI;
... conected as EDI
create or replace procedure get_file_list as
ns varchar2(1024);
cursor c_my_directory is
select directory_name, directory_path from all_directories where directory_path like '/home/oracle/EDI%';
begin
-- before generating the file list, the temporary table is deleted
delete from edi.temp_EDI_file_list;
for each_directory in c_my_directory loop
-- it reads the contents of all directories into a table called X$FRBMSFT via procedure SEARCHFILES
sys.SEARCHFILES (each_directory.directory_path, ns);
-- it interogate the X$FRBMSFT via file_list view
for each_file in (select fname_krbmsft as name from sys.file_list) loop
insert into temp_edi_file_list
values (each_directory.directory_name, each_file.name);
end loop;
end loop;
commit;
exception
when others then
raise_application_error (-20001,sqlcode || ' ' || sqlerrm);
end get_file_list;
The difference was made by the way they were called the objects created with user SYSTEM. They were called with SYS.xxx instead of SYSTEM.xxx
I have a function in PLSQL and trying to call it from PowerBI which I finally got it to work.
Currently to return a tables columns, I need to manually specify the types. Is there a way to simply edit this code to return all columns of any given table?
Meaning that given this script, I only need to change the name of the table in the script and it should give me all the data.
I have to use PLSQL for this as there are some Auth protection to query tables which I cant simply query from PowerBI.
Drop Type VW_PEOPLE_TABLE;
Drop Type VW_PEOPLE_TYPE;
Drop Function TESTPOWERBI;
Drop Public Synonym PBI;
CREATE TYPE VW_PEOPLE_TYPE AS Object
{
Name VarChar2(70).
ALIAS VarChar2(90)
};
/
Create Type VW_PEOPLE_TABLE as TABLE OF VW_PEOPLE_TYPE;
/
Grant execute on VW_PEOPLE_TABLE TO public;
Create Function TESTPOWERBI
Return VW_PEOPLE_TABLE Pipelined AUTHID current_user AS VWT VW_PEOPLE_TABLE;
Pragma AUTONOMOUS_TRANSACTION;
Begin
Select VW_PEOPLE_TYPE(NAME,ALIAS)
Bulk COLLECT INTO VWT
FROM mytable;
FOR I IN 1 .. VWT.count
LOOP
PIPE ROW(VW_PEOPLE_TYPE(VWT(I).NAME, VWT(I).ALIAS));
END LOOP;
END TESTPOWERBI;
/
create public synonym PBI for TESTPOWERBI;
Grant Execute on PBI to public;
EDIT : as per this powerBI community post :
You should be able to achieve it using a procedure like this.
CREATE OR REPLACE PROCEDURE get_query_result (
p_tab_name VARCHAR2,
p_rc OUT SYS_REFCURSOR
) AS
BEGIN
OPEN p_rc FOR 'select * FROM '
|| p_tab_name;
END;
/
In Oracle environment, If you are using Oracle 12c and above, you may use DBMS_SQL.RETURN_RESULT with a dynamic REFCURSOR.
CREATE OR REPLACE PROCEDURE get_query_result (
p_tab_name VARCHAR2
) AS
rc SYS_REFCURSOR;
BEGIN
OPEN rc FOR 'select * FROM '
|| p_tab_name;
dbms_sql.return_result(rc);
END;
/
Get the query result for any table using
EXEC get_query_result('EMPLOYEES');
Here is the definition of the stored procedure:
CREATE OR REPLACE PROCEDURE usp_dropTable(schema VARCHAR, tblToDrop VARCHAR) IS
BEGIN
DECLARE v_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_cnt
FROM all_tables
WHERE owner = schema
AND table_name = tblToDrop;
IF v_cnt > 0 THEN
EXECUTE IMMEDIATE('DROP TABLE someschema.some_table PURGE');
END IF;
END;
END;
Here is the call:
CALL usp_dropTable('SOMESCHEMA', 'SOME_TABLE');
For some reason, I keep getting insufficient privileges error for the EXECUTE IMMEDIATE command. I looked online and found out that the insufficient privileges error usually means the oracle user account does not have privileges for the command used in the query that is passes, which in this case is DROP. However, I have drop privileges. I am really confused and I can't seem to find a solution that works for me.
Thanks to you in advance.
SOLUTION:
As Steve mentioned below, Oracle security model is weird in that it needs to know explicitly somewhere in the procedure what kind of privileges to use. The way to let Oracle know that is to use AUTHID keyword in the CREATE OR REPLACE statement. If you want the same level of privileges as the creator of the procedure, you use AUTHID DEFINER. If you want Oracle to use the privileges of the user currently running the stored procedure, you want to use AUTHID CURRENT_USER. The procedure declaration looks as follows:
CREATE OR REPLACE PROCEDURE usp_dropTable(schema VARCHAR, tblToDrop VARCHAR)
AUTHID CURRENT_USER IS
BEGIN
DECLARE v_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_cnt
FROM all_tables
WHERE owner = schema
AND table_name = tblToDrop;
IF v_cnt > 0 THEN
EXECUTE IMMEDIATE('DROP TABLE someschema.some_table PURGE');
END IF;
END;
END;
Thank you everyone for responding. This was definitely very annoying problem to get to the solution.
Oracle's security model is such that when executing dynamic SQL using Execute Immediate (inside the context of a PL/SQL block or procedure), the user does not have privileges to objects or commands that are granted via role membership. Your user likely has "DBA" role or something similar. You must explicitly grant "drop table" permissions to this user. The same would apply if you were trying to select from tables in another schema (such as sys or system) - you would need to grant explicit SELECT privileges on that table to this user.
You should use this example with AUTHID CURRENT_USER :
CREATE OR REPLACE PROCEDURE Create_sequence_for_tab (VAR_TAB_NAME IN VARCHAR2)
AUTHID CURRENT_USER
IS
SEQ_NAME VARCHAR2 (100);
FINAL_QUERY VARCHAR2 (100);
COUNT_NUMBER NUMBER := 0;
cur_id NUMBER;
BEGIN
SEQ_NAME := 'SEQ_' || VAR_TAB_NAME;
SELECT COUNT (*)
INTO COUNT_NUMBER
FROM USER_SEQUENCES
WHERE SEQUENCE_NAME = SEQ_NAME;
DBMS_OUTPUT.PUT_LINE (SEQ_NAME || '>' || COUNT_NUMBER);
IF COUNT_NUMBER = 0
THEN
--DBMS_OUTPUT.PUT_LINE('DROP SEQUENCE ' || SEQ_NAME);
-- EXECUTE IMMEDIATE 'DROP SEQUENCE ' || SEQ_NAME;
-- ELSE
SELECT 'CREATE SEQUENCE COMPTABILITE.' || SEQ_NAME || ' START WITH ' || ROUND (DBMS_RANDOM.VALUE (100000000000, 999999999999), 0) || ' INCREMENT BY 1'
INTO FINAL_QUERY
FROM DUAL;
DBMS_OUTPUT.PUT_LINE (FINAL_QUERY);
cur_id := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.parse (cur_id, FINAL_QUERY, DBMS_SQL.v7);
DBMS_SQL.CLOSE_CURSOR (cur_id);
-- EXECUTE IMMEDIATE FINAL_QUERY;
END IF;
COMMIT;
END;
/
you could use "AUTHID CURRENT_USER" in body of your procedure definition for your requirements.
Alternatively you can grant the user DROP_ANY_TABLE privilege if need be and the procedure will run as is without the need for any alteration. Dangerous maybe but depends what you're doing :)
I want to create a simple Oracle Stored procedure on SQL Developer that will return some records on a simple select query. I do not want to pass in any parameter, but I just want the Records to be returned back from the procedure into a result set -> a suitable variable.
I have been trying to use the following syntax :
create or replace PROCEDURE Getmarketdetails2(data OUT varchar2)
IS
BEGIN
SELECT *
into data
from dual;
END Getmarketdetails2;
But it gives me an error while I try to execute with the following exec statement -->
Declare a Varchar2;
exec Getmarketdetails2(a);
Error: PLS-00103: Encountered the symbol "end-of-file" when expecting "something else".
Cause: Usually a PL/SQL compilation error.
Appreciate if anyone can help me out of this long pending situation! I have tried enough to find a basic guide to create a simple Oracle stored procedure and execute it in SQL Developer, but none of them answer to the point!!
You want:
DECLARE
a VARCHAR2(4000); -- Give it a size
BEGIN -- Begin the anonymous PL/SQL block
Getmarketdetails2(a); -- Call the procedure
DBMS_OUTPUT.PUT_LINE( a ); -- Output the value
END; -- End the anonymous PL/SQL block
/ -- End the PL/SQL statement
or:
VARIABLE a VARCHAR2(4000); -- Create a bind variable
EXEC Getmarketdetails2(:a); -- Execute the procedure using the bind variable
PRINT a -- Print the bind variable
Assuming an up-to-date Oracle version, you can use dbms_sql.return_result()
create or replace PROCEDURE Getmarketdetails2
IS
c1 SYS_REFCURSOR;
BEGIN
OPEN c1 FOR
SELECT *
from dual;
DBMS_SQL.RETURN_RESULT(c1);
END Getmarketdetails2;
/
Then simply run
exec Getmarketdetails2
The only drawback is that SQL Developer only displays the result as text, not as a proper result grid.
This is how I return a cursor in Oracle
PROCEDURE GetAllData (P_CURSOR OUT SYS_REFCURSOR)
IS
BEGIN
OPEN P_CURSOR FOR
SELECT *
FROM TABLE ;
END GetAllData ;
Declare a Varchar2;
exec Getmarketdetails2(a);
Your procedure is ok;
Instead of above query, use below query to run sp:
Declare
a Varchar2(10);
Begin
Getmarketdetails2(a);
End;
I have a stored procedure proc1 without parameters. I want to extract data from this stored procedure. How can I get that? Could you help me?
Stored procedure:
create procedure proc1
as
begin
select e_id, e_nm, e_sal
from emp
where e_id like 'e%';
end proc1;
You can do this in Oracle 12.1 or above:
create or replace procedure demo
as
rc sys_refcursor;
begin
open rc for select * from dual;
dbms_sql.return_result(rc);
end demo;
This requires an Oracle 12.1 or later client/driver to handle the implicit result set.
For more details, see Implicit Result Sets in the Oracle 12.1 New Features Guide, Tom Kyte's Blog, Oracle Base etc.
Here's one possible solution:
Declaration:
create procedure proc1 (emp_row IN OUT emp%rowtype)
as
begin
select * --e_id, e_nm, e_sal
into emp_row
from emp
where e_id like 'e%';
end proc1;
Use case:
DECLARE
l_emp_row emp%rowtype;
BEGIN
proc1(l_emp_row);
-- Here you can access every column of table "emp", like so:
-- dbms_output.put_line('e_id: ' || to_char(l_emp_row.e_id));
-- dbms_output.put_line('e_nm: ' || to_char(l_emp_row.e_nm));
-- dbms_output.put_line('e_sal: ' || to_char(l_emp_row.e_sal));
END;
Is there anything special that you need to get out of the Procedure?
Cheers
you create a view this query. (recomended)
Oracle procedure is not return any data. So, you do not see any result. Your result get buffer but not print screen. if You need a procedure, insert all data another table.
create procedure proc1
as
begin
insert into new_table select e_id, e_nm, e_sal from emp where e_id like 'e%';
end proc1;
Another way; You create a function. Because function outputs and inputs. This function;
for example Create an Oracle function that returns a table