I try to loop backwards in table.
I try it like this:
KlantContact.SETFILTER("No.", "<Contact>"."No.");
FOR i := 0 TO KlantContact.COUNT DO BEGIN
IF KlantContact.FINDSET THEN BEGIN REPEAT // KlantContact.FINDSET THEN BEGIN REPEAT
//KlantContact.CALCFIELDS(KlantContact."No.");
MESSAGE(KlantContact.Name);
UNTIL KlantContact.NEXT -1 = 0;
//MESSAGE(KlantContact.COUNT);
END;
END;
Thank you
Set the key, set descending order and loop through the table with REPEAT..UNTIL:
Record.RESET;
Record.SETCURRENTKEY("Field1","Field2");
Records.ASCENDING(FALSE);
IF Record.FINDSET(FALSE,FALSE) THEN BEGIN
REPEAT
// do something
UNTIL Record.NEXT = 0;
END;
If you want to modify the table use the parameters in FINDSET accordingly.
Cheers!
Related
I am using a hash join on some sample data to join a small table on a larger one. In this example '_1080544_27_08_2016' is the larger table and '_2015_2016_playerlistlookup' the smaller one. Here is my code:
data both(drop=rc);
declare Hash Plan
(dataset: 'work._2015_2016_playerlistlookup'); /* declare the name Plan for hash */
rc = plan.DefineKey ('Player_ID'); /* identify fields to use as keys */
rc = plan.DefineData ('Player_Full_Name',
'Player_First_Name', 'Player_Last_Name',
'Player_ID2'); /* identify fields to use as data */
rc = plan.DefineDone (); /* complete hash table definition */
do until (eof1) ; /* loop to read records from _1080544_27_08_2016 */
set _1080544_27_08_2016 end = eof1;
rc = plan.add (); /* add each record to the hash table */
end;
do until (eof2) ; /* loop to read records from _2015_2016_playerlistlookup */
set _2015_2016_playerlistlookup end = eof2;
call missing(Player_Full_Name,
Player_First_Name, Player_Last_Name); /* initialize the variable we intend to fill */
rc = plan.find (); /* lookup each plan_id in hash Plan */
output; /* write record to Both */
end;
stop;
run;
This is producing a table that has the same numbers of rows as the smaller, lookup table. What I would like to see if a table the same size as the larger one with the additional fields from the lookup table joined on via the primary key.
The larger table has repeating primary keys. That is to say the primary key is not unique (based on row number for example).
Can someone please tell me what I need to amend in the code?
Thanks
You are loading both datasets into your hash object - the small one when you declare it, and then the large one as well in your first do-loop. This makes no sense to me, unless you have lookup values already populated for some but not all of the rows in your large dataset, and you are trying to carry them over between rows.
You are then looping through the lookup dataset and producing 1 output row for each row of that dataset.
It is unclear exactly what you are trying to do here, as this is not a standard use case for hash objects.
Here's my best guess - if this isn't what you're trying to do, please post sample input and intended output datasets.
data want;
set _1080544_27_08_2016;
if 0 then set _2015_2016_playerlistlookup;
if _n_ = 1 then do;
declare Hash Plan(dataset: 'work._2015_2016_playerlistlookup');
rc = plan.DefineKey ('Player_ID');
rc = plan.DefineData ('Player_Full_Name', 'Player_First_Name', 'Player_Last_Name', 'Player_ID2');
rc = plan.DefineDone ();
end;
call missing(Player_Full_Name, Player_First_Name, Player_Last_Name);
rc = plan.find();
drop rc;
run;
I have a group of tables that I need to the integer key from and I would like to be able to pass in any of them into a single and get the next value for the key.
I believe that RecordRef is the way to do this, but the code so far doesn't seem quite right.
I am trying to build a function that will take a table record and then return an integer value, that integer value will be the next record for the primary key. IE: if the last record's key is is 62825 the function will return 62826.
FunctionA
BEGIN
Id := GetNextId(SalesRecord); //Assignment not allowed
END;
FunctionB
BEGIN
Id := GetNextId(CreditMemoRecord); //Assignment not allowed
END;
GetNextId(pTableReference: RecordRef) rNextId : Integer
BEGIN
CASE pTableReference.NUMBER OF
DATABASE::SalesRecord: BEGIN
//Find last Record
pTableReference.FINDLAST;
lFieldRef := pTableReference.FIELD(1); //Set to the PK field
END;
DATABASE::CreditMemoRecord: BEGIN
//Find last Record
pTableReference.FINDLAST;
lFieldRef := pTableReference.FIELD(10); //Set to the PK field
END;
... //do more here
END; //CASE
EVALUATE(rNextId,FORMAT(lFieldRef.VALUE)); //Get the integer value from FieldRef
rNextId := rNextId + 1; //Add one for the next value
EXIT(rNextId); //return the value
END;
With this code I am getting the error "Assignment is not allowed for this variable." on the Function Call to GetNextId
Idea of the Table Structure:
Table - SalesRecord
FieldId, Fieldname, Type, Description
1 id integer PK
2 text1 text(30)
3 text2 text(30)
4 dec1 decimal
5 dec2 decmial
Table - CreditMemoRecord
FieldId, Fieldname, Type, Description
10 id integer PK
20 text1 text(30)
30 text2 text(30)
40 dec1 decmial
50 dec2 decmial
Just put function like this in both tables
GetNextId() rNextId : Integer
BEGIN
RESET;
FINDLAST;
EXIT(id+1);
END;
an then call it from record variable
FunctionA
BEGIN
Id := SalesRecord.GetNextId();
END;
FunctionB
BEGIN
Id := CreditMemoRecord.GetNextId();
END;
This is common practice I believe.
You mean "GetNextValue" get next record? I don't quite understand your use-case.
If you want to pass in a generic record, then you'll want to use the VARIANT data type. This is a wildcard type that will accept Records from any table, and allow you to return records from any table.
This is untested, but hopefully give you an idea of how they could work;
LOCAL NextRecord(VAR RecVariant : Variant)
IF RecVariant.ISRECORD THEN BEGIN
RecRef.GETTABLE(RecVariant);
// RecRef.NUMBER is useful for Database::"Customer" style comparisons
RecRef.NEXT;
RecRef.SETTABLE(RecVariant); // Might not be necessary
END;
I've programmed a stack of generic numbers in Ada using a 'Indefinite_Doubly_Linked_Lists' list.
Pop & push operations are implemented with append and delete_last but for a sorting method I would need to access individual items of the list.
I did work out a sorting method using only append/prepend delete_last/first but the result is far from elegant (and maybe not correct)
procedure sort is
elem1: Item;
elem2: Item;
--l is a package-private Indefinite_Doubly_linked_lists'
begin
if Integer(MyList.Length(l)) > 1 then
for i in 0 .. Integer(MyList.Length(l))-1 loop
for j in 0 .. Integer(MyList.Length(l))-1 loop
--Inner sort loop
elem1 := MyList.Element(l.first);
l.Delete_First;
elem2 := MyList.Element(l.first);
l.Delete_First;
if elem1>elem2 then
l.Prepend(elem1);
l.Append(elem2);
else
l.Prepend(elem2);
l.Append(elem1);
end if;
end loop;
end loop;
end if;
end;
How do can I access individual elements (or iterate over) from a list of generic type?
A couple things:
Unless the point of your exercise is writing a sort, you could just...uh...use the generic sort:
package List_Sort is new MyList.Generic_Sorting;
If you're using an Ada 2012 compiler, generalized looping gives you easy access to each element:
procedure Iterate is
begin
for Elem of L loop
Put_Line(Item'Image(Elem));
end loop;
end Iterate;
If you're not using Ada 2012, you can make due with cursors, either on their own:
procedure Cursor_Iterate is
C : MyList.Cursor := L.First;
use type MyList.Cursor;
begin
loop
exit when C = MyList.No_Element;
Put_Line(Item'Image(MyList.Element(C)));
MyList.Next(C);
end loop;
end Cursor_Iterate;
or with MyList's Iterate procedure:
procedure Iterate
(Container : in List;
Process : not null access procedure (Position : in Cursor));
I know that there are REGEXP_ functions, but this ones return maximum 1 row when simply applied to a string var. I know that you can use it in a WHERE clause, but I need a way of dealing with large strings/text/clob vars not tables, so I would like to know if some function can return multiple substrings somehow (I am thinking at something like the explode() or - even better - preg_match() in PHP).
As APC suggested I am providing a sample string and examples of outcomes that I would like to get..
Like I said in the comments bellow, I wand to get the functions/procedures bodies (functions/procedures that are part of some packages) like this:
THE STRING:
create or replace PACKAGE BODY export_db IS
FUNCTION o_functie(ceva NUMBER) return boolean IS
BEGIN
RETURN null;
END;
FUNCTION o_functie(ceva NUMBER, altceva VARCHAR2) return boolean IS
BEGIN
RETURN null;
END;
PROCEDURE export_db_tabele IS
v_ddl CLOB;
BEGIN
FOR c IN(SELECT object_type,object_name FROM user_objects where object_type IN ( 'TABLE')) LOOP
v_ddl := v_ddl || dbms_metadata.get_ddl(c.object_type, c.object_name)||';'||CHR(13)||CHR(10);
END LOOP;
INSERT INTO dbexport(tipobiect, ddltext) VALUES ('tabele', v_ddl);
END;
PROCEDURE export_db_restrictii IS
v_ddl CLOB;
BEGIN
FOR c IN(SELECT constraint_name FROM user_constraints) LOOP
v_ddl := v_ddl || dbms_metadata.get_ddl('CONSTRAINT', c.constraint_name)||';'||CHR(13)||CHR(10);
END LOOP;
INSERT INTO dbexport(tipobiect, ddltext) VALUES ('restrictii', v_ddl);
END;
PROCEDURE export_db_secvente IS
v_ddl CLOB;
BEGIN
FOR c IN(SELECT sequence_name FROM user_sequences) LOOP
v_ddl := v_ddl || dbms_metadata.get_ddl('SEQUENCE', c.sequence_name)||';'||CHR(13)||CHR(10);
END LOOP;
INSERT INTO dbexport(tipobiect, ddltext) VALUES ('secvente', v_ddl);
END;
PROCEDURE export_db_proceduri IS
v_ddl CLOB;
BEGIN
FOR c IN(SELECT OBJECT_NAME FROM user_objects up WHERE object_type = 'PROCEDURE') LOOP
v_ddl := v_ddl || dbms_metadata.get_ddl('PROCEDURE', c.OBJECT_NAME)||CHR(13)||CHR(10);
END LOOP;
INSERT INTO dbexport(tipobiect, ddltext) VALUES ('proceduri', v_ddl);
END;
PROCEDURE export_db_functii IS
v_ddl CLOB;
BEGIN
FOR c IN(SELECT OBJECT_NAME FROM user_objects uo WHERE object_type = 'FUNCTION' ) LOOP
v_ddl := v_ddl || dbms_metadata.get_ddl('FUNCTION', c.OBJECT_NAME)||CHR(13)||CHR(10);
END LOOP;
INSERT INTO dbexport(tipobiect, ddltext) VALUES ('functii', v_ddl);
END;
PROCEDURE export_db_pachete IS
v_ddl CLOB;
BEGIN
FOR c IN(SELECT OBJECT_NAME FROM user_objects uo WHERE object_type = 'PACKAGE' ) LOOP
v_ddl := v_ddl || dbms_metadata.get_ddl('PACKAGE', c.OBJECT_NAME)||CHR(13)||CHR(10);
END LOOP;
INSERT INTO dbexport(tipobiect, ddltext) VALUES ('pachete', v_ddl);
END;
PROCEDURE export_db_declansatoare IS
v_ddl CLOB;
BEGIN
FOR c IN(SELECT OBJECT_NAME FROM user_objects uo WHERE object_type = 'TRIGGER' ) LOOP
v_ddl := v_ddl || dbms_metadata.get_ddl('TRIGGER', c.OBJECT_NAME)||CHR(13)||CHR(10);
END LOOP;
INSERT INTO dbexport(tipobiect, ddltext) VALUES ('declansatoare', v_ddl);
END;
END;
OUTCOMES would be:
ex:
FUNCTION o_functie(ceva NUMBER, altceva VARCHAR2) return boolean IS
BEGIN
RETURN null;
END;
and
PROCEDURE export_db_secvente IS
v_ddl CLOB;
BEGIN
FOR c IN(SELECT sequence_name FROM user_sequences) LOOP
v_ddl := v_ddl || dbms_metadata.get_ddl('SEQUENCE', c.sequence_name)||';'||CHR(13)||CHR(10);
END LOOP;
INSERT INTO dbexport(tipobiect, ddltext) VALUES ('secvente', v_ddl);
END;
IF you know any other method of geting those procedures/functions I am glad to give up parsing all this - from what I know - there is no select to do that... not even from user_source, user_procedures tables or other...
Something like this maybe:
CREATE OR REPLACE FUNCTION explode(longline varchar)
RETURN sys.dbms_debug_vc2coll PIPELINED
IS
pos PLS_INTEGER;
lastpos PLS_INTEGER;
element varchar(2000);
BEGIN
lastpos := 1;
pos := instr(longline, ',');
while pos > 0 loop
element := substr(longline, lastpos, pos - lastpos);
lastpos := pos + 1;
pos := instr(longline, ',', lastpos);
pipe row(element);
end loop;
if lastpos <= length(longline) then
pipe row (substr(longline, lastpos));
end if;
RETURN;
END;
/
This can be used like this:
SQL> select * from table(explode('1,2,3'));
COLUMN_VALUE
---------------------------------------------
1
2
3
SQL>
If you are not on 11.x you need to define the return type yourself:
create type char_table as table of varchar(4000);
and change the function declaration to:
CREATE OR REPLACE FUNCTION explode(longline varchar)
RETURN char_table pipelined
.....
I'm having some problems creating a process to hide a region in pl/sql.
Can anyone give a example of how to do it?
Tanks.
If appropriate, you can put the PL/SQL you need directly into the Condition, using the Condition Type "PL/SQL Function Body Returning a Boolean". For example (using your code from above, which doesn't seem quite right to me - all roads lead to hidden=3!):
DECLARE
a NUMBER;
b NUMBER;
hidden NUMBER;
BEGIN
select count(1) into a from TN_HISTORY_ITEMID where itemid in (select id from TN_TREE where pid = (select id from tn_tree where pid =:P1_ID));
select count(1) into b from surv_host_data where id_client = :P1_ID;
if b <> 0 AND a = 0 then hidden := 3;
elsif a = 0 then hidden := 3;
elsif b = 0 then hidden := 3;
else hidden := 3;
end If;
return (hidden = 3);
End;
If you need to do it with a process and a page item, then you need to make sure that the item is rendered before the region, and that the process runs before the region to be hidden is rendered. Otherwise by the time the item is set it will be too late.