I'm new to free pascal and lazarus stuff and i wonder how can i get row data (for example id column) from selected row in tbgrid?
The grid is linked to a dataset. The dataset has an active row, you can use that.
Better late than never for future seekers.
Consider you have an integer value(primary column ID as you stated).
At your SQLQuery after scroll event, go something like this:
procedure TMainform1.SQLQuery1AfterScroll(DataSet: TDataSet);
Var id:Integer;
Begin
id := DBGrid1.Columns.Items[0].Field.AsInteger;
end;
Related
I am new to Oracle PL/SQL. I have found this package and it is called from a trigger. I just cannot figure exactly what this simple looking package code is doing.
It is called from a trigger as below:
IF INSERTING THEN
i := STATE_PKG_OVERRIDE_CN.AffectedRows.COUNT+1;
STATE_PKG_OVERRIDE_CN.AffectedRows(i).IDU := :new.IDU;
STATE_PKG_OVERRIDE_CN.AffectedRows(i).cn := :new.cn;
This is the package. Can somebody please explain the basics of what is it doing? does it return a value? change a value? what is AffectedRows RIDARRAY or EMPTY ?
create or replace PACKAGE STATE_PKG_OVERRIDE_CN
AS
TYPE rowid_cn IS RECORD
(
idu dirxml.USR.IDU%TYPE,
cn dirxml.USR.CN%TYPE
);
TYPE RIDARRAY IS TABLE OF rowid_cn INDEX BY BINARY_INTEGER;
AffectedRows RIDARRAY;
EMPTY RIDARRAY;
END;
I have googled EMPTY but found nothing, i believe it is creating a table of type record. The trigger is passing in a value of cn or IDU am i am familiar with these two values. But what is the package doing? or returning ? im confused.
Cheers
This is a bespoke package belonging to your organisation. (That's why Google wasn't helpful for you.) We cannot tell you for sure what it does or how it's used. But we can guess.
The package has no procedures or functions, it just defines array variables of a bespoke type, which can be used by other program units such as triggers. The trigger you posted assigns values to the array AffectedRows. Presumably this trigger fires FOR EACH ROW. Likely there is another trigger on the same table firing AFTER STATEMENT which reads that array and does some processing, then assigns AffectedRows := EMPTY to reset the array.
The purpose of this infrastructure is to pass state across trigger actions. A common reason for doing this is work around a mutating table exception. They are risky because state cannot be guaranteed; for instance if a insert fails before the AFTER STATEMENT trigger fires the AffectedRows array is not re-initialised, so subsequent processing will be incorrect (or will fail).
Since 11g Oracle provides compound triggers which remove the need for this sort of package. Find out more.
To investigate further, first you want to check USER_TRIGGERS to find other triggers on the table which owns the trigger you mentioned. If that doesn't help or you want to see whether other tables also use this package run this query:
select *
from user_dependencies
where referenced_type = 'PACKAGE'
and referenced_name = 'STATE_PKG_OVERRIDE_CN'
It begins with the package which
declares a type ROWID_CN as a record that contains two values: IDU and CN
syntax requires to create another type (RIDARRAY) which is based on previously declared ROWID_CN
affectedrows and empty are arrays whose type is RIDARRAY. Basically, you can imagine them as a table that has two columns, IDU and CN
Now, a trigger: piece of code you posted says that those 3 lines are executed when someone inserts a row into a table the trigger is based on. For example,
create or replace trigger trg_biu_emp
before insert or update on emp
for each row
declare
i number;
begin
if inserting then ...
Apparently, there's some code that is executed when updating, or even deleting rows.
Anyway:
i := ... line counts number of elements in affectedrows array (which is declared in the package) and adds 1 to that number. For example, if there were 3 elements, i would be 4.
...affectedrows(i).idu := :new.idu enters a new row into the array. Its ordinal number is i (4 in our example). As you're inserting a row into the table, trigger knows IDU column's :new value and puts it into the array. For example, if you used
insert into emp (idu, cn) values (100, 'A')
then affectedrows(4).idu = 100, while affectedrows(4).cn = 'A'
It is, probably, something similar with EMPTY array. Google can't return anything useful, it is just a custom-made array.
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.
I have a cursor that iterates a table. Each row is inserted into a type that is equal to the cursor row.
CURSOR lc_lines
IS
SELECT tlc1, tlc2, tlc3
FROM table_x
ORDER BY tlc1;
TYPE table_lines IS TABLE OF lc_lines%ROWTYPE
INDEX BY PLS_INTEGER;
i_table_lines table_lines;
I needed to dynamically access the columns in the type. Something like this:
LOOP
FETCH lc_lines
BULK COLLECT INTO i_table_lines
LIMIT p_i_limit;
--e ai vamos nos :)
FOR i_idx IN 1 .. i_table_lines.COUNT
LOOP
varx := i_table_lines (i_idx).tlc||'2';
Is this possible?
Thanks in advance
You should be able to use DBMS_SQL routines to do this ... it's there to assist with complex, dynamic SQL/cursors, etc.
Try reading over the documentation here:
http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/dynamic.htm#LNPLS00001
You basically setup your cursor, your binds, your columns, etc.
then you can start accessing them without necessarily knowing how many columns you have in the first place.
[edit] I linked to the oracle 11 page, however, that feature's pretty much same between Oracle 10 and 11 .. here's the 10g doc if you need :
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_sql.htm#i996963
[/edit]
In a number of places in my applications, I need to display data as CSV.
For this, I usually loop through a cursor and build the CSV.
for j in c_get_mydata loop
v_CSV := v_CSV || ', ' || j.column_value;
end loop;
How can I write a re-usable function that could accept a query and return a CSV string? Do you recommend having this as a separate function or is it better to process each query like above? If you recommend having it as a separate function, do you recommend that the function take the string of the query or the resultant cursor as input?
EDIT:
Clarification: I want to display a single column of data as one row, separated by commas.
E.g.: Tom, Dick, Harry, Sally
I'm not looking to display multi-column data on multiple rows as:
Tom, 18, London
Dick, 22, New York
Harry, 16, San Fransisco
Sally, 18, Paris
EDIT 2:
I found out about the collect function:
select collect(employee_name)
from employees
This returns me a dataset, but how can I convert it into a string?
The simplest option would generally by to use the DBMS_SQL package. Tom Kyte has a good example with his dump_csv procedure. Of course, you probably want to write the results to a CLOB rather than writing it to a file using utl_file but that's a relatively easy tweak to make.
Old question and already correctly answered I know, but as this is one of the top Google hits for "oracle cursor to csv" and I've just written something to do this, I thought I'd post a new answer.
From Oracle 11.2 onwards DBMS_SQL can handle a normal ref cursor rather than just a string as in Tom Kyte's 2000 solution. You ingest it into DBMS_SQL using
l_cursor_id := dbms_sql.to_cursor_number(your_refcursor);
(l_cursor_id will then contain a meaningless generated number that DBMS_SQL uses to track its corresponding internal values.)
Since the ref cursor was already opened and parsed, you skip those steps and call
dbms_sql.describe_columns(l_cursor_id, l_col_count, l_cursor_columns);
where l_cursor_columns (an OUT parameter that DBMS_SQL populates for you) is a dbms_sql.desc_tab, which is an array of column definitions populated with an entry for each column in the query and containing the column name, datatype etc, and l_col_count somewhat redundantly contains the same value as l_cursor_columns.count. You then need to loop through this array calling dbms_sql.define_column to tell DBMS_SQL to handle each one using the datatype it just told you, a rather verbose step I can never quite see the point of. (It does allow you to treat all '%CHAR%' types as varchar2 etc so I guess it allows some flexibility.)
Then it's just a matter of calling dbms_sql.fetch_rows in a loop and processing the results. I put this in a pipelined table function which accepts a ref cursor argument, so you get something like this:
select column_value
from table(csv.report(cursor(
select * from dept -- << Use any query here
)));
COLUMN_VALUE
----------------------------------------------
10,ACCOUNTING,NEW YORK
20,RESEARCH,DALLAS
30,SALES,CHICAGO
40,OPERATIONS,BOSTON
4 rows selected.
Full writeup and code here:
www.williamrobertson.net/documents/refcursor-to-csv.shtml
There have been a couple of hints that seem to have gotten me close here, but with some unique issues, I'm hoping this question is distinguishing enough to merit its own posting.
For starters here's what I have. I have an Oracle procedure that returns a standard REF CURSOR, and this REF CURSOR is passed back to my application. The REF CURSOR is a list of lookup IDs.
I then want to take this list and bring it to another data store and use it in a select statement. It will absolutely be possible to accomplish this by looping through the REF CURSOR, but I'm hoping to avoid that. I would much rather be able to write a SELECT...WHERE lookup_id IN result_ref_cursor OR SELECT...WHERE EXISTS...
First is this possible or should I just try a less than elegant solution? If it is possible, any hints as to where I should get started looking?
I'm relatively new to Oracle, but fairly experienced in RDBMs in general, so feel free to just through some links at me and I can study up. Much appreciated
Why kurosch didn't put his response as an "answer" I'll have no idea.
So, what you do is define a SQL type which describes one row of the output of the ref cursor, and also a SQL type which is a table of the previous. Then, you'll create a pipelined function which returns the rows returned by the ref cursor. This function can then be used in a standard SQL. I'm borrowing from Ask Tom on this one.
create or replace type myLookupId as object ( id int)
/
create or replace type myLookupIdTable as table of myLookupId
/
create or replace function f return myLookupIdTable PIPELINED is
l_data myLookupId;
l_id number;
p_cursor SYS_REFCURSOR;
begin
p_cursor := function_returning_ref_cursor();
loop
fetch p_cursor into l_id;
exit when p_cursor%notfound;
l_data := myLookupId( l_id );
pipe row (l_data);
end loop;
return;
end;
/
And now a sample query...
SELECT *
FROM SOME_TABLE
WHERE lookup_id in (SELECT ID FROM table(f));
Sorry if the code isn't exactly right, I don't have the DB to test right now.
There are several directions you could go with this, but I did a search on the specific solution you want and it seems like no-one has done it often enough to show up there. What you can do is search the oracle metalink - that is usually really good at finding obscure answers. (Though you do need a service agreement - just found out that mine expired :( )
Other possible solutions:
Create a link between the data stores so that you can do the select in the plsql directly
Create a function in Java that loops through it for you to create the string for the query. This will look a little more pretty at least.
Otherwise, REF CURSOR's need to go back and forth - I don't know how you can pipe the results of the REF CURSOR in one connection to the query in another without looping through it.