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
Related
I have used the List.Accumulate() to merge mutliple tables. This is the output I've got in this simple example:
Now, I need a solution to expand all these with a formula, because in real - world I need to merge multiple tables that keep increasing in number (think Eurostat tables, for instance), and modifying the code manually wastes much time in these situations.
I have been trying to solve it, but it seems to me that the complexity of syntax easily becomes the major limitation here. For instance, If I make a new step where I nest in another List.Accumulate() the Table.ExpandTableColumns(), I need to pass inside a column name of an inner table, as a text. Fine, but to drill it down actually, I first need to pass a current column name in [] in each iteration - for instance, Column 1 - and it triggers an error if I store column names to a list because these are between "". I also experimented with TransformColumns() but didn't work either.
Does anyone know how to solve this problem whatever the approach?
See https://blog.crossjoin.co.uk/2014/05/21/expanding-all-columns-in-a-table-in-power-query/
which boils down to this function
let Source = (TableToExpand as table, optional ColumnNumber as number) =>
//https://blog.crossjoin.co.uk/2014/05/21/expanding-all-columns-in-a-table-in-power-query/
let ActualColumnNumber = if (ColumnNumber=null) then 0 else ColumnNumber,
ColumnName = Table.ColumnNames(TableToExpand){ActualColumnNumber},
ColumnContents = Table.Column(TableToExpand, ColumnName),
ColumnsToExpand = List.Distinct(List.Combine(List.Transform(ColumnContents, each if _ is table then Table.ColumnNames(_) else {}))),
NewColumnNames = List.Transform(ColumnsToExpand, each ColumnName & "." & _),
CanExpandCurrentColumn = List.Count(ColumnsToExpand)>0,
ExpandedTable = if CanExpandCurrentColumn then Table.ExpandTableColumn(TableToExpand, ColumnName, ColumnsToExpand, NewColumnNames) else TableToExpand,
NextColumnNumber = if CanExpandCurrentColumn then ActualColumnNumber else ActualColumnNumber+1,
OutputTable = if NextColumnNumber>(Table.ColumnCount(ExpandedTable)-1) then ExpandedTable else ExpandAll(ExpandedTable, NextColumnNumber)
in OutputTable
in Source
alternatively, unpivot all the table columns to get one column, then expand that value column
ColumnsToExpand = List.Distinct(List.Combine(List.Transform(Table.Column(#"PriorStepNameHere", "ValueColumnNameHere"), each if _ is table then Table.ColumnNames(_) else {}))),
#"Expanded ColumnNameHere" = Table.ExpandTableColumn(#"PriorStepNameHere", "ValueColumnNameHere",ColumnsToExpand ,ColumnsToExpand ),
Background: I have a table named 'SNR_log', out of which I have queried to obtain below list.
Next,I designed a split form, with a text box.In this text box SNR is enterted to find and filter from the list. To perform this operation 'Apply filter' macro is used with a where condition,
[SNR] Like "*" & [Forms]![SNR_History]![Text17] & "*"
Problem: Is it possible to filter by inserting SNR in the textbox and obtaining the below result.
In other words, with SNR as input, display UID and all the SNR under that UID.
You're not going to be able to do this in an "Apply Filter" macro. You're going to need some VBA behind the scenes.
The first thing you want to do is query your data for the UID. You would do that like this:
Dim db as Database
Dim Rec as Recordset
Dim UIDx as Integer
Dim strSQL as String
Dim strSQL2 as String
Set db = CurrentDB
strSQL = "SELECT UID FROM SNR_Log " & _
"WHERE [SNR] Like ""*" & [Forms]![SNR_History]![Text17] & "*""
Set Rec = db.OpenRecordSet(strSQL)
'Now grab the UID that's returned and put it inside a variable
UIDx = Rec(0)
Now you have your UID that you want to query on. So set up your RecordSource based on that:
strSQL2 = "SELECT * FROM SNR_Log WHERE [UID] = " & UIDx & ""
Forms!Me!frmSubFormName.RecordSource = strSQL2
Forms!Me!frmSubFormName.ReQuery
That will change the record source of the form, and refresh the form so it's looking at the current record source you just set it to.
You may have to putz with strSQL a bit, I can't remember exactly how to work with the Like operator inside a SQL string. I'm pretty sure it's correct, though.
I was able to work around it in a simple way. I created a main form and subform. The record source for subform is the text box where SNR is entered. Once a command button is clicked with following event procedure in it I obtained the desired result;
Private Sub Command4_Click()
Text0.SetFocus
Me.SNR_log_Subform.Form.RecordSource = "Select * from SNR_Log where UID in(Select UID from SNR_Log where SNR like '*" & Text0.Text & "*')"
End Sub
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.
Is there a automation tool which can automate the software build on Team Developer (v6.0).
I have tried with multiple automation tools to spy the table object in the application, it identifies it as Gupta ChildTable. But I am not able to retrieve the values from the row.
For example:
1. I have 10 rows in the table(grid) with 12 columns. I need to find the value "AAAAA" contained in first column and select that particular row via Automation.
2. I have 10 rows in the table(grid) with 12 columns. I need to find the value "AAAAA" contained in first column and click on particular cell in that row to input the data via Automation.
Thanks in advance.
Use VisTblFindString . This function ( and many others ) are included into your TD code if include 'VT.apl' in your include libraries .
VisTblFindString will return the Row - so then you simply set context to that row using SalTblSetContext( hWndForm, nRow ) , and then you can refer to the contents of each cell by name to return the value.
Syntax
nRow = VisTblFindString(hWndTable, nStartRow, hWndColumn, sString)
Handle: hWndTable
Number: nStartRow
Number: hWndColumn
String: sString
Description
Locates a string value within a column.
The string must match exactly, but case is ignored. Searching ends when the last row in the table is checked. A SAM_FetchRow message is sent for all rows that have not yet been fetched into the cache.
You can use the pattern matching characters understood by the function SalStrScan. The percent character (%) matches any set of characters. The underscore character ( _ ) matches any single character.
Parameters
hWndTable Table window handle.
nStartRow Row number at which to start the search.
hWndColumn Handle of column to search. Use hWndNULL to search all string columns.
sString String for which to search.
Return Value
Number: The row number if sString is found, or -1 if not found.
Example:
Set nRow = VisTblFindString (twOrders, 0, colDesc, 'AAAAAA')
Call SalTblSetContext( twOrders , nRow )
( Now you can get the value of any cell in nRow by referring to the Column Name )
e.g. Set sCellValue = twOrders.colDesc or Set sCellValue = twOrders.colId etc.
Rows ( or anything what-so-ever in a TableWindow - even the cell borders , backgrounds , lines, row headers etc ) can be treat as an 'Item' or 'Object' by TeamDeveloper . Recommend you use MTbl - it is an invaluable set of add-on functions that make dealing with Tables a breeze. I know of no sites using TableWindows that don't use MTbl. In terms of rows , you can define any row as an Item or Object and manipulate it accordingly. See M!Tbl ( a TableWindows extention ) and specifically fcMTblItem.DefineAsRow( hWndTbl, nRow ).
BTW , you can also use MTbl to completely change the look and feel of your TableWindows , to give them a real modern look.
Very rough napkin code, don't have TD on this computer. Not that you can copy&paste this easily anyway due to the code structure, only line by line.
tbl1 is the name of the table, col1 is the name of the column, substitute to fit your program.
Set nRow = TBL_MinRow
While SalTblFindNextRow( tbl1, nRow, 0, 0 )
Call SalTblSetContext( tbl1, nRow )
If tbl1.col1 = "AAAAA"
Call SalTblSetFocusCell( tbl1, nRow, tbl1.col1, 0, -1 )
Break
This should run through each row, check whether col1 has the chosen value, and then activates edit mode for that cell - provided the column is editable.
I tried looking for an example of using Arel::UpdateManager to form an update statement with a from clause (as in UPDATE t SET t.itty = "b" FROM .... WHERE ...), couldn.t find any. The way I've seen it, Arel::UpdateManager sets the main engine on initialization and allows to set the various fields and values to update. Is there actually a way to do this?
Another aside would be to find out how to express Postgres posix regex matching into ARel, but this might be impossible by now.
As far as I see the current version of arel gem is not support FROM keyword for the sql query. You can generate a query using the SET, and WHERE keywords only, like:
UPDATE t SET t.itty = "b" WHERE ...
and the code, which copies a value from field2 to field1 for the units table, will be like:
relation = Unit.all
um = Arel::UpdateManager.new(relation.engine)
um.table(relation.table)
um.ast.wheres = relation.wheres.to_a
um.set(Arel::Nodes::SqlLiteral.new('field1 = "field2"'))
ActiveRecord::Base.connection.execute(um.to_sql)
Exactly you can use the additional method to update a relation. So we create the Arel's UpdateManager, assigning to it the table, where clause, and values to set. Values shell be passed to the method as an argument. Then we need to add FROM keyword to the generated SQL request, we add it only if we have access to external table of the specified one by the UPDATE clause itself. And at the last we executes the query. So we get:
def update_relation!(relation, values)
um = Arel::UpdateManager.new(relation.engine)
um.table(relation.table)
um.ast.wheres = relation.wheres.to_a
um.set(values)
sql = um.to_sql
# appends FROM field to the query if needed
m = sql.match(/WHERE/)
tables = relation.arel.source.to_a.select {|v| v.class == Arel::Table }.map(&:name).uniq
tables.shift
sql.insert(m.begin(0), "FROM #{tables.join(",")} ") if m && !tables.empty?
# executes the query
ActiveRecord::Base.connection.execute(sql)
end
The you can issue the the relation update as:
values = Arel::Nodes::SqlLiteral.new('field1 = "field2", field2 = NULL')
relation = Unit.not_rejected.where(Unit.arel_table[:field2].not_eq(nil))
update_relation!(relation, values)