VFP ComboBox Content from SQL Server 2005 Express table - combobox

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

Related

c++ builder: getting values via ADOQuery using SELECT

The question is as for delphi coders as for c++ builder coders, cuz I'm using the same components.
I'm trying to fill labels on the form by the data from database. I do a SELECT query via TADOQuery. But when I try to get a result, I always get an error like "ADOQuery1: Field 'count' not found".
'id' passed to the function is an autoincrement field value, which is EXACTLY exists in database (it was got via DBLookupComboBox). Also, executing the query manually to show result in DBGrid is successfull.
Querying without parameters and writing 'id' value to query string fails too.
What's the problem? Here's the code.
void TSellForm::LoadData(int id) {
TADOQuery* q = DataModule1->ADOQuery1;
q->Active = false;
try
{
q->SQL->Text = "select * from drugs where(id=:id)";
q->Parameters->ParamByName("id")->Value = IntToStr(id);
q->ExecSQL();
this->LabelAvail->Caption = q->FieldByName("count")->Value;
}
catch (Exception* e) {
MessageBox(NULL, PChar(WideString(e->Message)),
L"Exception", MB_OK|MB_ICONWARNING);
}
q->SQL->Clear();
}
ExecSQL is only used for SQL statements that don't return a recordset, and to determine the results you use RowsAffected.
For SELECT statements (which return a recordset), you use Open or set Active to true.
Also, count is a reserved word in most SQL dialects (as in SELECT Count(*) FROM ..., so if you have a column with that name you're going to need to escape it, typically by using either [] or double-quotes around it or by aliasing it in the SELECT itself.
ADOQuery1->Close();
ADOQuery1->SQL->Text= "SELECT * FROM reportTble WHERE (firstName =:firstName) " ;
ADOQuery1->Parameters->ParamByName("firstName")->Value = textBox->Text ;
ADOQuery1->Open();
This is how you can use ADOQuery

oracle forms builder pl/sql - access a column that is dervied from other columns

I have a view in my database that has a bunch of fields derived from other information in the database, this is how the view is defined:
create view patient_account_view AS
select patient.p_mrn,
p_fname,
p_lname,
ammount_paid,
quantity*item_cost + repeats*item_cost "ammount_owing",
(quantity*item_cost + repeats*item_cost) - ammount_paid "balance"
from patient_account,
patient,
diagnosis,
prescribed_treatment,
items_used,
item,
perscription
where patient.p_mrn = diagnosis.p_mrn AND
patient_account.p_mrn = patient.p_mrn AND
diagnosis.prescribed_treatment_id = prescribed_treatment.prescribed_treatment_id AND
prescribed_treatment.prescribed_treatment_id = perscription.prescribed_treatment_id AND
items_used.ptreatment_id = prescribed_treatment.prescribed_treatment_id AND
items_used.item_number = item.item_number;
I would like to use pl/sql to access the information in the view to stick it into a form, but I'm getting a 'bad bind variable' error. How do I access this kind of attribute without having to recalculate the information stored there?
Here is the plsql that is problematic:
DECLARE
pmrn patient.p_mrn%TYPE;
var_ptuple patient%ROWTYPE;
var_accttuple patient_account%ROWTYPE;
BEGIN
pmrn := :PATIENT_BLOCK.MRN_FIELD;
SELECT * INTO var_ptuple from patient WHERE patient.p_mrn = pmrn;
SELECT * INTO var_accttuple from patient_account_view WHERE patient_account_view.p_mrn = pmrn;
:PATIENT_BLOCK.FNAME := var_ptuple.p_fname;
:PATIENT_BLOCK.LNAME := var_ptuple.p_lname;
:PATIENT_BLOCK.BALACNCE_OWING := var_accttuple.balance;
END;
The columns of your view patient_account_view do not match exactly the columns of the table patient_account, but in your code you have:
var_accttuple patient_account%ROWTYPE;
which means when you run this:
SELECT * INTO var_accttuple from patient_account_view ...
You haven't specified which columns get mapped to which record attributes, so Oracle requires that the column list matches exactly.
In this case, I'd expect you probably want to change the definition of the variable, e.g.
var_accttuple patient_account_view%ROWTYPE;
Side note
Since you're only using one attribute from the view, you can simplify your code as follows:
SELECT balance INTO :PATIENT_BLOCK.BALACNCE_OWING
from patient_account_view WHERE patient_account_view.p_mrn = pmrn;
and you no longer need var_accttuple.

FoxPro form not updating tables

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.

SQLite to Oracle

I have a SQLite database in one system, I need to extract the data stored in SQLite to Oracle database. How do I do this?
Oracle provides product called the Oracle Database Mobile Server (previously called Oracle Database Lite) which allows you to synchronize between a SQLite and an Oracle database. It provides scalable bi-directional sync, schema mapping, security, etc. The Mobile Server supports both synchronous and asynchronous data sync. If this is more than a one-time export and you need to keep your SQLite and Oracle Databases in sync, this is a great tool!
Disclaimer: I'm one of the Product Managers for Oracle Database Mobile Server, so I'm a bit biased. However, the Mobile Server really is a great tool to use for keeping your SQLite (or Berkeley DB) and Oracle Databases in sync.
You'll have to convert the SQLite to a text file (not certain of the format) and then use Oracle to load the database from text (source is http://www.orafaq.com/wiki/SQLite). You can use the .dump command from the SQLite interactive shell to dump to a text file (see the docs for syntax).
SQL Loader is a utility that will read a delimited text file and import it into an oracle database. You will need to map out how each column from your flat file out of sqlite matches to the corresponding one in the Oracle database. Here is a good FAQ that should help you get started.
If you are a developer, you could develop an application to perform the sync. You would do
SELECT name FROM sqlite_master WHERE type='table'
to get the table names, then you could re-create them in Oracle (you can do DROP TABLE tablename in Oracle first, to avoid a conflict, assuming SQLite will be authoritative) with CREATE TABLE commands. Getting the columns for each one takes
SELECT sql FROM sqlite_master WHERE type='table' and name='MyTable'
And then you have to parse the result:
string columnNames = sql.replace(/^[^\(]+\(([^\)]+)\)/g, '$1').replace(/ [^,]+/g, '').split(',');
string[] columnArray = columnNames.Split(',');
foreach (string s in columnArray)
{
// Add column to table using:
// ALTER TABLE MyTable ADD COLUMN s NVARCHAR(250)
}
A StringBuilder can be used to collect the table name with its columns to create your INSERT command. To add the values, it would just be a matter of doing SELECT * FROM MyTable for each of the tables during your loop through the table names you got back from the initial query. You would iterate the columns of the rows of the datatable you were returned and add the values to the StringBuilder:
INSERT INTO MyTable ( + columnA, columnB, etc. + ) VALUES ( datarow[0], datarow[1], etc. + ).
Not exactly like that, though - you fill in the data by appending the column name and its data as you run through the loops. You can get the column names by appending s in that foreach loop, above. Each column value is then set using a foreach loop that gives you each object obj in drData.ItemArray. If all you have are string fields, it's easy, you just add obj.ToString() to your StringBuilder for each column value in your query like I have below. Then you run the query after collecting all of the column values for each row. You use a new StringBuilder for each row - it needs to get reset to INSERT INTO MyTable ( + columnA, columnB, etc. + ) VALUES ( prior to each new row, so the new column values can be appended.
If you have mixed datatypes (i.e. DATE, BLOB, etc.), you'll need to determine the column types along the way, store it in a list or array, then use a counter to determine the index of that list/array slot and get the type, so you know how to translate your object into something Oracle can use - whether that means simply adding to_date() to the result, with formatting, for a date (since SQLite stores these as date strings with the format yyyy-MM-dd HH:mm:ss), or adding it to an OracleParameter for a BLOB and sending that along to a RunOracleCommand function. (I did not go into this, below.)
Putting all of this together yields this:
string[] columnArray = null;
DataTable dtTableNames = GetSQLiteTable("SELECT name FROM sqlite_master WHERE type='table'");
if (dtTableNames != null && dtTableNames.Rows != null)
{
if (dtTableNames.Rows.Count > 0)
{
// We have tables
foreach (DataRow dr in dtTableNames.Rows)
{
// Do everything about this table here
StringBuilder sb = new StringBuilder();
sb.Append("INSERT INTO " + tableName + " ("); // we will collect column names here
string tableName = dr["NAME"] != null ? dr["NAME"].ToString() : String.Empty;
if (!String.IsNullOrEmpty(tableName))
{
RunOracleCommand("DROP TABLE " + tableName);
RunOracleCommand("CREATE TABLE " + tableName);
}
DataTable dtColumnNames = GetSQLiteTable("SELECT sql FROM sqlite_master WHERE type='table' AND name='"+tableName+"'");
if (dtColumnNames != null && dtColumnNames.Rows != null)
{
if (dtColumnNames.Rows.Count > 0)
{
// We have columns
foreach (DataRow drCol in dtTableNames.Rows)
{
string sql = drCol["SQL"] != null ? drCol["SQL"].ToString() : String.Empty;
if (!String.IsNullOrEmpty(sql))
{
string columnNames = sql.replace(/^[^\(]+\(([^\)]+)\)/g, '$1').replace(/ [^,]+/g, '').split(',');
columnArray = columnNames.Split(',');
foreach (string s in columnArray)
{
// Add column to table using:
RunOracleCommand("ALTER TABLE " + tableName + " ADD COLUMN " + s + " NVARCHAR(250)"); // can hard-code like this or use logic to determine the datatype/column width
sb.Append("'" + s + "',");
}
sb.TrimEnd(",");
sb.Append(") VALUES (");
}
}
}
}
// Get SQLite Table data for insertion to Oracle
DataTable dtTableData = GetSQLiteTable("SELECT * FROM " + tableName);
if (dtTableData != null && dtTableData.Rows != null)
{
if (dtTableData.Rows.Count > 0)
{
// We have data
foreach (DataRow drData in dtTableData.Rows)
{
StringBuilder sbRow = sb; // resets to baseline for each row
foreach (object obj in drData.ItemArray)
{
// This is simplistic and assumes you have string data for an NVARCHAR field
sbRow.Append("'" + obj.ToString() + "',");
}
sbRow.TrimEnd(",");
sbRow.Append(")");
RunOracleCommand(sbRow.ToString());
}
}
}
}
}
}
All of this assumes you have a RunOracleCommand() void function that can take a SQL command and run it against an Oracle DB, and a GetSQLiteTable() function that can return a DataTable from your SQLite DB by passing it a SQL command.
Note that this code is untested, as I wrote it directly in this post, but it is based heavily on code I wrote to sync Oracle into SQLite, which has been tested and works.

LinqToSQL not updating database

I created a database and dbml in visual studio 2010 using its wizards. Everything was working fine until i checked the tables data (also in visual studio server explorer) and none of my updates were there.
using (var context = new CenasDataContext())
{
context.Log = Console.Out;
context.Cenas.InsertOnSubmit(new Cena() { id = 1});
context.SubmitChanges();
}
This is the code i am using to update my database. At this point my database has one table with one field (PK) named ID.
**INSERT INTO [dbo].Cenas VALUES (#p0)
-- #p0: Input Int (Size = -1; Prec = 0; Scale = 0) [1]
-- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build:
4.0.30319.1**
This is LOG from the execution (printed the context log into the console).
The problem i'm having is that these updates are not persistent in the database. I mean that when i query my database (visual studio server explorer -> new query) i see the table is empty, every time.
I am using a SQL Server database file (.mdf).
EDIT (1): Immediate Window result
context.GetChangeSet()
{Inserts: 1, Deletes: 0, Updates: 0}
Deletes: Count = 0
Inserts: Count = 1
Updates: Count = 0
context.GetChangeSet().Inserts
Count = 1
[0]: {DBTest.Cena}
If you construct a DataContext without arguments, it will retrieve its connection string from your App.Config or Web.Config file. Open the one that applies, and verify that it points to the same database.
Put a breakpoint on context.SubmitChanges(); and in your immediate window in VS, do:
context.GetChangeSet();
There is an inserts property and it should have one record. That will help tell if its queuing up an insert.
HTH.

Resources