How to select and commit data to DB by chunks? - abap

I have written a code to get all records into internal table using select query, perform concatenation on it and update the DB from this internal table.
To improve performance how can I select specific number of records like 1000 records, process them and after updating the DB select next 1000 records and so on.
Here is my code
DATA: lv_index_table TYPE /ngv/b0tabnm,
lref_tname TYPE REF TO data.
FIELD-SYMBOLS: <lt_indx_table> TYPE ANY TABLE,
<lwa_indx_table> TYPE any.
CALL METHOD /ngv/cl_bmtr_framework=>generated_table_get
EXPORTING
i_dtarea = /ngv/if_bcon=>mc_data_area-index
IMPORTING
e_table = lv_index_table "here i get DB table name .
EXCEPTIONS
table_not_found = 1
OTHERS = 2.
CHECK lv_index_table IS NOT INITIAL.
CREATE DATA lref_tname TYPE TABLE OF (lv_index_table).
ASSIGN lref_tname->* TO <lt_indx_table>.
CHECK <lt_indx_table> IS ASSIGNED.
SELECT * FROM (lv_index_table) INTO TABLE <lt_indx_table> UP TO 1000 ROWS.
LOOP AT <lt_indx_table> ASSIGNING <lwa_indx_table>.
ASSIGN COMPONENT 'EXTID' OF STRUCTURE <lwa_indx_table> TO FIELD-SYMBOL(<lv_extid>).
ASSIGN COMPONENT 'MTRCT' OF STRUCTURE <lwa_indx_table> TO FIELD-SYMBOL(<lv_mtrct>).
ASSIGN COMPONENT 'MSKVL' OF STRUCTURE <lwa_indx_table> TO FIELD-SYMBOL(<lv_mskvl>).
CHECK <lv_extid> IS ASSIGNED AND <lv_mtrct> IS ASSIGNED AND <lv_mskvl> IS ASSIGNED.
CONCATENATE <lv_extid> '_' <lv_mtrct> INTO <lv_mskvl>.
ENDLOOP.
MODIFY (lv_index_table) FROM table <lt_indx_table>.

There are different solutions for your requirement. It depends your number of records, your system, database etc.
If you have really huge data try cursors.
You can process data in packages. That's not increase performance pretty much but it helps prevention of performance deterioration.
For detailed information check this links (1 , 2, 3).
DATA s_cursor TYPE cursor.
DATA:it_mara TYPE TABLE OF mara.
OPEN CURSOR WITH HOLD s_cursor FOR
SELECT * FROM mara.
DO.
FETCH NEXT CURSOR s_cursor APPENDING TABLE it_mara PACKAGE SIZE 10.
IF sy-subrc <> 0.
EXIT.
ENDIF.
"Your logic should be here..
"If you want to update database use CALL FUNCTION 'DB_COMMIT'
ENDDO.
CLOSE CURSOR s_cursor.

Related

use SELECT on internal table (ABAP)

I am still very inexperienced with SAP ABAP.
I have an internal table that I want to filter further and further based on whether data is present.
I have tried the following, but unfortunately I cannot apply a SELECT to an internal table.
How can I solve this problem?
Hope I have explained my problem clearly enough!
"Here I'm getting the hole database into my internal table
SELECT * FROM TABLE
INTO CORRESPONDING FIELDS OF TABLE #itab.
"This should be my first filter if iv_name is not initial
IF iv_name IS NOT INITIAL.
SELECT * FROM itab
WHERE NAME = #iv_name
INTO CORRESPONDING FIELDS OF TABLE #itab.
ENDIF.
"This should be my second filter if iv_age is not initial
IF iv_age IS NOT INITIAL.
SELECT * FROM itab
WHERE AGE = #iv_age
INTO CORRESPONDING FIELDS OF TABLE #itab.
ENDIF.
There are several ways in ABAP to achieve your goal.
You can use the DELETE keyword to filter the data in an internal table:
IF iv_name IS NOT INITIAL
DELETE itab WHERE name NE iv_name.
ENDIF.
Another possibility is to use the FILTER keyword, but the prerequisite is, that the internal table is TYPE SORTED or HASHED:
itab = FILTER #( itab WHERE name = iv_name ).

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.

SSIS String variable size

I would like to know what is the size limit on the SSIS string variable. I have a OLE DB data source that queries a column (alpha-numeric 10 digit) values and feeds the result set into a script task, the script task then creates a set based delete sql statement(i.e. DELETE FROM Table Where ID IN("all id's go here") and assigns the sql statement to a string variable, a third task (execute sql) then executes the sql from the variable. I am wondering if I get 10,000 values in the IN clause would that cause any issues with the string variable? Please advise
There is no limit on a string variable size .SSIS data types are derived from .net sub system so incase if you do have a very very large string value then you may run out of memory
One possible solution can be to put the ID in a temporary table in your destination database and then you can do a DELETE statement with a join between the temporary table and the table you are deleting from.
Or, you can rewrite your statement: DELETE FROM Table WHERE ID IN (SELECT ID FROM TempTable)

How to set dynamic key for READ TABLE?

I'm trying to work out a way to read an internal table that has to be created dynamically. I have created the following report that fills a dynamic internal table with data.
On the last line, I'm trying to read it with a key (mandt for example), but I I get this syntax error:
The specified type has no structure and therefore no component called MANDT
I have debugged and I can see that <any_tab> has been populated successfully and the structure of the table (field names) are correct. The problem presents itself when I try to read the table into a work area. Maybe I'm doing this wrong, but it seems like something that should be possible to do, and I have a feeling I'm missing something small.
The reason I am trying this out is that I have found identical selects happening in a program and want to buffer records in memory and read them from there to avoid DB accesses. This is easy to implement, however I haven't done this when the table, where clause and into clause of the OPEN SQL statement I'm trying to optimize are dynamic.
How to correct the syntax error?
DATA: t681_rep TYPE TABLE OF t681 , wa_681 LIKE LINE OF t681_rep,
tabref TYPE REF TO data , waref TYPE REF TO data.
FIELD-SYMBOLS: <any_tab> TYPE ANY TABLE,
<any_wa> TYPE ANY,
<var1> TYPE ANY.
"fill t681_rep
SELECT *
FROM t681
INTO TABLE t681_rep
UP TO 1 ROWS WHERE kotab = 'A002'.
READ TABLE t681_rep INTO wa_681 WITH KEY kotab = 'A002'.
IF sy-subrc = 0.
"if A002 is found create a table of that type and fill it
CREATE DATA tabref TYPE TABLE OF (wa_681-kotab).
ASSIGN tabref->* TO <any_tab>.
SELECT * UP TO 10 ROWS
FROM (wa_681-kotab)
INTO TABLE <any_tab>.
ENDIF.
CREATE DATA waref TYPE a002.
ASSIGN waref->* TO <any_wa>.
READ TABLE <any_tab> ASSIGNING <any_wa> WITH KEY mandt = '800'. <- problem area
IF sy-subrc = 0.
"do stuff with <any_wa>...
ENDIF.
You just need to put the field name in parentheses.
data: field type string.
field = 'MANDT'.
READ TABLE <any_tab> ASSIGNING <any_wa> WITH KEY (field) = '800'.
IF sy-subrc = 0.
"do stuff with <any_wa>...
ENDIF.
AFAIK, you have to do it the 'long way round':
FIELD-SYMBOLS: <any_field> TYPE any.
LOOP AT <any_tab> ASSIGNING <any_wa>.
ASSIGN COMPONENT 'MANDT' OF STRUCTURE <any_wa> TO <any_field>.
IF <any_field> <> 800.
CONTINUE.
ENDIF.
" do stuff with <any_wa> - you will have to assign <any_field> again to access fields.
ENDLOOP.
You are trying to beat a database in efficiency, it is a loosing battle.
Just go to SE11, select your table, go to technical settings and change the technical settings ( buffering & buffering type ), you do not require an object modification key for this. You can also make sure that the size category is correct.
You can use RTTS to get the table keys.
data table_name type string.
table_name = 'A002'.
" Dynamically create the table type
data the_table type ref to data.
create data the_table type table of (table_name).
" Use RTTS to get table keys
data typedescription type ref to cl_abap_tabledescr.
typedescription ?= cl_abap_tabledescr=>describe_by_data_ref( the_table ).
data keys type abap_table_keydescr_tab.
keys = typedescription->get_keys( ).
REPORT y_test_dynamic_table.
DATA: table_name TYPE string,
typedescription TYPE REF TO cl_abap_tabledescr,
keys TYPE abap_keydescr_tab,
ls_key TYPE abap_keyname.
table_name = 'ZYFRM_STG'.
" Dynamically create the table type
DATA the_table TYPE REF TO data.
CREATE DATA the_table TYPE TABLE OF (table_name).
" Use RTTS to get table keys
typedescription ?= cl_abap_tabledescr=>describe_by_data_ref( the_table ).
keys = typedescription->KEY.
loop at keys INTO ls_key.
***
ENDLOOP.

Oracle SQL "meta" query for records which have specific column values

I'd like to get all the records from a huge table where any of the number columns countains a value greater than 0. What's the best way to do it?
E.g.:
/* table structure*/
create table sometable (id number,
somestring varchar2(12),
some_amount_1 number(17,3),
some_amount_2 number(17,3),
some_amount_3 number(17,3),
...
some_amount_xxx number(17,3));
/* "xxx" > 100, and yeah I did not designed that table structure... */
And I want any row where any of the some_amount_n > 0 (even better solution is to add a field in the first place to show which field(s) are greater than zero).
I know I can write this with a huge some_amount_1 > 0 OR some_amount_2 > 0 OR ... block (and the field names with some case when but is there should be some more elegant solution, isn't there?
Possible solutions:
Normalize the table. You said you are not allowed to. Try to convince those that forbid such a change by explaining the benefits (performance, ease of writing queries, etc).
Write the huge ugly OR query. You could also print it along with the version of the query for the normalized tables. Add performance tests (you are allowed to create another test table or database, I hope.)
Write a program (either in PL/SQL or in another procedural language) that produces the horrible OR query. (Again, print along with the elegant version)
Add a new column, say called Any_x_bigger_than_zero which is automatically filled with either 0 or 1 via a trigger (that uses a huge ugly OR). Then you just need to check: WHERE Test_x_bigger_than_zero = 1 to see if any of the rows is > 0
Similar to previous but even better, create a materialized view with such a column.
First, create a table to sort the data into something more easily read from...something simple like id,column_name,column_value. You'll have to bear with me, been a while since I've operated in oracle, so this is heavy pseudo code at best:
Quick dynamic sql blurb...you can set a variable to a sql statement and then execute that variable. There are some security risks and it's possible this feature is disabled in your environment...so confirm you can run this first. Declare a variable, set the variable to 'select 1' and then use 'execute immediate' to execute the sql stored in your variable.
set var = 'select id, ''some_amount_' || 1 || '', some_amount || 1 || ' from table where some_amount_' || 1 || ' <> 0'
Assuming I've got my oracle syntax right...( pipe is append right? I believe a 3 single quote as ''' should result in one ' when in a variable too, you may have to trial and error this line until you have the var set to):
select id, 'some_amount_1',some_amount_1
from table
where some_amount_1 <> 0
This should select the ID and the value in some_amount_1 for each id in your database. You can turn this into an insert statement pretty easily.
I'm assuming some_amount_xxx has an upper limit...next trick is to loop this giant statement. Once again, horrible pseudo code:
declare sql_string
declare i and set to 1
for i = 1 to xxx (whatever your xxx is)
set sql_string to the first set var statement we made, replacing the '1' with the i var here.
execute sql
increment i
loop
Hopefully it makes sense...it's one of the very few scenarios you would ever want to loop dynamic sql on. Now you have a relatively straight forward table to read from and this should be a relatively easy query from here