Complex Cursors for returning multiple rows - sql

Each row in a cursor should be joined with another table and the whole result should be returned a one cursor
Say cursor1 return 5 rows.
Each of these 5 rows should be joined with table1.
and the final result for all the 5 rows should be returned a 1 row.
Plz help

It is rather tricky to combine the fetched records from a ref cursor with the result set of another query. A much better idea would be to open just the one ref cursor with a SELECT which joins all the relevant tables.

The question isn't clear, but it sounds like what you need is something like this
a) Define an object type with the shape of your result row
b) Define a collection of that object type
c) Create a function with a return type of the collection type - this could take in cursor A as a parameter (SYS_REFCURSOR), join each row in cursor A to table B, and then use PIPE ROW for each result row.
d) If the final result is needed as a cursor, then another function along the lines of
FUNCTION complex_query(in_cursor SYS_REFCURSOR)
RETURN SYS_REFCURSOR
IS
lreturn SYS_REFCURSOR;
BEGIN
OPEN lreturn FOR
(SELECT * FROM TABLE(convert_to_collection(in_cursor)));
RETURN lreturn;
END;
Alternatively, you could just do the SELECT * FROM TABLE(convert_to_collection(in_cursor)) directly.
What I don't understand is the requirement that everything is returned as 1 row.

Related

Fetch cursor from multiple tables view PostgreSQL

I try until couple of hours to fetch all the row from a view, created from multiple tables.
I have two tables (Position, and Vector) to which correspond respectively two custom composite types , type_position and type_vector
Position (id, sys_time, lat, lon)
Vector (id, sys_time, speed)
I want to create a temporary VIEW in plpgsql procedure to bring together all Position and Vector and order them by sys_time so i wrote :
CREATE TEMP VIEW posAndVectView AS
SELECT * from position
UNION ALL
SELECT * from vector;
I need to iterate over this view and made some work depends on Position and Vector attributes.
So i think i should use a cursor :
DECLARE
manyRows refcursor;
BEGIN
OPEN manyRows FOR
SELECT * FROM posAndVectView ORDER BY sys_time ASC;
LOOP
FETCH manyRows INTO posOrVect;
EXIT WHEN NOT FOUND;
...
END LOOP;
CLOSE manyRows;
So my question is what should the type of the posOrVect variable be in the DECLARE section ?
It seems to be sometimes a type_vector, sometimes a type_position...
I found the answer it is pretty simple.
I need to declare the posOrVect variable with the record type as its mentionned in the PostgreSQL documentation :
Record variables are similar to row-type variables, but they have no
predefined structure. They take on the actual row structure of the row
they are assigned during a SELECT or FOR command. The substructure of
a record variable can change each time it is assigned to. A
consequence of this is that until a record variable is first assigned
to, it has no substructure, and any attempt to access a field in it
will draw a run-time error.
Simple. The type should be posAndVectView.
Like for any other table, there is also a composite type with the same name as a temporary table.
But do you need a view? You could just open the cursor for the query with the UNION. In that case, you'd use type position because it is the first table.

Fetch multiple rows into a variable

I am attempting to fetch multiple unique account ID's (transactions.ID) and store in Variable v_Trxn. The problem is that the query count returns incorrect. Expected result is 300485 rows.
DECLARE
l_Partition transactions.Partitionkey%TYPE;
v_Trxn transactions.ID%TYPE;
l_Count number;
CURSOR v_Trxn_cur IS
SELECT ID
FROM transactions
WHERE Partitionkey > l_Partition;
BEGIN
SELECT dl_common.Get_Partitionkey(aInstitutionId => DL_COMMON.Get_InstitutionId , aDate => add_months(sysdate,-4)) INTO l_Partition FROM Dual;
OPEN v_Trxn_Cur;
LOOP
FETCH v_Trxn_Cur INTO v_Trxn;
EXIT WHEN v_Trxn_Cur%NOTFOUND;
END LOOP;
SELECT COUNT(UNIQUE(ID)) INTO l_Count FROM transactions WHERE ID In v_Trxn;
DBMS_OUTPUT.PUT_LINE(l_Count || ' Number of Unique Sernos');
CLOSE v_Trxn_Cur;
End;
Output: 1 Number of Unique Sernos
If I put the partitionkey directly into the WHERE clause I get the expected number of rows.
SELECT
dl_common.Get_Partitionkey(aInstitutionId => DL_COMMON.Get_InstitutionId,
aDate => add_months(sysdate, -4)) PARTITION
FROM Dual;
Returns: PARTITION 2914365
SELECT
COUNT(UNIQUE(ID)) C_COUNT
FROM transactions C
WHERE C.Partitionkey > 2914365
Returns: C_COUNT 300485
The expected number of rows is fetched.
Please explain what I am doing wrong.
You're repeatedly selecting a single ID into your scalar variable. It can never hold more than a single value at a time. After the first iteration of your loop your variable holds the first ID returned by your cursor query (which is indeterminate because you don't have an order-by clause). After the second iteration your variable holds the second ID returned by the cursor query. It does not, and cannot, hold both values simultaneously.
To hold multiple values you would need to bulk select into a collection type, which needs to be declared at schema level (i.e. an SQL type, not a PL/SQL type) if you really want to use it as part of a later SQL query.
But storing hundreds of thousands of values in a collection is going to consume a significant amount of memory. Without knowing what you're really going to do with the values once you have them it isn't clear if that is just a price you'll have to pay; if you can do your work in batches (of, say, 1000 IDs at a time; if row-by-row processing is appropriate; or if you really want a join as part of a larger query, without holding them as a PL/SQL variable at all.
You need to use VARRAY OR NESTED TABLES FOR THIS PURPOSE.
You can hold multiple values like this-
Type var_dnames IS VARRAY(1000) transactions.ID%TYPE;
v_Trxn var_dnames;
Now "v_Trxn" can hold multiple values from fetch statement.

Cursor in procedure returning more values than query

I am using a simple cursor in a procedure that receives a couple of parameters.
I then make a cursor on a select query with a where clause with multiple conditions, which are equal to the received parameters. This cursor should only return 1 row, instead it returns multiple rows. I found this out because I'm using a for loop to go through this cursor and insert something into another table based on the values of this cursor.
When I run the query on the database statically(as in without pl/sql) I get what I expect, but when I do it from a cursor which should return only one row, and run it in a for loop, the loop does multiple iterations. How is this possible?
Thank you!
EDIT:
ID kind kolo kolo1 mjt salesman money date done
1 001 001 002 00013 00056 100,00 21-feb-12 N
I run a cursor like this:
Cursor linija IS
SELECT *
FROM table_x X
where x.mjt = mjt
and x.salesman = salesman
and x.kind = kind
and x.kolo1 = kolo1
and x.done = 'N';
This should return only one row, but instead my cursor returns %rowcount is 10.
You have a name conflict. You have called your local variables the same as your column names, and the column names are taking precedence, as noted in the documentation:
If a SQL statement references a name that belongs to both a column and either a local variable or formal parameter, then the column name takes precedence.
Caution:
When a variable or parameter name is interpreted as a column name, data can be deleted, changed, or inserted unintentionally.
The first four checks are always going to be true (unless you have null values), so you'll get every row that has done = 'N'.
Change your local variable names to something else; it's fairly common to use a prefix to distinguish between local variables, parameters, and columns, something like:
Cursor linija IS
SELECT *
FROM table_x X
where x.mjt = l_mjt
and x.salesman = l_salesman
and x.kind = l_kind
and x.kolo1 = l_kolo1
and x.done = 'N';
If this is in a stored procedure, rather than an anonymous block, you could use the procedure/function name as a prefix, which some people prefer. If your procedure was called myproc, for example, you could do:
Cursor linija IS
SELECT *
FROM table_x X
where x.mjt = myproc.mjt
and x.salesman = myproc.salesman
and x.kind = myproc.kind
and x.kolo1 = myproc.kolo1
and x.done = 'N';
In addition to what Alex has said (and I can't second his advice to distinguish variable names from column names enough!), why are you using a cursor for loop to do the insert?
You could just do your insert in one SQL statement, eg:
insert into your_table (col1, col2, ...)
select col1, col2, ...
from your_table
where ...
That will perform much better than going through the whole dataset and inserting each row one at a time. When it comes to databases, think set-based as much as you can, not procedurally!

Convert select into stored procedure best approach

I use this SQL to get count for every group of type.
select
mytype, count(mytype)
from types1
group by 1
The result is 5 records with count for each type. I need to convert this to a stored procedure; should I write the above SQL using For...Select or should I return single value using Select...Where...Into 5 times for each type?
I will use the return counts to update a master table and types may increase in the future.
That depends on what you want out of the procedure:
If you want the same output as your select with five rows, use a FOR SELECT. You will get one row for each type and an associated count. This is probably the "standard" approach.
If however you want five output variables, one for each count of each type, you can use five queries of the form SELECT COUNT(1) FROM types1 WHERE mytype = 'type1' INTO :type1. Realize though that this will be five queries and you may be better off doing a single FOR SELECT query and looping through the returned rows in the procedure. Also note that if you at some point add a sixth type you will have to change this procedure to add the additional type.
If you want to query a single type, you can also do something like the following, which will return a single row with a single count for the type in the input parameter:
CREATE PROCEDURE GetTypeCount(
TypeName VARCHAR(256)
)
RETURNS (
TypeCount INTEGER
)
AS
BEGIN
SELECT COUNT(1)
FROM types1
WHERE mytype = :TypeName
INTO :TypeCount;
SUSPEND
END

How can I count the rows returned from a stored-procedure that returns a table?

Is it possible to count how many rows returned by a stored-procedure which returns a simple table?
Return ##ROWCOUNT as either an output parameter or second result set.
Or add another column to the one resultset using a COUNT..OVER construct
...
COUNT(*) OVER () AS RowCount
...
Or read it in the client code eg DataTable.Rows.Count