I get a database table name at run time(let us suppose from user). I need to query the table and return few fields(which I know). How to do this?
"FOR EACH" wont accept a variable name in it. So, I cant use it.
I have gone through dynamic queries, especially SET-BUFFERS() method. Even with this, I need to know the table name before.
I need something like:
DEF VAR tablename AS CHAR.
tablename = somename.
FOR EACH tablename WHERE ....:
...
...
END.
Can someone please point me to right direction?
You can do a dynamic query with a dynamic buffer. Simply replace the value of cTableName variable in this example:
/* Replace _file with whatever field you're after */
DEFINE VARIABLE cTableName AS CHARACTER NO-UNDO INIT "_file".
DEFINE VARIABLE hQuery AS HANDLE NO-UNDO.
DEFINE VARIABLE hBuffer AS HANDLE NO-UNDO.
CREATE BUFFER hBuffer FOR TABLE cTableName.
CREATE QUERY hQuery.
hQuery:SET-BUFFERS(hBuffer).
hQuery:QUERY-PREPARE("FOR EACH " + cTableName).
hQuery:QUERY-OPEN().
REPEAT:
hQuery:GET-NEXT().
IF hQuery:QUERY-OFF-END THEN LEAVE.
DISPLAY hBuffer:BUFFER-FIELD(1):BUFFER-VALUE.
/* If you know the name of the field you can do: */
/* DISPLAY hBuffer:BUFFER-FIELD("nameoffield"):BUFFER-VALUE. */
END.
/* Clean up */
hQuery:QUERY-CLOSE().
hBuffer:BUFFER-RELEASE().
DELETE OBJECT hBuffer.
DELETE OBJECT hQuery.
Related
When I create an object in a constructor,
myTable property is constructed on the fly, for example, from csv file. I want to have some validation for that property as I have for myInt.
Do you happen to know how to do that? In the future, I guess I will need a per-column validation...
classdef BigObject
properties
myInt (1,1) double {mustBeNonnegative, mustBeInteger} = 0;
myTable (?,?) table {mustBeGreaterThan(table{1}.Variables,1), mustBeLessThan(table{2} ? ,2)} = table;
I tried selecting columns
myTable table {mustBeGreaterThan(table{1},1)} = table;
myTable table {mustBeGreaterThan(table{2}.Variables,2)} = table;
myTable table {mustBeGreaterThan(table.Variables,3)} = table;
The best way to achieve this is to write your own simple property validation function. You can build this on top of existing functions. So, you might do something like this:
%% Base case - passes
t = table((1:10).', 'VariableNames',{'MyVar'});
someFcn(t);
%% Fails because value is negative
t2 = table(-(1:10).', 'VariableNames',{'MyVar'});
someFcn(t2)
% Example function that uses validation
function someFcn(t)
arguments
t {mustHavePositiveVar(t, 'MyVar')}
end
end
% Custom validation function
function mustHavePositiveVar(t, varName)
try
mustBeA(t, 'table');
mustBeMember({varName}, t.Properties.VariableNames)
mustBePositive(t.(varName));
catch E
error('Input must be a table with a variable named %s that is all positive', varName)
end
end
Note there's a nuance if you use the class constraint syntax at the same time as a property validation function - namely that the property gets assigned and type-coerced before the validation functions run. So if you're going to validate the type of an input, you need to omit the class constraint (otherwise the input might simply be coerced to the correct type anyway).
I have a requets which giving me an ids. I need to iterate them into another request, so I have a sheme like this: scheme
In tPostgresqlInput I have this code rc.id = upper('18ce317b-bf69-4150-b880-2ab739eab0fe') , but instead of id I need to put smthn like globalMap.get(row4.id). How did I do this?
Apparently this is a syntax issue
Try with :
"select * FROM table LEFT JOIN table on parameter JOIN table on parameter
WHERE 1=1 AND
column = 'content'
AND upper(rc.id) = upper('"+((String)globalMap.get("row4.id")) +"')"
Expressions in tDBInput should always begin and end with double quotes.
Don't forget to cast globalMap.get() with the type of your element (here I put String)
.equals is not a DB function but a java function. I have replaced it with '='
Let me know if it's better
I'm trying to get this temp-table's name/variablename ('TT_Test') in code so I could compare it:
DEFINE TEMP-TABLE TT_Test NO-UNDO
FIELD Test AS CHAR
.
I've tried to use this without success:
PROCEDURE testProc:
DEF VAR name AS CHAR NO-UNDO.
name = TT_Test:NAME.
END PROCEDURE.
Is there any way to get it?
cName = TEMP-TABLE tt_test:NAME.
I have a temp-table called tt. I want to create a database table with the same field names and types using my temp-table.
I can't figure out how to do in Progress-4gl. Is this possible ?
Thanks.
Short answer: yes
The safest way is to do this by code is to create an incremental df and then load this.
Here is a very partial start that should get you going:
DEFINE TEMP-TABLE tt NO-UNDO
FIELD ii AS INT
FIELD cc AS CHAR
INDEX ttix IS UNIQUE PRIMARY ii.
DEF VAR hb AS HANDLE NO-UNDO.
DEF VAR hf AS HANDLE NO-UNDO.
DEF VAR ifield AS INT NO-UNDO.
hb = TEMP-TABLE tt:DEFAULT-BUFFER-HANDLE.
OUTPUT TO "tt.df".
PUT UNFORMATTED SUBSTITUTE( "ADD TABLE &1", QUOTER( hb:NAME ) ) SKIP.
DO ifield = 1 TO hb:NUM-FIELDS:
hf = hb:BUFFER-FIELD( ifield ).
PUT UNFORMATTED
SUBSTITUTE(
"ADD FIELD &1 OF &2 AS &3",
QUOTER( hf:NAME ),
QUOTER( hb:NAME ),
hf:DATA-TYPE
) SKIP.
/* to do: add other field attributes like label, initial value, decimals, format */
END.
/* to do: add indices */
OUTPUT CLOSE.
The resulting df can be loaded with:
RUN prodict/load_df.p ( "tt.df" ).
While this might be possible it's not a very good idea. Chances are you will corrupt your database. This might lead to data loss. You are much better off sticking to the provided tools even if they might have some problems.
Backup first
Always back up before applying any changes!
Single Use Database
You can modify a single usage database (if you're locally connected) online directly in the Data Dictionary. This is quite straight forward. Just use the GUI to add the table, it's fields and indices. Make sure you commit your changes.
Multi User Database
A multi user database can be changed offline or (a little bit more limited) online. Adding a new table with corresponding fields and indices is a change that can be made online. Adding the changes offline is done exactly in the same way as the "Single Use Database".
Online changes requires more work:
First of all you need two connected databases. I'll call them "live" and "development" in this case. "Development" will consist of a perfect copy of "live" when it comes to the schema. It doesn't have to have any data in it. The databases can have the same name - then you'll use a "logical name" alias when connecting to one of them.
Make the changes in "development" just like you would in an offline database or single usage database (use Data Dictionary). Make sure you commit your changes.
Enter the Data Administration Tool. Make sure you have "development" selected as default database. Go to the Admin -> Dump Data and Definitions -> Create Incremental .df file. Select your "live" database as comparative database and select a good file name (delta.df is default).
The file created is a diff-file between your "live" and "development" databases. It's wise to check it to make sure you're not dropping any tables or anything like that by mistake.
Now select the "live" database as default and go to Admin -> Load Data and Definitions. Here you can choose to add the changes offline (default) or online (check 'Add new objects on-line').
Some schema changes will require you to compile your source code again!
Using the example of #Stefan Drissen, and done some modification.
{temp-tables.i}
def input param table-handle tt.
def var hb as handle no-undo.
def var hf as handle no-undo.
def var ifield as int no-undo.
def var vindex as char no-undo.
def var vi as int.
hb = tt:default-buffer-handle.
output to value("/home/user/teste.df") append.
put unformatted substitute( "ADD TABLE &1", quoter(hb:name)) skip.
DO ifield = 1 to hb:num-fields:
hf = hb:buffer-field( ifield ).
put unformatted
substitute(
"ADD FIELD &1 OF &2 AS &3",
quoter( hf:name ),
quoter( hb:name ),
hf:data-type
) skip.
put unformatted
substitute(
"FORMAT &1",
quoter(hf:format)
) skip.
put unformatted
substitute (
"LABEL &1",
quoter( hf:name )
) skip.
put unformatted "ORDER " + string(ifield * 10) skip.
if ifield < hb:num-fields then put unformatted skip (1).
end.
put unformatted skip(1).
/*=== INDEX CREATION ===*/
assign vindex = hb:index-information(1).
if lookup("default",vindex) <= 0
then do:
put unformatted(
substitute(
"ADD INDEX &1 ON &2",
quoter(entry(01,vindex,",")),
quoter( hb:name ))) skip.
if entry(02,vindex,",") = "1"
then put unformatted "UNIQUE" skip.
if entry(03,vindex,",") = "1"
then put unformatted "PRIMARY" skip.
do vi = 5 to 20 by 2:
if num-entries(vindex,",") > vi
then put unformatted "INDEX-FIELD " entry(vi,vindex,",") " " (if entry(vi + 1,vindex,",") = "0" then "ASCENDING" else "DESCENDING") skip.
end.
end.
/*=== INDEX CREATION ===*/
put unformatted skip(1).
output close.
To make it work, just call this code on a program.p.
run createDF.p(input table nameOfYourTempTable).
CREATE TABLE new_table
AS (SELECT * FROM tt);
Just replace the new_table with the table name you want to give
What is the best way to access table data from a SAP system?
I tried it with it RFC_READ_TABLE, but this RFC returns the data in concatenated form within a single column and has a size restriction for row data.
Is there a better way to access SAP data in generic form without creating custom RFCs into the system?
I am searching for a standard RFC solution, not a custom script.
If I understand your question right, you want to read a table, but at time of programming, you don't know which table.
With Select * from (tablename)you can read with a dynamic table name.
The target field can be defined dynamic with create data.
An example (untested, currently I have no access to an SAP-system):
DATA: lv_tablename TYPE string,
ev_filelength TYPE i.
lv_tablename = 'mara'. "e.g. a parameter
DATA dref TYPE REF TO data.
CREATE DATA dref TYPE TABLE OF (lv_tablename).
FIELD-SYMBOLS: <wa> TYPE ANY TABLE.
ASSIGN dref->* to <wa>.
SELECT * FROM (lv_tablename) INTO TABLE <wa>. "Attention for test, may be large result
"<wa> is like a variable with type table mara
TYPES: BEGIN OF t_bseg,
*include structure bseg.
bukrs LIKE bseg-bukrs,
belnr LIKE bseg-belnr,
gjahr LIKE bseg-gjahr,
buzei LIKE bseg-buzei,
mwskz LIKE bseg-mwskz, "Tax code
umsks LIKE bseg-umsks, "Special G/L transaction type
prctr LIKE bseg-prctr, "Profit Centre
hkont LIKE bseg-hkont, "G/L account
xauto LIKE bseg-xauto,
koart LIKE bseg-koart,
dmbtr LIKE bseg-dmbtr,
mwart LIKE bseg-mwart,
hwbas LIKE bseg-hwbas,
aufnr LIKE bseg-aufnr,
projk LIKE bseg-projk,
shkzg LIKE bseg-shkzg,
kokrs LIKE bseg-kokrs,
END OF t_bseg.
DATA: it_bseg TYPE STANDARD TABLE OF t_bseg INITIAL SIZE 0,
wa_bseg TYPE t_bseg.
DATA: it_ekko TYPE STANDARD TABLE OF ekko.
*Select all fields of a SAP database table into in itab
SELECT *
FROM ekko
INTO TABLE it_ekko.
Try this snippet of RFC_READ_TABLE to get data in structured form:
DATA: oref TYPE REF TO cx_root,
text TYPE string,
obj_data TYPE REF TO data.
lt_options TYPE TABLE OF rfc_db_opt,
ls_option TYPE rfc_db_opt,
lt_fields TYPE TABLE OF rfc_db_fld,
ls_field TYPE rfc_db_fld,
lt_entries TYPE STANDARD TABLE OF tab512.
FIELD-SYMBOLS: <fs_tab> TYPE STANDARD TABLE.
TRY.
ls_option-text = `some query`.
APPEND ls_option TO lt_options.
ls_field-fieldname = 'PARTNER'.
APPEND ls_field TO lt_fields.
ls_field-fieldname = 'TYPE'.
APPEND ls_field TO lt_fields.
ls_field-fieldname = 'BU_GROUP'.
APPEND ls_field TO lt_fields.
ls_field-fieldname = 'BU_SORT1'.
APPEND ls_field TO lt_fields.
ls_field-fieldname = 'TITLE'.
APPEND ls_field TO lt_fields.
CALL FUNCTION 'RFC_READ_TABLE' DESTINATION dest
EXPORTING
query_table = 'BUT000'
TABLES
options = lt_options
fields = lt_fields
data = lt_entries.
CATCH cx_root INTO oref.
text = oref->get_text( ).
MESSAGE text TYPE 'E'.
ENDTRY.
IF lt_entries IS NOT INITIAL.
CREATE DATA obj_data TYPE TABLE OF but000.
ASSIGN obj_data->* TO <fs_tab>.
CREATE DATA obj_data TYPE but000.
ASSIGN obj_data->* TO FIELD-SYMBOL(<fs_line>).
LOOP AT lt_entries ASSIGNING FIELD-SYMBOL(<wa_data>).
LOOP AT lt_fields ASSIGNING FIELD-SYMBOL(<fs_fld>).
ASSIGN COMPONENT <fs_fld>-fieldname OF STRUCTURE <fs_line> TO FIELD-SYMBOL(<lv_field>).
IF <lv_field> IS ASSIGNED AND sy-subrc IS INITIAL.
<lv_field> = <wa_data>-wa+<fs_fld>-offset(<fs_fld>-length).
ENDIF.
APPEND <fs_line> TO <fs_tab>.
ENDLOOP.
ENDLOOP.
ENDIF.
IF <fs_tab> IS NOT INITIAL.
"Bingo!
ENDIF.