I've created a very simple form with two textboxes and an 'update locations' button, the inputted values of which should be updating to two linked tables in a database. I'm eventually trying to put this in a standalone application but for now I just want the form itself to work. I'm very new to FoxPro so I don't know if it's possible for just a form to update my tables, or if there's some other issue at work.
Here's the code for my 'update locations' button (OldLoc is the first textbox and NewLoc the second):
SET DATABASE TO LOCATIONS
CLOSE ALL DATABASES
OPEN DATABASE locations\locations EXCLUSIVE
IF this.parent.OldLoc.Value == "" then
MESSAGEBOX('Please fill in the old location.', 48, 'Missing Location Information')
this.parent.OldLoc.SetFocus
ELSE
INDEX ON table1.loc TAG loc
SET ORDER TO loc
SEEK this.parent.OldLoc.Value IN table1
IF FOUND()
IF this.parent.NewLoc.Value == "" then
MESSAGEBOX('Please fill in the new location.', 48,
'Missing Location Information') this.parent.NewLoc.SetFocus
UPDATE table1 SET loc = this.parent.NewLoc.Value ;
WHERE loc = this.parent.OldLoc.value
UPDATE table2 SET loc = this.parent.NewLoc.Value ;
WHERE loc = this.parent.OldLoc.value
ENDIF
ENDIF
ENDIF
Any input you have is appreciated!
you are doing redundant work in your click event... Closing the database, then re-opening, then opening exclusive. The tables you are trying to update, should already have an index on the key columns you ever plan to join or search based on. Once that is done, you don't need to explicitly create the index tag over and over... Once an index "TAG" is created, you don't need to do again..
So, that being said...
Open the form, and open the "INIT" event. In there, that is where you can explicitly open the tables for use while the form is in use...
IF NOT DBUSED( "Locations" )
*/ You only need exclusive if you will be modifying the database.
*/ The indexes should already exist before the form ever starts
Open Database Locations\Locations SHARED
ENDIF
Now, the "CLICK" event of your update button... Pre-validate that there are values, don't worry about trying to seek or update unless BOTH are filled in...
IF ALLTRIM( This.Parent.OldLoc.Value ) == "";
OR ALLTRIM( This.Parent.NewLoc.Value ) == ""
*/ Simple one message about BOTH being required
messagebox( "Please fill in both Old and New Locations.", 48, ;
"Missing Location Information" )
ENDIF
*/ Then, update... if no entries match, no records will be updated.
*/ VFP sometimes chokes with multiple "." values such as form values
*/ as SQL commands are expecting "alias.field" format...
lcOldValue = This.Parent.OldLoc.Value
lcNewValue = This.Parent.NewLoc.Value
Update Locations!Table1;
set Loc = lcNewLoc;
where Loc = lcOldLoc
*/ Were there any updates?
llAnyUpdates = _Tally > 0
*/ Now, the OTHER table too...
Update Locations!Table1;
set Loc = lcNewLoc;
where Loc = lcOldLoc
*/ Were there any updates on THIS cycle...
llAnyUpdates = llAnyUpdates OR _Tally > 0
if llAnyUpdates
messagebox( "Locations have been updated.", 0, "Update complete" )
else
messagebox( "No such location found to be updated.", 0, "No Records Updated" )
endif
You might also want to take a look at the data environment of the form.
You can select which tables the form is to open when the form loads, this saves explicitly opening them either in the forms load event or in the forms init event
My humble offering for the Click Event of the 'Update Locations' button is :-
LOCAL lcOldLoc,lcNewLoc
WITH THISFORM
lcOldLoc=.oldloc.VALUE
lcNewLoc=newloc.VALUE
DO CASE
CASE EMPTY(lcOldLoc)
MESSAGEBOX("PLEASE ENTER OLD LOCATION",16,"ERROR")
CASE EMPTY(lcNewLoc)
MESSAGEBOX("NEW LOCATION CAN'T BE EMPTY",16,"ERROR")
OTHERWISE
UPDATE table1 SET loc=lcNewLoc WHERE loc=lcOldLoc
DO CASE
CASE _TALLY=0
MESSAGEBOX("OLD LOCATION NOT FOUND",16,"ERROR")
OTHERWISE
UPDATE tabel2 SET loc=lcNewLoc WHERE loc=lcOldLoc
ENDCASE
ENDCASE
ENDWITH
This should be the only code required. Clearly there are many improvements that can be made to the above , but in essence that will do the job.
If you would like to see the above form built from scratch, pop an email to support#foxsoft.co.uk and I will give you a live demonstration.
Related
I referred to this link create a download zip button to download files in zip format in Oracle apex latest version 22.2. It is working fine without any issues but only concern is; it downloads all the files in one zip file. Whereas my requirement is to include a checkbox on a report (either IG or IR) and to download selected files in one zip file.
Below is the table I am referring to. Its from Oracle apex sample files upload and download.
select
ID,
ROW_VERSION_NUMBER,
PROJECT_ID,
FILENAME,
FILE_MIMETYPE,
FILE_CHARSET,
FILE_BLOB,
FILE_COMMENTS,
TAGS,
CREATED,
CREATED_BY,
UPDATED,
UPDATED_BY
from EBA_DEMO_FILES
I tried searching over the internet and found few links pointing to APEX_ZIP, PL/SQL compress blob etc. But could not see any demo or working model similar to the link I provided above.
If anybody has working demo or blog,I request to share it. Many thanks.
Update: As suggested by Koen Lostrie, I am updating Page process code below:
DECLARE
l_id_arr apex_t_varchar2;
l_selected_id_arr apex_t_varchar2;
var_zip blob;
BEGIN
-- push all id values to an array
FOR i IN 1..APEX_APPLICATION.G_F03.COUNT LOOP
apex_string.push(l_id_arr,APEX_APPLICATION.G_F03(i));
FOR j IN 1 .. APEX_APPLICATION.G_F01.COUNT LOOP
IF APEX_APPLICATION.G_F01(j) = APEX_APPLICATION.G_F03(i) THEN
-- push all selected emp_id values to a 2nd array
apex_string.push(l_selected_id_arr,APEX_APPLICATION.G_F03(i));
END IF;
END LOOP;
END LOOP;
-- Create/clear the ZIP collection
APEX_COLLECTION.CREATE_OR_TRUNCATE_COLLECTION(
p_collection_name => 'ZIP');
-- Loop through all the files in the database
begin
for var_file in (select fi.filename, fi.file_blob, pr.project
from eba_demo_files fi
inner join eba_demo_file_projects pr on fi.project_id = pr.id
where fi.id in (SELECT column_value FROM table(apex_string.split(apex_string.join(l_selected_id_arr,':'),':'))))
loop
-- Add each file to the var_zip file
APEX_ZIP.ADD_FILE (
p_zipped_blob => var_zip,
p_file_name => var_file.project || '/' || var_file.filename,
p_content => var_file.file_blob );
end loop;
exception when no_data_found then
-- If there are no files in the database, handle error
raise_application_error(-20001, 'No Files found!');
end;
-- Finish creating the zip file (var_zip)
APEX_ZIP.FINISH(
p_zipped_blob => var_zip);
-- Add var_zip to the blob column of the ZIP collection
APEX_COLLECTION.ADD_MEMBER(
p_collection_name => 'ZIP',
p_blob001 => var_zip);
END;
Once page process is done, follow step 3 and 4 from the link provided in OP.
Below is the updated query:
select
ID,
ROW_VERSION_NUMBER,
PROJECT_ID,
FILENAME,
FILE_MIMETYPE,
FILE_CHARSET,
FILE_BLOB,
FILE_COMMENTS,
TAGS,
CREATED,
CREATED_BY,
UPDATED,
UPDATED_BY,
APEX_ITEM.CHECKBOX(1,ID) checkbox,
APEX_ITEM.TEXT(2,FILENAME) some_text,
APEX_ITEM.HIDDEN(3,ID) hidden_empno
from EBA_DEMO_FILES
Big Thanks to Koen Lostrie.
All credits goes to Koen Lostrie.
Thanks,
Richa
This is just an answer to the last comment - the base question was answered in the comments. The question in the comment is "how do I include APEX_ITEM.HIDDEN columns in my report without hiding the columns".
When the columns are hidden in the report, they're not rendered in the DOM, so the values do not exist when the form is posted. That is the reason you're getting the error.
However, take a step back and check what APEX_ITEM.HIDDEN generates. Add a column of type APEX_ITEM.HIDDEN to the report and inspect the column in the browser tools. It generates an input element of type "hidden", so the value is not shown in the report. So to include the column in your report but not make it visible on the screen, just concatenate it to an existing other column:
In your case, with the select from the question that would be:
select
APEX_ITEM.HIDDEN(3,ID) || APEX_ITEM.HIDDEN(2,FILENAME) || ID,
ROW_VERSION_NUMBER,
PROJECT_ID,
FILENAME,
FILE_MIMETYPE,
FILE_CHARSET,
FILE_BLOB,
FILE_COMMENTS,
TAGS,
CREATED,
CREATED_BY,
UPDATED,
UPDATED_BY,
APEX_ITEM.CHECKBOX(1,ID) checkbox
from EBA_DEMO_FILES
Note that filename can also be in a hidden element.
I have to implement a trigger which would:
7) Show the DDL for how a trigger can be used to transfer all rental copies from a store being deleted from the Store information table to the central store
8) Show how this trigger can be extended to make
sure that the central store is never deleted from the database
So far I have done this:
CREATE OR REPLACE TRIGGER stores BEFORE DELETE ON stores FOR
EACH ROW BEGIN IF DELETING WHERE cvr = 123456789 THEN
Raise_Application_Error ( num => -20050, msg => 'You can
not delete Main Store.'); END IF; IF DELETING THEN
UPDATE store_id=123456789 ON movies WHERE isActive = 0 END
IF; END;
so main store is with cvr which is written, but it gives me a compilation error. Any help? Thanks in advance.
You have two errors in your code.
there is no "DELETING WHERE" expression, you have to use two boolean exceptions like this:
IF DELETING AND :old.cvr = 123456789 THEN...
:old.cvr refers to value of cvr column in deleted record
Syntax of UPDATE clause is
UPDATE table_name
SET column_name1 = value1,
column_name1 = value2
WHERE where_clause;
in your case probably somethink like this:
UPDATE movies
set store_id = 123456789
WHERE store_id = :old.cvr
I guess, I don't know required functionality
I m trying to write if statement to give error message if user try to add existing ID number.When i try to enter existing id i get error .untill here it s ok but when i type another id no and fill the fields(name,adress etc) it doesnt go to database.
METHOD add_employee.
DATA: IT_EMP TYPE TABLE OF ZEMPLOYEE_20.
DATA:WA_EMP TYPE ZEMPLOYEE_20.
Data: l_count type i value '2'.
SELECT * FROM ZEMPLOYEE_20 INTO TABLE IT_EMP.
LOOP AT IT_EMP INTO WA_EMP.
IF wa_emp-EMPLOYEE_ID eq pa_id.
l_count = l_count * '0'.
else.
l_count = l_count * '1'.
endif.
endloop.
If l_count eq '2'.
WA_EMP-EMPLOYEE_ID = C_ID.
WA_EMP-EMPLOYEE_NAME = C_NAME.
WA_EMP-EMPLOYEE_ADDRESS = C_ADD.
WA_EMP-EMPLOYEE_SALARY = C_SAL.
WA_EMP-EMPLOYEE_TYPE = C_TYPE.
APPEND wa_emp TO it_emp.
INSERT ZEMPLOYEE_20 FROM TABLE it_emp.
CALL FUNCTION 'POPUP_TO_DISPLAY_TEXT'
EXPORTING
TITEL = 'INFO'
TEXTLINE1 = 'Record Added Successfully.'.
elseif l_count eq '0'.
CALL FUNCTION 'POPUP_TO_DISPLAY_TEXT'
EXPORTING
TITEL = 'INFO'
TEXTLINE1 = 'Selected ID already in database.Please type another ID no.'.
ENDIF.
ENDMETHOD.
I'm not sure I'm getting your explanation. Why are you trying to re-insert all the existing entries back into the table? You're just trying to insert C_ID etc if it doesn't exist yet? Why do you need all the existing entries for that?
If so, throw out that select and the loop completely, you don't need it. You have a few options...
Just read the table with your single entry
SELECT SINGLE * FROM ztable INTO wa WITH KEY ID = C_ID etc.
IF SY-SUBRC = 0.
"this entry exists. popup!
ENDIF.
Use a modify statement
This will overwrite duplicate entries with new data (so non key fields may change this way), it wont fail. No need for a popup.
MODIFY ztable FROM wa.
Catch the SQL exceptions instead of making it dump
If the update fails because of an exception, you can always catch it and deal with exceptional situations.
TRY .
INSERT ztable FROM wa.
CATCH sapsql_array_insert_duprec.
"do your popup, the update failed because of duplicate records
ENDTRY.
I think there's a bug when appending in internal table 'IT_EMP' and inserting in 'ZEMPLOYEE_20' table.
Suppose you append the first time and then you insert. But when you append the second time you will have 2 records in 'IT_EMP' that are going to be inserted in 'ZEMPLOYEE_20'. That is because you don't refresh or clear the internal table and there you will have a runtime error.
According to SAP documentation on 'Inserting Lines into Tables ':
Inserting Several Lines
To insert several lines into a database table, use the following:
INSERT FROM TABLE [ACCEPTING DUPLICATE KEYS] . This
writes all lines of the internal table to the database table in
one single operation. The same rules apply to the line type of
as to the work area described above. If the system is able to
insert all of the lines from the internal table, SY-SUBRC is set to 0.
If one or more lines cannot be inserted because the database already
contains a line with the same primary key, a runtime error occurs.
Maybe the right direction here is trying to insert the work area directly but before you must check if record already exists using the primary key.
Check the SAP documentation on this issue clicking the link before.
On the other hand, once l_count is zero because of l_count = l_count * '0'. that value will never change to any other number making that you won't append or insert again.
why are you retrieving all entries from zemployee_20 ?
You can directly check wether the 'id' exists already or not by using select single. If exists, then show message, if not, add.
It is recommended to retrieve only one field when its needed and not the entire table with asterisc *
SELECT single employee_id FROM ZEMPLOYEE_20 where employee_id = p_id INTO v_id. ( or field in structure )
if sy-subrc = 0. "exists
"show message
else. "not existing id
"populate structure and then add record to Z table
endif.
Furthermore, l_count is not only unnecessary but also bad implemented.
You can directly use the insert query,if the sy-subrc is unsuccessful raise the error message.
WA_EMP-EMPLOYEE_ID = C_ID.
WA_EMP-EMPLOYEE_NAME = C_NAME.
WA_EMP-EMPLOYEE_ADDRESS = C_ADD.
WA_EMP-EMPLOYEE_SALARY = C_SAL.
WA_EMP-EMPLOYEE_TYPE = C_TYPE.
INSERT ZEMPLOYEE_20 FROM WA_EMP.
If sy-subrc <> 0.
Raise the Exception.
Endif.
Can any one please help me how to Insert the data into database from window form. How to fetch the data to show on window form & same to update the data from database. I am looking for the code that contain sql query with in the code not from the quick select data window. I am very new in powerbuilder.I want to write a code fetch update data from the code any where & show anywhere.
Thanks
I'm not quite sure about your question. Try going to this website http://powerbuilder.hyderabad-colleges.com.
Look for Datawindow control and Datawndow object topics.
There are other ways to manipulate data in Powerbuilder like using Embeded SQL (stored procedure and cursors).
I hope this will help you.
The whole point of the Datawindow is that it does all that work for you.
Retrieve data:
dw_1.Retrieve(arguments)
Update the database:
dw_1.Update()
I'm not understanding the question entirely you must be having trouble with a multi-table update they can be challenging for a new developer.
This will do an update into two tables I did it in a hurry so might be a syntax error or two.
// insert a row
li_row = dw_1.insertrow(0)
dw_1.setitem(li_row, 'col1', 'try reading')
dw_1.setitem(li_row, 'col2', 'the PowerBuilder')
dw_1.setitem(li_row, 'col3', 'manual next time')
// do accept text left out for purposes of brevity
// Update first table and dont bother with another accepttext
// since weve already done one and dont set the updateflags
// so second half of update creates correct sql statement
li_rtn = dw_1.Update(false, false)
if li_rtn = 1 then
dw_1.modify('tbl1_col1.Update = No')
dw_1.modify('tbl1_col2.Update = No')
dw_1.modify('tbl1_col3.Update = No')
dw_1.modify('tbl1_id.Key = No')
dw_1.modify("Datawindow.Table.updateable = 'tbl2'")
dw_1.modify('tbl2_col1.Update = Yes')
dw_1.modify('tbl2_col2_id.Key = Yes')
li_rtn = dw_1.update(false, true)
if li_rtn = 1 then
commit using sqlca;
else
rollback using sqlca;
end if
end if
// cleanup the temp recs
li_rowcount = dw_1.rowcount()
for li_row = li_rowcount to 1 step -1
dw_1.deleterow(li_row)
next
dw_1.Update()
I'm still newbie in VFP, I'm looking for advices and suggestions on how to pull SQL Server table fields into VFP combobox(or other objects as well, if its better), just like auto-complete but from sql server database.
I have about 2 columns and 1000 rows inside the table, the combobox should only show the second columns field for user to choose but use the chosen first column field to be recorded to another table. I hope you get the idea.
Thanks in advance for your feedback.
I would handle it a little differently since you want BOTH columns.. one shown, and one for the actual data. Comboboxes can be directly bound to a table or cursor (cursor is nothing more than a temp table that automatically gets erased when closed).
In the INIT() of your combobox, I would run your query to your SQL database, but just grab those two columns...
* -- Try to connect and get a connection handle.
lnHandle = Sqlstringconnect( YourConnectionString )
if lnHandle < 1
*/ Return, get out, unable to get handle to SQL server
messagebox( "Unable to connect to SQL" )
return
end
lcSQLCmd = "Select ShowThisColumn, ButUseThisColumn from YourDatabase.dbo.YourTable"
lnSQLResult = SQLExec( lnHandle, lcSQLCmd, "C_ChoicesFromSQL" )
sqldisconnect( lnHandle )
if lnSQLResult < 1
messagebox( "Unable to retrieve data" )
return
endif
*/ Ok, we have the data, now the binding. VFP Can set the row source directly
*/ to the ALIAS ("C_ChoicesFromSQL") from the SQL query directly, no need to scan
*/ and add items. Just tell it both columns.
This.ColumnCount = 2 && You will bind BOTH columns to the combobox
This.BoundColumn = 2 && You want the data from the SECOND column bound for storage
This.BoundTo = .T. && Yes, bind to whatever the Control Source of the combobox
*/ Here's the trick. Set the column widths = whatever you have visible on the form
*/ for the first column, and the second column has a visible width of 0
This.ColumnWidths = alltrim( str( This.Width )) + ",0"
*/ Now, the row source...
This.RowSource = "C_ChoicesFromSQL.ShowThisColumn, ButUseThisColumn"
This.RowSourceType = 2 && bound to alias
*/ Fixed dropdown LIST, dont allow others to type in their own values
*/ but only choose from the data available in your source.
This.Style = 2
Well, here's how I'd do it programmatically assuming a combobox called 'cboSQL' on the form, and this code in the form Init() method (this only does one column in the combo but check the VFP help under AddItem() ):
Local lnHandle, lnResult As Integer
Local lcConnstring, lcCommand As String
Local llReturn As Boolean
* -- Do the default behaviour.
llReturn = DoDefault()
If llReturn
* -- Try to connect to a local SQL Express 2008 instance.
lcServername = ".\SQLEXPRESS"
lcDbname = "umbraco"
lcConnstring = [server=]+ lcServername+ [;driver={SQL Server};database=]+ lcDbname
lcConnstring = lcConnstring + [;DSN='';Trusted_Connection=Yes]
* -- Try to connect and get a connection handle.
lnHandle = Sqlstringconnect(lcConnstring)
* -- Got a connection ?
If lnHandle > 0
* -- Run a query, put the results in a VFP cursor called 'results'.
lcCommand = "select top 1000 logComment from [umbraco].[dbo].[umbracoLog]"
lnResult = SQLExec(lnHandle, lcCommand, "results")
If lnResult = -1
Messagebox("Error running SELECT.", 48, "Error!")
Else
* -- Got some rows, populate combobox.
Select results
Scan
* -- Transform() just stringifies the column.
Thisform.cboSql.AddItem(Transform(results.logComment))
Endscan
Endif
* -- Tidy up.
Use In Select("results")
SQLDisconnect(lnHandle)
Else
Messagebox("Couldn't connect to server.", 48, "Error!")
llReturn =.F.
Endif
Endif
Return llReturn