Optional Data Input into Spotfire Text Area parameters got from a Oracle Function - oracle

I have created a Function from Oralce DB and used it as input to Spotfire report. The reason why i used a function instead of view is because the parameters had some complex logic operations and i was not able to get that done in a view.
Coming to the report i have built. Currently i am using 2 parameters in the function and i am taking both the values from Spotfire text area in Data on Demand mode. So i have set up two 'Input Field' for getting the data. Issue is that unless i enter values for both parameters i wont get the output. My requirement is that i need to add few more parameters for the report which i can but i need to set up the function and the settings in Spotfire such that if 5 parameters are there , if users enters one value for just one parameter report should run for that parameter. I know that parameters set up with complex logis are there but also i need to add additional ones and make sure it works properly. I dont know about proc in case a solution is achievable using that.
I cannot go back to use a view since that wont give me the desired result. Need any ideas or suggestions to implement it. Let me know in case i need to post additional information.
This is how i implemented in oracle :
create or replace function Function_test(p1 varchar2='',p2 varchar2='',p3 varchar2)
return SYS_REFCURSOR as
my_cursor SYS_REFCURSOR;
begin
open my_cursor for
select distinct
x.c1
x.c2
x.c3
from x
where x.c1=p3
and (p1='' or x.c2=p1)
and (p2='' or x.c3=p2);
return my_cursor;
end;

Using a STORED PROCEDURE or a TABLE VALUED FUNCTION will achieve similar results here and you can probably use either one. I understand you can't use a VIEW because VIEWS can't have parameters. I prefer PROCEDURES over VIEWS anyway while working with SPOTFIRE because if you need to add a column or parameter to the PROCEDURE, your INFORMATION LINK on the PROCEDURE will in inherit these changes, whereas using an INFORMATION LINK on a VIEW, the change will not cascade down and you'll need to recreate the INFORMATION LINK all together.
For your case there are a couple of things I would suggest. Since you are trying to make some of the parameters optional, you need to code your FUNCTION or PROCEDURE to accept this. For example, assume one parameter is #param1 and this is accepting input from {$propertyControl1}. To make this optional, you need to instruct users to leave the property control blank if they don't want it to be used to limit the results. Meanwhile, in your FUNCTION or PROCEDURE you need to default it's value to ''. Here is what it could look like using SQL Server, but it should be similar for ORACLE
CREATE FUNCTION dbo.MyFunction (#param1 VARCHAR(256) = '') --default value is blank. Remember don't use NULL since your property control can't be NULL
RETURNS
#returnTBL TABLE(
Column1 datetime,
Column2 int,
Column3 varchar(16))
AS
BEGIN
INSERT INTO #returnTBL (Column1, Column2, Column3)
SELECT
c.C1
c.C2
c.C3
FROM Table2
WHERE #param1 = '' OR c.C3 = #param1 --this returns all rows if the user passes in blank, and filters on it if they pass in a value
RETURN
END
Or, similarly, here is the same logic using a STORED PROCEDURE in SQL Server
CREATE PROCEDURE dbo.MyProcedure(#param1 VARCHAR(256) = '') --default value is blank. Remember don't use NULL since your property control can't be NULL
AS
SELECT
c.C1
c.C2
c.C3
FROM Table2
WHERE #param1 = '' OR c.C3 = #param1 --this returns all rows if the user passes in blank, and filters on it if they pass in a value
GO
Lastly, since you have multiple parameters, I wouldn't have the data on demand automatically refresh. I will have the users click the refresh button to retrieve the data. This will prevent the FUNCTION from being executed each time an individual parameter is change. If a user needs to change 3 of the 5 parameters, you really only want it executed once, versus three times.

Related

PL/SQL: ORA 01422 fetch returns more than requested number of rows

I am developing an order transaction where a user can order a product. Once they clicked the 'add to cart' button, it will be able to save on the database in how many times they want with the same order id. Order id is like a transaction id.
My problem is that whenever I want to display the items that customer ordered, it displays an error or ORA 01422. How can I resolve this error?
Here is my code
DECLARE
order_item_id NUMBER;
BEGIN
order_item_id := :MOTOR_PRODUCTS_ORDER.M_ORDERID;
SELECT MOTOR_ID,
MOTOR_QTY_PURCHASED,
UNITPRICE
INTO :MOTOR_ORDER_ITEMS.MOTOR_ID,
:MOTOR_ORDER_ITEMS.MOTOR_QTY_PURCHASED,
:MOTOR_ORDER_ITEMS.UNITPRICE
FROM MOTOR_ORDERS
WHERE motor_order_id = order_item_id;
END;
As krokodilo says, this error is caused because your query returns multiple rows. Depending on what you want to do, you have a couple of options.
If you want multiple values then either use a loop and process them one row at a time (if you are going to be performing a dml operation use bulk collect). If you only want a single row then narrow your result set down with an extra where clause, or use MAX to ensure you only get one value back.
If there is more than one row which will be returned from a query you'll need to use a cursor. One way to do this is with a cursor FOR loop:
DECLARE
order_item_id NUMBER;
BEGIN
order_item_id := :MOTOR_PRODUCTS_ORDER.M_ORDERID;
FOR aRow IN (SELECT MOTOR_ID, MOTOR_QTY_PURCHASED, UNITPRICE
FROM MOTOR_ORDERS
WHERE motor_order_id = order_item_id)
LOOP
-- Do something here with the values in 'aRow'. For example, you
-- might print them out:
DBMS_OUTPUT.PUT_LINE('MOTOR_ID=' || aRow.MOTOR_ID ||
' MOTOR_QTY_PURCHASED=' || aRow.MOTOR_QTY_PURCHASED ||
' UNITPRICE=' || aRow.UNITPRICE);
END LOOP;
END;
Best of luck.
This looks like a Forms question; is it? If so, my suggestion is to let Forms do that job for you.
According to what you posted, there are two blocks:
MOTOR_PRODUCTS_ORDER (a master block, form type)
MOTOR_ORDER_ITEMS (a detail block, tabular type)
I guess that there is a master-detail relationship between them. If there's none, I'd suggest you to create it. Although you can make it work without such a relationship, it'll be much more difficult. If you are unsure of how to do it, start from scratch:
delete MOTOR_ORDER_ITEMS block (detail)
create it once again, this time by following the Data Block Wizard
Set MOTOR_PRODUCTS_ORDER to be its master block
Relationship is on ORDER_ID column/item
Let's presume that by this point everything is set up. Retrieving items that belong to that ORDER_ID is now very simple:
navigate to master block
enter query mode
enter value into an item that represents ORDER_ID
execute query
End of story. Forms triggers & procedures (which were created by the Wizard) will do its job and retrieve both master and detail records.
No need for additional coding; if you're skilled developer, you can create such a form in a matter of minutes. Won't be beautiful, but will be effective & reliable.
There's really no use in doing it manually, although it is possible. Your code works if there's a single item for that ORDER_ID. For two or more items, as you already know, it'll fail with TOO-MANY-ROWS error.
Basically, if you insist, you should use a loop:
you'd enter ORDER_ID into the master block
as you need to move through the detail block, i.e. use NEXT_RECORD, which is a restricted procedure, you can't use number of triggers (open Forms Online Help System and read about them) so a "Show items" button (with its WHEN-BUTTON-PRESSED trigger) might be just fine
a cursor FOR loop would be your choice
for every row it fetches, you'd populate block items and
navigate to next record (otherwise, you'd keep overwriting existing values in the 1st tabular block row)
As I said: possible, but not recommended.

Oracle Stored Procedure List Parameters

I'm developing a .NET front end that interacts with an Oracle database. I have figured out how to get a list of stored procedures to execute, but I don't know how to get a list of parameters that belong to the stored procedure. I want to be able to show a list of all the parameters that are both input and output parameters for the stored procedure.
I have tried using the DBA_SOURCE, DBA_PROCEDURES, ALL_DEPENDENCIES, but I haven't seen anything that shows the parameters that belongs to the specified stored procedure.
Any ideas?
I believe that both responses I received are correct, but I ended up finding a different query which gives me exactly what I'm looking for:
SELECT
ARGUMENT_NAME,
PLS_TYPE,
DEFAULT_VALUE
FROM
USER_ARGUMENTS
WHERE
OBJECT_NAME = '<my_stored_proc>'
This has been working for me so far and pulls all the OracleParameter information that I want as well.
You find parameter metadata in DBA/ALL/USER_ARGUMENTS view.
This is the query that we use, more or less:
SELECT *
FROM
ALL_ARGUMENTS
WHERE
DATA_TYPE IS NOT NULL
-- This check removes package procedure arguments that don't really
-- seem to mean anything
AND
DATA_LEVEL = 0
-- Use this predicate to remove entries for the return value of functions
AND
POSITION > 0
ORDER BY
OWNER,
PACKAGE_NAME,
OBJECT_NAME,
OBJECT_ID,
OVERLOAD,
POSITION

Crystal Reports not sending parameters to Oracle procedure

I have a new Crystal Report, starting from our company's standard template, which has two connections: one to get some header information and then the Oracle stored procedure. When I run the report, only data for the header shows. That means the connection is working.
When I do a Show SQL Query, I can see the second query is not sending any parameters to Oracle:
OraDB
SELECT *
FROM HeaderInfo RPT
WHERE RPT_NUM = 'RPT829'
OraDB
BEGIN "OraUser"."Proc_829"(:CRYSTAL_CURSOR, NULL, NULL, NULL); END ;
I've entered the parameters, but there is a linkage missing somewhere, because it's just NULLs here. Where do I set that?
I have tried re-creating the report, starting from the same template, and it does not work. I have started a new report, adding my parameter first and then the command for the header, and it does. It just works, and binding those variables isn't something that is normally done manually. But when the binding doesn't work, is there a way to force it?
In my field explorer, it has the following:
- Database Fields
.....+ Proc_829
.....+ Command
+ Formula Fields
- Parameter Fields
.....Divisions
.....StartDate
.....EndDate
When I refresh, it asks for those parameters, which match the parameters that the Proc_829 needs. But it shows no data (the proc runs in Oracle and returns data with those same parameters), and the Show SQL Query, as above, is sending nulls.
The standard is to avoid the use of a subreport, and start with this template. The template was changed recently, but I've used previous iterations and it has worked. The template has the Command and some header information already added, and common parameter fields, which happen to match what my procedure needs.
Edit Here is the opening (obfuscated) part of the procedure:
CREATE OR REPLACE PROCEDURE PROC_829 (
CRYSTAL_CURSOR IN OUT SYS_REFCURSOR
,Divisions IN VARCHAR2
,StartDate IN DATE
,EndDate IN DATE
) IS
-- bunch of stuff here, including
OPEN crystal_cursor FOR
-- my select stuff that returns data in Oracle

ORA-0675:package or function ORG_SPGETTYPE is in an invalid state

PROCEDURE ORG_spGetType
(
v_TypeId IN NUMBER DEFAULT NULL
)
AS
BEGIN
SELECT *
FROM ORG_Type
WHERE TypeId = v_TypeId ;
END;
While Running above proceedure in oracle10g usinq eclipse platform sql editor getting error like "ORA-0675:package or function ORG_SPGETTYPE is in an invalid state"
That's not much of a surprise; that procedure is invalid. When compiling a procedure or any other PL/SQL block you should check whether it's compiled correctly.
If you're compiling on the command line, i.e. in SQL*Plus you can use the SHOW command and use the ERRORS variable, which:
displays the line and column number of the error (LINE/COL) as well as the error itself (ERROR).
After you've compiled the procedure type the following:
show errors
Alternatively, you can use the USER_ERRORS system view which will show you a list of all errors, which the schema you're in can see.
select *
from user_errors
where name = 'ORG_SPGETTYPE'
The actual reason for your error is that you cannot simply SELECT in PL/SQL. If you do you're not giving PL/SQL the ability to use the results of your SELECT statement in any way and so it won't allow you to do so. If you're selecting something you need to do something with it; what that something is is up to you.
There are numerous ways of doing this but let's say the table ORG_TYPE is unique on the column TYPEID and you want to return one column as an OUT parameter. Your procedure would then look like this:
create or replace procedure org_spgettype (
P_typeid in org_type.type_id%type
, P_some_column out org_type.some_column%type
) is
begin
select some_column into P_some_column
from org_type
where typeid = P_typeid;
end;
/
show errors
Please note a number of things:
There doesn't seem to be any need for the DEFAULT NULL if this is your primary key; it can't be NULL so you don't want to allow this possibility.
I've declared your parameters as the type of the column; this enables you to change the column without having to recode everything.
There is so much more to explain about all of this (exceptions for a start) so I would highly recommend taking some basic tutorials first or asking a colleague/friend for help.
You would have a 'compiled with warnings' message when creating the procedure. You can query the user_errors view to see what problems are reported for your peocedure. You will see something like 'PLS-00428: an INTO clause is expected in this SELECT statement', because you are not selecting into something.
The documentation shows how to do that. You need to declare variables to select into. In this case you could declare a rowtype variable:
CREATE OR REPLACE PROCEDURE ORG_spGetType (v_TypeId IN NUMBER DEFAULT NULL) AS
l_org_type org_type%rowtype;
BEGIN
SELECT *
INTO l_org_type
FROM ORG_Type
WHERE TypeId = v_TypeId ;
-- do something with l_org_type
END;
/
Note that this can get a no_data_found exception if there are no matching rows, or too_many_rows if there are multiple matches for the passed ID.
But it isn't clear what you're planning to do with the retrieved data. This currently does nothing at all with it, it's just selected and then forgotten. The name of the procedure suggests you want to return all or part of the data from the table to the caller. If it's only one field value then this should probably be a function rather than a procedure. Or you could add out parameters to put the values into (as Ben shows), or return a refcursor. Depends what you need.
The default null, however, maybe suggests you sometimes expect more than one result, maybe the whole table if no value is passed - though your where clause won't find anything if v_typeid is null.

How to handle procedure that returns 2 different results

I have a stored procedure made in Oracle 9g that returns a cursor with different columns depending on a parameter, its something like this:
CREATE OR REPLACE PROCEDURE ASCHEMA.SP_TWOCURSORS
(
aParam NUMBER,
P_RETURN OUT SYS_REFCURSOR
)
IS
BEGIN
IF aParam = 1 THEN
OPEN P_RETURN FOR
SELECT
a.column1, (number)
a.column2 (varchar2)
FROM
table1 a;
ELSE
OPEN P_RETURN FOR
SELECT
b.column1, (varchar2)
b.column2, (number)
b.column3 (number)
FROM
table1 b;
END IF;
END;
I have to consume this procedure in PowerBuilder and pass the returned data to a DataWindow1 or DataWindow2, depending on the returned cursor, these datawindows are filled in runtime by the execution of another procedures coming from other source. I can't modify the database objects (like split the sp in two), just the PowerBuilder code. My problem is how to handle this scenario in an elegant way. I have some ideas but don't know if it will work:
Create a DataWindow object that handles every column involved in both cursors returned from the sp, then copy each row to the expected DataWindow.
Create a DataStore and pass the sp with the Create method, then copy the rows in the expected DataWindow.
Execute the procedure dynamically, fetch every row and add each result into a new row of the expected DataWindow.
I haven't tried the first one because there are many columns and it will take a long time to do. The second looks good but I don't know how to handle a DataStore with no DataWindow object and don't know if this is possible (1). The third is my last option to solve this problem. I want to ask people before start implementing this solution because I'm new to PowerBuilder, and even if I won't work on it too long I want to do it in the right way.
Thanks for the help.
(1) I have found this article about using Custom DataStores but I don't know if I can use only 1 DataStore or I should use 2. Also, for the Oracle connection I don't use SQLCA but another transaction object, so I don't know how to do this.
Keep It Simple.
You know the details of the stored proc. If you are calling this sp from PB, you are knowing its aParam already before the call. Why not defining 2 datawindows, one for each version of the results ?
Each DW would have a retrieval argument (the one that is passed to the stored proc) and will get its result from the sp.
At runtime, depending on the retrieval argument, and before retrieving the values, assign the corresponding dataobject to the datawindow object that is on : either the DW that suits the aParam = 1 or the DW that suits the else part.

Resources