I'm trying using a TAdoTable component,
On form Create I call .Append() and in a button i call .Post()
but it loads the entire table! I don't need it to load anything, just need to insert a row into this table.
I was wondering if there is "good way" of inserting data into database with Ado, i already tried using the a "manual" approach with TAdoCommand but it doesn't seems right to me
I generate the INSERT query using Format() and all string fields are escaped with QuotedStr()
Thanks in advance!
Arthur.
You can use a TADODataset (or TADOQuery).
The way I do it sometimes is with by setting the CommandText to return 0 records from the table, i.e. SELECT TOP 0 * FROM [table], then use .Append and .Post
But personally, I prefer writing the SQL, such as with a TADOCommand
Use the TADOQuery object if you don't need to display the table's data.
Basically:
Use TADOQuery.SQL.Text to set the SQL command
Use TADOQuery.ExecSQL method to fire the SQL command
You can also use the TADOCommand component, and have it execute the specific SQL command. If you find yourself performing the same command over and over again (like inserts into a table) then consider using parameters rather than directly changing the SQL for every call. Parameters are easy to use, just place a :PARAMNAME in your sql, then use the parameters object on the ado component your using to set the value. For example:
Assuming the CommandText of the TAdoCommand component contains "INSERT INTO TABLENAME (FIELD1) VALUES (:FIELDVALUE1)"
AdoCommand1.Parameters.ParamByName('FIELDVALUE1').Value := 'TEST'
AdoCommand1.Execute;
When the above sql is executed, then the string "TEST" would be written to FIELD1.
var
CountVar: Integer;
begin
TADOConnection1.Execute(ASQLInsertStatement, CountVar, [adExecuteNoRecords]);
end;
Related
I am new at programming with delphi. I am currently creating a simple notebook program and i need some help. I have a form called contacts with 5 tEdit fields. I am thinking i could create a stored procedure in my sybase database to insert record into Contacts table, so I can call it with my delphi programm. How do I call this procedure in delphi? the values that will be inserted should be taken from users input into these tEdit fields. Anyone has any suggestions? Or am I thinking the wrong way? thanks in advance
You have several options here, and it will depend on what VCL controls you are using.
(1). You can insert via a tTable component. This let's you have a quick, easy, low level control. You drop the component on the form, set the component properties (tablename, etc), then something like
MyTable.Open;
MyTable.Insert; (or maybe append)
MyTable.FieldByName('MY_FIELD').AsString := 'Bob'; // set the field values
MyTable.post;
(2). Use SQL. Drop a SQL component on the form. Set the SQLText property, using parameters;
for example : "Insert into table (MyField) values :X". My opinion is that this is easier to do in complex situations, correlated subselects, etc.
MySQL.Close;
MySQL.ParamByName('X').AsString := 'BOB';
ExecSQL;
(3). Use stored procedures. - The advantage to this is that they are useable by multiple applications, and can be changed easily. If you want to update the SQL code, you update it once (in the database), versus having to change it in an app, and then distribute the app to multiple users.
The code for this will be nearly identify to (2), although I don't know the specifics of your VCL library. In effect though, you will specify the routine to run, specify the parameter values, and then execute the stored procedure.
Note that all these routines will return an error code or exception code. It is best practice to always check for that...
Here is a little more complex example, using a SQL statement called qLoader. qLoader exists on a datamodule. I am passing a parameter, executing the SQL statement, then iterating through all the results.
try
with dmXLate.qLoader do
begin
Close;
ParamByName('DBTYPE').AsString := DBType;
Open;
while not dmXLate.qLoader.Eof do
begin
// Here is where we process each result
UserName:= dmXLate.qLoader.FieldByName('USERNAME').AsString;
dmXLate.qLoader.Next;
end;
end;
except
on E: Exception do
begin
ShowMEssage(E.Message);
exit;
end;
end;
There is a function having a parameter. The function internally invokes a stored procedure with the parameter. And clients can pass a string to the function through HTTP requests.
I'm trying to add a method to remove any possibilities of injecting dangerous SQL statement through the parameter. The method name is IsSQLParameterSafe() and it returns boolean values depending on the parameter. If the value is safe to execute, then the method will return true, otherwise it returns false.
In my case, the parameter doesn't have to have blanks so if there are any whitespaces, then it'll return false. Also, I'm going to limit the length of the input up to 64 because its the maximum length of the parameter.
Do you think that my idea will work? If not, can you suggest ideas?
Thanks
You can use a parameterized query even with stored procedures. This is the best way to deal with SQL injection risks. It's difficult to be more specific without knowing what language you're using, but in Java, for example, you'd use something similar to this:
String callStmt = "CALL PROC(?)";
PreparedStatement prepStmt = con.prepareStatement(callStmt);
prepStmt.setString(1, parameter);
ResultSet rs = prepStmt.executeQuery();
You might also be interested in the OWASP SQL Injection Prevention Cheat Sheet, which goes into more detail.
You won't need to worry, unless you are stitching the SQL together manually and then executing it with the EXEC command.
For example, this is a simple Stored Procedure:
CREATE PROCEDURE DeleteRecord
(
#name VARCHAR(64)
)
AS
BEGIN
DELETE FROM Records WHERE [Name] = #name
END
If you attempt to pass this string into the procedure...
name OR 1=1
...then the Procedure will Delete 0 records, because no one has this exact name.
Why doesn't it delete everything?
The Stored Procedure doesn't stitch the SQL together into a big string (you often see this sort of thing in tutorials for beginners in PHP). Instead, it passes the original SQL statement, then each parameter as a distinct argument. I don't know the technical details of how this works, but I know from experience that adding slashes and quotes and garbled characters will not break this query.
But...
If you are writing Dynamic SQL, and if you parameter represents a Table or Column name, then you need to be more careful. I would use a white list for that.
http://www.sommarskog.se/dynamic_sql.html
I need to insert values from a table into a sproc. For example:
exec mysproc #param1='col1', #param2='col2'
This can be done using a cursor but is there some way to do it via a set operation?
It is not possible to invoke an sproc as part of a "set operation". Probably, the reason for that is that the sproc might have arbitrary side-effects like modifying data, sending additional result sets (!) or shutting down the server.
A cursor is the canonical approach to this. (Alas.)
You could modify the sproc to take a TVP, of course. Not sure if that is workable for you.
I imagine that the method you choose would be based on the amount of time you have available and it's difficult to say which of these methods is most time consuming without being more intimate with the logic.
There are a few approaches to this problem.
As Robert Harvey has alluded to, you should maybe look at maybe
modifying the proc to accept a table valued parameter (if you are
using SQL Server 2008 upwards). If not, you could create a scalar
XML parameter that is "decoded" in to a table inside the proc.
Populate a #table with your "parameter data" and a ROW_NUMBER() and
use a WHILE loop to call the proc for each row in your #table.
Create a CURSOR (I hate giving CURSOR advice) of type FAST_FORWARD
and iteratively call the procedure.
Dynamic SQL; build up a SQL command string using EXEC or preferably
SP_EXECUTESQL.
My opinion is that first prize would be to re-engineer the proc to
accept parameter filters. Going on the assumption that the dataset
you wish to create parameters from is the result of a filtered
query:
SELECT Moo, Meow
FROM Woof
WHERE Fu = #ParmX
AND Bar = #ParmY
Your proc should be called with #ParmX, #ParmY and the logic inside would then proceed in a set based manner.
Each time I perform a query (INSERT, DELETE,UPDATE). After Do I need to do Select * From Table, so my info can be seen on the Grid control?
For example:
UniQuery1 is my dataset.
I'm using a TDBADvListView control.
UniQuery1.Close;
UniQuery1.SQL.Clear;
SQL_QUERY:= 'insert into ListaCamiones(Tablilla,Marca,Modelo,Color) Values ('
+QuotedStr(a1)+','+
QuotedStr(a2)+','+
QuotedStr(a3)+','+
QuotedStr(a4)+')';
UniQuery1.SQL.Text := SQL_QUERY;
UniQuery1.Execute;
Do I need to do, Select * From ListaCamiones;
So I can see the information back on my TDBADvListView?
The answer is both yes and no!
Yes in that you do have to perform a SELECT query again in order to aggregate the modified recordset, no in that you don't have to perform the query as a separate execution.
If you append a semicolon at the end of your INSERT/UPDATE/DELETE query string, and immediately follow that by the desired SELECT query, your call to Execute will simultainiously update the records and aggregate the updated recordset for display.
Additionally, I would change the way you're building your SQL string too!
const
INSERT_QUERY_STRING = 'INSERT INTO ListaCaminoes(Tablilla, Marca, Modelo, Color) VALUES ("%s", "%s", "%s", "%s"); SELECT * FROM ListaCaminoes';
// Now inside your method
UniQuery1.SQL.Text := Format(INSERT_QUERY_STRING, [a1, a2, a3, a4]);
UniQuery1.Execute;
Hope it helps!
In general, yes, because in my experience when you make database changes via SQL statements:
no database component automatically refreshes the query,
no database can refresh the data in your application when the data
has changed in the database.
I recommend that you use a separate query component (UniQuery2) to execute your SQL statement. The you can use the ReQuery method of your Query to re-execute your original query (UniQuery1). Depending on the database components you are using, your local cursor may be reset.
Alternately you can Append/Insert to add records and Edit to change records of UniQuery1. This avoids the need to re-execute your original query because the changes are added to the dataset records buffered locally by the Query component. But, re-executing the query is necessary to get records that were added/edited by other users since your query was last executed.
If you just inserted the Information to the Database you have got it already!
In some SQL-Variants (in mySQL I am shure) you can have the command "insert_id()" from the API, that returns the AUTO_INCREMENT - value of the last inserted Dataset.
If you just want to get this ID, it is the way to go (on mySQL, like I said), but if you want to have other data you have to Query it again. In a combined query (like posted before) or in two seperate queries.
Glad to help!
I have to execute a loop in database. This is only a one time requirement.
After executing the function, I am dropping the function now.
Is there any good approach for creating temporary / disposable functions?
I needed to know how to do a many time use in a script I was writing. Turns out you can create a temporary function using the pg_temp schema. This is a schema that is created on demand for your connection and is where temporary tables are stored. When your connection is closed or expires this schema is dropped. Turns out if you create a function on this schema, the schema will be created automatically. Therefore,
create function pg_temp.testfunc() returns text as
$$ select 'hello'::text $$ language sql;
will be a function that will stick around as long as your connection sticks around. No need to call a drop command.
A couple of additional notes to the smart trick in #crowmagnumb's answer:
The function must be schema-qualified at all times, even if pg_temp is in the search_path (like it is by default), according to Tom Lane to prevent Trojan horses:
CREATE FUNCTION pg_temp.f_inc(int)
RETURNS int AS 'SELECT $1 + 1' LANGUAGE sql IMMUTABLE;
SELECT pg_temp.f_inc(42);
f_inc
-----
43
A function created in the temporary schema is only visible inside the same session (just like temp tables). It's invisible to all other sessions (even for the same role). You could access the function as a different role in the same session after SET ROLE.
You could even create a functional index based on this "temp" function:
CREATE INDEX foo_idx ON tbl (pg_temp.f_inc(id));
Thereby creating a plain index using a temporary function on a non-temp table. Such an index would be visible to all sessions but still only valid for the creating session. The query planner will not use a functional index, where the expression is not repeated in the query. Still a bit of a dirty trick. It will be dropped automatically when the session is closed - as a depending object. Feels like this should not be allowed at all ...
If you just need to execute a function repeatedly and all you need is SQL, consider a prepared statement instead. It acts much like a temporary SQL function that dies at the end of the session. Not the same thing, though, and can only be used by itself with EXECUTE, not nested inside another query. Example:
PREPARE upd_tbl AS
UPDATE tbl t SET set_name = $2 WHERE tbl_id = $1;
Call:
EXECUTE upd_tbl(123, 'foo_name');
Details:
Split given string and prepare case statement
If you are using version 9.0, you can do this with the new DO statement:
http://www.postgresql.org/docs/current/static/sql-do.html
With previous versions, you'll need to create the function, call it, and drop it again.
For ad hock procedures, cursors aren't too bad. They are too inefficient for productino use however.
They will let you easily loop on sql results in the db.