SQL query with variables - delphi

I am doing a PAT for school and I am doing the following how can I correct it. I want to send an entered email address, name, Id
number, birth date, gender, town and all is string my statement is:
Adoquery1.sql.text := 'insert into besprekings
values('email', 'name', 'Id', 'birth', 'gender', 'town')';
The fields are as follows:
Email(string), Name(string), ID(string), Birth(string), Gender(string), town(string)
This is not really homework it is a project that counts 25% of my years mark. I have finished everything but can't get this right. We have to bring in something new that we haven't learned in school and for me that is opening programs like mail(windows 8) and doing this I really apreciate everybody trying to help.

You need to use parameterized queries, to prevent SQL injection. Even though that might not be something to worry about in your app now, it's best to get in the habit of doing it right in the first place. I'll show a little of the code, and you can figure out how to finish it yourself.
First, properly populate your SQL. Specify the names of the columns you're inserting into, and the parameter names you'll be using to populate them (the parts starting with :):
ADOQuery1.SQL.Clear;
ADOQuery1.SQL.Add('INSERT INTO beskprekings (email, name, Id)');
ADOQuery1.SQL.Add('VALUES (:email, :name, :Id)');
Now put the actual values to insert into the parameters, using the same names you used in your VALUES list:
ADOQuery1.Parameters.ParamByName('email').Value := email;
ADOQuery1.Parameters.ParamByName('name').Value := name;
ADOQuery1.Parameters.ParamByName('id').Value := Id;
Now, execute the query.
The added benefit of doing it with parameterized queries is that, once it's been run once, you can simply repopulate the parameters and run it again; the database will already have done what it needs to to prepare the query (hint: the word I marked has meaning for ADO and other databases - you should look into it) so that it's much faster when you use it again and again.

Related

How to implement a TDataSet-bound combobox with not-in-list values

I need to implement a combobox, which is bound to a TpFIBDataSet (descendant of TDataSet). I've done this several times before. It's not a big thing if it contains only predefined values.
This time, I'd like to have a combobox that accepts custom values entered by the user, also giving the ability to the user to select some predefined value. Newly entered values shall be inserted into some table of the database just before the record the combobox's field belongs to is posted.
The main problem seems to me, that predefined values are internally represented as integer IDs (the combobox I use is TwwDBComboBox from Roy Woll's InfoPower package, as it implements maplist functionality) because the field is a foreign key, while custom values may be nearly everything (only restricted by a mask).
How can I distinguish between an integer ID and integer user-input, for example?
See the set properties of the combobox:
AComboBox.Style := csDropDown;
AComboBox.MapList := True;
I don't request a solution as take this piece of code and be happy. I'm rather looking for some advice by others who might have or had a similar problem.
How can I distinguish between an integer ID and integer user-input, for example?
You go back to the database. Either query directly select count(*) from table where id = ComboBoxId.
Or use the Locate method of the dataset.
Or keep a cache handy in a MyList: TList<Integer> and do a MyList.BinarySearch to see if the item is already in the DB.
Obviously the cache will only work if the DB is single-user, because otherwise you will not be able to keep it up-to-date.
If it is not in the DB, you run the insert query.
After it's inserted you run the default combobox behavior, because now the values is sure to be in the DB.

IN Clause used in the case of IN parameters is not Working while DELETING

I want to delete my records from the database based on some ID's.
This is the statement written in my STORED PROCEDURE to delete records based on DeletedID.
DELETE FROM tms_activity where activity_id IN (DeletedID);
My DeletedID is a string with records comma seperated like("1,2,3")
Now when I am passing DeletedID in my Statement as a parameter it is taking the input as "1,2,3" and deleting the record with the first DeletedID it is getting(1 in this case).But I want to delete all the records based on the given parameter.
DeletedId must be passed like (1,2,3) rather than ("1,2,3") than only it can delete all the records based on provided ID's...Now how can I do that?
I consulted this question: MySQL wrong output with IN clause and parameter,
but couldn't understand how can I achieve my result.
Did you try this
DELETE FROM tms_activity where activity_id in
( SELECT ACTIVITY_ID FROM SOMETABLE WHERE FIELD = CRITERIA )
Or some more findings, if I were at this problem would select one of these solutions.
I investigated and found some very good links for you.
[http://johnhforrest.com/2010/10/parameterized-sql-queries-in-c/][1]
The Parametrized SQL queries have a benefit if you have to send a large number of parameters and want to run against one sql. It takes the sql in one chunk and all the parameters in other so keep the network traffic low.
You can search more in this topic
Thanks
QF

Triggering a stored procedure to run instead of INSERT (PL/SQL)

Given a simple employees table (id, lastname, firstname) the assignment requres me to write a stor proc that takes first and last names, figures out the next id and inserts a new record into the table. That's done. Next part's asking to write a trigger that will call this stor proc whenever a new INSERT happens. My understanding is that this trigger is supposed to intercept the insert statement that's triggered it, extract its arguments and run the stor proc INSTEAD (not before or after) of the insert. The problem is that instead-of triggers seem to only work with views which I'm not allowed to write. Any ideas on how this might be approached?
Thank you for your input!
In Oracle there are sequences. So you could create a sequence and then assign the next sequence number each time in the insert before trigger. Examples can be found here
http://www.adp-gmbh.ch/ora/concepts/sequences.html
and here
http://www.adp-gmbh.ch/ora/sql/trigger/before_after_each_row.html
Let me know if you can do the assignment now or if you need more help.

Is it faster to constantly assign a value or compare

I am scanning an SQLite database looking for all matches and using
OneFound:=False;
if tbl1.FieldByName('Name').AsString = 'jones' then
begin
OneFound:=True;
tbl1.Next;
end;
if OneFound then // Do something
or should I be using
if not(OneFound) then OneFound:=True;
Is it faster to just assign "True" to OneFound no matter how many times it is assigned or should I do the comparison and only change OneFuond the first time?
I know a better way would be to use FTS3, but for now I have to scan the database and the question is more on the approach to setting OneFound as many times as a match is encountered or using the compare-approach and setting it just once.
Thanks
Your question is, which is faster:
if not(OneFound) then OneFound:=True;
or
OneFound := True;
The answer is probably that the second is faster. Conditional statements involve branches which risks branch mis-prediction.
However, that line of code is trivial compared to what is around it. Running across a database one row at a time is going to be outrageously expensive. I bet that you will not be able to measure the difference between the two options because the handling of that little Boolean is simply swamped by the rest of the code. In which case choose the more readable and simpler version.
But if you care about the performance of this code you should be asking the database to do the work, as you yourself state. Write a query to perform the work.
It would be better to change your SQL statement so that the work is done in the database. If you want to know whether there is a tuple which contains the value 'jones' in the field 'name', then a quicker query would be
with tquery.create (nil) do
begin
sql.add ('select name from tbl1 where name = :p1 limit 1');
sql.params[0].asstring:= 'jones';
open;
onefound:= not isempty;
close;
free
end;
Your syntax may vary regarding the 'limit' clause but the idea is to return only one tuple from the database which matches the 'where' statement - it doesn't matter which one.
I used a parameter to avoid problems delimiting the value.
1. Search one field
If you want to search one particular field content, using an INDEX and a SELECT will be the fastest.
SELECT * FROM MYTABLE WHERE NAME='Jones';
Do not forget to create an INDEX on the column, first!
2. Fast reading
But if you want to search within a field, or within several fields, you may have to read and check the whole content. In this case, what will be slow will be calling FieldByName() for each data row: you should better use a local TField variable.
Or forget about TDataSet, and switch to direct access to SQLite3. In fact, using DB.pas and TDataSet requires a lot of data marshalling, so is slower than a direct access.
See e.g. DiSQLite3 or our DB classes, which are very fast, but a bit of higher level. Or you can use our ORM on top of those classes. Our classes are able to read more than 500,000 rows per second from a SQLite3 database, including JSON marshalling into objects fields.
3. FTS3/FTS4
But, as you guessed, the fastest would be indeed to use the FTS3/FTS4 feature of SQlite3.
You can think of FTS4/FTS4 as a "meta-index" or a "full-text index" on supplied blob of text. Just like google is able to find a word in millions of web pages: it does not use a regular database, but full-text indexing.
In short, you create a virtual FTS3/FTS4 table in your database, then you insert in this table the whole text of your main records in the FTS TEXT field, forcing the ID field to be the one of the original data row.
Then, you will query for some words on your FTS3/FTS4 table, which will give you the matching IDs, much faster than a regular scan.
Note that our ORM has dedicated TSQLRecordFTS3 / TSQLRecordFTS4 kind of classes for direct FTS process.

Calling a stored proc within a stored proc

I am attemting to create a storedproc that reads like:
Select
ep.EmployeeID, GetEmployeeFirstName(ep.EmployeeID),
GetEmployeeLastName(ep.EmployeeID), ed.EmployeeDateOfBirth,
ed.EmployeeAddress, ed.EmployeeAddress2, ed.City, ed.State, ed.ZipCode
From
EmployeeProfile ep, EmployeeDetail ed
Where
ep.EmployeeID = ed.EmployeeID
This block of code will be a stored procedure.
My issue is that GetEmployeeFirstName is a stored proc that has to be passed an EmployeeID to get the employees first and last name.
How can I call a storedproc within a stored proc.
Thanks
Mike
These would probably be better suited as a function.
GetEmployeeFirstName(ep.EmployeeID), GetEmployeeLastName(ep.EmployeeID)
Better yet, just join the table that has the names.
I don't understand what these stored procedures do. Even if the first and last name are not in the EmployeeProfile table, and even if you have to do some manipulation of the strings before they are returned, a join would be a much better solution than a stored procedure or function. Especially when you take performance into account.
If you have the GetEmployeexName sprocs because you use them elsewhere, that's fine. Whatever they do, I would not consider it code duplication if they don't get called from your query.
You need to understand that for every row in your result set, two other procedures or functions get called. This is extremly costly and can render an application unacceptably slow, even for relatively small result sets of a few thousand employees. I know what I am talking about - I removed a lot of function calls from queries during a recent database tuning initiative.
In SQL Server, in order to call the GetEmployeeLastName within the Select statement list I would convert it to a database Function.
You can use EXEC or sp_executesql to execute a stored procedure from another stored procedure. (BTW, you have not specified your RDBMS).
Doesn't your table EmployeeDetail contain the employee's first and last name?
Select
ep.EmployeeID, ed.FirstName
ed.LastName, ed.EmployeeDateOfBirth,
ed.EmployeeAddress, ed.EmployeeAddress2,
ed.City, ed.State, ed.ZipCode
From
EmployeeProfile ep
inner join EmployeeDetail ed ON ep.EmployeeID = ed.EmployeeID

Resources