How to include an integer value in a SQL (Delete) statement in delphi - sql

In the database the DoctorID is a integer column. Neither of the edited out "//" lines of code work. I would really appreciate it if someone could show me how to correctly specify integer values in a SQL statement.
procedure TForm1.btnDeleteDocClick(Sender: TObject);
var
iID : Integer;
begin
iID := StrToInt (InputBox('Delete Doctor','Please enter in your Doctor ID',''));
with dmHospital do
begin
qryDoctors.SQL.Clear;
//qryDoctors.SQL.Add('DELETE FROM Doctors WHERE DoctorID = iID ' ) ;
//qryDoctors.SQL.Add('DELETE FROM Doctors WHERE DoctorID = ' + QuotedStr(iID));
qryDoctors.ExecSQL;
qryDoctors.SQL.Clear;
qryDoctors.SQL.Add('SELECT * FROM Doctors');
qryDoctors.Open;
end;
end;

The problem with
qryDoctors.SQL.Add('DELETE FROM Doctors WHERE DoctorID = iID ' )
is that it doesn't introduce the value of the variable iID into the DELETE statement, as you've obviously gathered.
Equally, the problem with
qryDoctors.SQL.Add('DELETE FROM Doctors WHERE DoctorID = ' + QuotedStr(iID))
is that it surrounds the value of iID with quotes, so that what the Sql engine which executes the DELETE statement actually sees is something like
DELETE FROM Doctors WHERE DoctorID = '99'
but DoctorID is an Integer column not a string one.
So, since your ID column is an integer column type, try this instead (but see below about the perils of Sql Injection):
qryDoctors.SQL.Add('DELETE FROM Doctors WHERE DoctorID = ' + iID);
Iow, you don't need quotes around integer values.
A parameterized version of your DELETE statement would be a better solution:
qryDoctors.SQL.Text := 'DELETE FROM Doctors WHERE DoctorID = :DoctorID';
qryDoctors.ParamByName('DoctorID').Value := StrToInt(iID);
One reason this is better is that it's immune to Sql Injection (see https://en.wikipedia.org/wiki/SQL_injection), whereas your way of doing it, by allowing the user to specify part of the SQL using the InputQuery and then concatenating it with the other DELETE text, is not. In fact, concatenating user input into a SQL statement is exactly the thing which allows the Sql Injection exploit - for instance a malicious user can tack another statement (or more) onto the end of the one you're building, e.g. DROP TABLE Employee (or worse). The opportunity for this user-subversion of the Sql statement never arises when the query is parameterised.
Fwiw, the reason I personally don't like using the Value property of a TParameter is that it's a variant, so subverts data-typing on the value specified.
Btw, iID isn't a very good name for a variable that's actually a string. The 'i' prefix usually leads readers to expect an integer.

Related

SELECT INTO STATEMENT INSIDE A SQL PROCEDURE THROWING AN ERROR

Hi I want to store a avalue of select statement into a variable and then update my table using that variable within the procedure but there is an error . I still dont know its only returning one column then also below error exists.
exact fetch returns more than requested number of rows.
Here is the example of demo code.Can anyone please give me an alternative of what else I can do here to make it work since I have many such plsql statement to populate table columns
create or replace procedure pcountry (country IN Varchar) is
var_date Date;
begin
select date into var_date from countrytable where country=country;
update newtable
set date=var_date
where country=country
commit;
end pcountry;
You need to change the name of your procedure'input argument since you have a column with the same name in your table. Oracle is interpretting your where clause
where country = country as where 1 = 1 which is always true. So it returns more rows instead of one row.
create or replace procedure pcountry (country_in IN Varchar) is
var_date Date;
begin
select date into var_date from countrytable where country = country_in ;
update newtable
set date=var_date
where country= country_in
commit;
end pcountry;
Skip select, switch to merge.
CREATE OR REPLACE PROCEDURE pcountry (par_country IN varchr2)
IS
BEGIN
MERGE INTO newtable n
USING countrytable c
ON (c.country = n.country)
WHEN MATCHED
THEN
UPDATE SET n.date_column = c.date_column
WHERE n.country = par_country;
END;
/
Don't name your PL/SQL variables/arguments with the same name as columns. The SQL engine will look for the country value in the local SQL scope in preference to the outer PL/SQL scope so country=country is (assuming non-NULL values) the same as 1=1 and you will match all rows.
Assuming each country is unique then:
create or replace procedure pcountry (
v_country IN COUNTRY_TABLE.COUNTRY%TYPE
) is
var_date COUNTRY_TABLE."DATE"%TYPE;
begin
select "DATE"
into var_date
from countrytable
where country=v_country;
update newtable
set "DATE"=var_date
where country=v_country
end pcountry;
/
Also, DATE is a reserved word and you cannot use is as an unquoted identifier.
You can combine it into a single SQL statement using MERGE:
CREATE PROCEDURE pcountry (
v_country IN COUNTRY_TABLE.COUNTRY%TYPE
) is
var_date COUNTRY_TABLE."DATE"%TYPE;
BEGIN
MERGE INTO newtable n
USING (SELECT *
FROM countrytable
WHERE country = v_country) c
ON (c.country = n.country)
WHEN MATCHED THEN
UPDATE
SET "DATE" = c."DATE";
END pcountry;
/
The issue, as has been explained in other answers, is that it makes no sense to expect country in a condition like country = country to mean different things on the two sides of the equal sign. The name country has more than one meaning - then a set of rules is applied to figure out which meaning is to be accepted each time the name is used. That is usually the narrowest context ("scope") in which the name exists; in this case, the name exists in the table referenced in the SQL statement, so that's what country means there.
One solution is simple - use a different name for the parameter used in the procedure. This has also been shown in the other answers.
There is another solution though. It might be preferred if your procedure was already very long, it used a parameter name like country, and now you would need to add some code where you need to use this name in a SQL statement. It would be pretty time-consuming to change the parameter name everywhere. Happily, PL/SQL understands qualified names. country (where you used it in the where clause) is the column name for the table referenced in the query. But if you write pcountry.country on the right-hand side, qualifying the variable name with the name of the procedure, no confusion would arise anymore.
... where country = pcountry.country
will achieve the same result as the other proposed answers in this thread. The right-hand side is the parameter or variable coming from the procedure, not the column name from the table.
Note that you could also qualify the left-hand side:
... where countrytable.country = pcountry.country
and perhaps this would be clearer to future readers.
However, this would not help:
... where countrytable.country = country
can you see why?

SQL syntax error in updating a database value

I'm update the money for one person only in a database. The money is saved as a currency and the email as a string. My SQL is throwing a syntax error
ADOQuery.sql.text:= ' UPDATE TblPlayerdetails SET Money = "' + Inttostr(NewAmount) + '" WHERE Email = "' + Playersemail + '"';
Newamount is an integer and email is a string.
I was hoping you would manage to work out what to do from the documentation I linked in comments, but on reflection I thought I had better provide a correct answer.
Set up the following code
procedure TForm1.Button1Click(Sender: TObject);
begin
AdoQuery2.SQL.Text := 'update moneytable set money = :money where id = :id';
AdoQuery2.Parameters.ParamByName('ID').Value := 1;
AdoQuery2.Parameters.ParamByName('Money').Value := 99;
AdoQuery2.ExecSQL;
end;
The line
AdoQuery2.SQL.Text := 'update moneytable set money = :money where id = :id';
sets up a parameterised UPDATE statement. The :id and :money are placeholders for parameter values which will be provided separately. The parameter names are ID and Money, though they could be given other names. Note that you could set up AdoQuery2's SQL.Text in the IDE at design time if you wanted to.
The next two lines
AdoQuery2.Parameters.ParamByName('ID').Value := 1;
AdoQuery2.Parameters.ParamByName('Money').Value := 99;
specify the values which the parameters are to be set to for when the UPDATE is actually executed. The ID value is the Row iD (aka primary key) of the row in the table which is to be updated. Before the UPDATE staement is actually executed, the AdoQuery parses the SQL and creates the parameters if they don't alteady exist (you can create them at design time in the IDE by editing the Parameters property of the AdoQuery.
Finally
AdoQuery2.ExecSQL;
is what actually executes the UPDATE statement. Note that you can repeat the steps of setting the parameter values and calling ExecSQL as many times as you want.
The main thing which was wrong with your UPDATE statement was that you were using double-quote (") marks, whereas when a SQL statement needs quote marks (and values of numeric columns do NOT) they should be single quotes('). A complication when constructing a SQL statement in Delphi code is that its syntax requires single quotes which are to be embedded in the SQL to be doubled up.
Note also that you should always used parameterised SQL for your SELECT, UPDATE, INSERT and DELETE statements as this helps protect your app against Sql injection. Making, say, an unparameterised statement accessible to the user can allow a malicious user to attempt to execute any SQL they wish.
In your question, you did not indicate what type of column is 'Money'. If it's varchar, char, then I understand why you might convert NewAmount to a string.
However, if the database expects numeric value (because the field is of type int, double, dec, or float), the syntax would be SET Money= '+ NewAmount +'.

Holding a select row into a variable and accessing its fields with informix

Is there a way in Informix to create a ROW type named or unnamed that you can put a row resulted from a select into? I'm working in a stored procedure.
What I want is something like this:
DEFINE ROW rowVar;
SELECT * INTO rowVar FROM myTableName;
Haven't been able to find the correct syntax so far. I want the row object to behave sort-of like it would be SAMEAS with the table columns.
I do this regularly with Informix.
define o_Row row(cs_nr int not null, addr_nr int, last_name varchar(255));
foreach
select cs into o_Row from cs where cs_nr = 1234
end foreach;
if you are returning more than one row or are not in a foreach you can use a multiset.
define o_Row multiset(row(cs_nr int not null, addr_nr int, last_name varchar(255))not null);
It is not possible to use an "undefined" ROWtype in Informix Stored Procedure Language (SPL).
If you try the following (using Informix 12.10.FC8DE):
CREATE PROCEDURE sp_dummy();
DEFINE generic_row ROW;
END PROCEDURE;
It returns the following error:
-999 Not implemented yet.
The Informix manual does not seem to be correct:
The following statements show examples of generic ROW variables and
named ROW variables:
DEFINE d ROW; -- generic ROW variable
If you define the fields of the ROW then you can use it inside the SPL.

delete using stored proc in mysql drops entire table

I have a stored proc which has only 1 line in it to delete a record in a table given a condition.
When I call the storedproc using 'CALL storedprocname('2');', it deletes all records from the table i.e. truncates. I have run the delete statement independently and it works fine, deleting only specified record. Am I missing something?
(I am using mysqlworkbench on macosx)
DROP PROCEDURE IF EXISTS usp_DeleteCompany;
DELIMITER $$
CREATE PROCEDURE usp_DeleteCompany (IN CompanyId VARCHAR(3))
BEGIN
DELETE FROM Company WHERE CompanyId = (CAST(CompanyId AS UNSIGNED));
-- DELETE FROM Company WHERE CompanyId = (CAST('2' AS UNSIGNED))
END; $$
Why not pass the parameter as unsigned directly like IN CompanyId UNSIGNED? no need of casting it then.
Also, consider changing the parameter name to something different than the actual column name CompanyId.
To me it seems like your delete query below
DELETE FROM Company WHERE CompanyId = (CAST(CompanyId AS UNSIGNED));
getting converted to below cause it's considering CompanyId as the exact column of the table instead of the actual parameter passed to the procedureand so end up deleting all rows.
DELETE FROM Company WHERE CompanyId = CompanyId;
Consider using a different name for the parameter like Cid in your query
CREATE PROCEDURE usp_DeleteCompany (IN Cid UNSIGNED)
BEGIN
DELETE FROM Company WHERE CompanyId = Cid;
END;

SQL stored proc - help me write this one, please! (part 2)

I have the following table with the value 501 in it..
CREATE TABLE _Numbers(
Number numeric(20,0) NOT NULL PRIMARY KEY
)
INSERT INTO _Numbers VALUES(501)
How can I write a stored proc on this which returns me 501 and increments Number to next in sequence (i.e. 502)? I would like this behaviour repeated every time the stored proc is called.
(Also, how can I call this stored proc from any query?)
Part of my previous question 3151056.
Thanks,
Voodoo
Use an IDENTITY column which takes care of numbering and incrementing for you.
Any returned number is liable to be already used by another connection/client/process
You're importing data from old tables, right?
What if you import data from old tables with identity off and after that you set the identity with the highest number+1 and continue your life using identity.
Other approach is using a trigger at insert that would check if NumberItem is null and it will add the Max+1 if it's null. If not, do nothing.
I don't think that SP is a good solution. And I'm pretty sure you don't need all that stuff.
CREATE OR REPLACE PROCEDURE read_and_increment (number_just_read OUT NUMBER)
IS
BEGIN
DECLARE
stored_number NUMBER DEFAULT NULL;
BEGIN
SELECT number
INTO stored_number
FROM _numbers
WHERE ROWNUM = 1;
number_just_read := stored_number;
UPDATE _numbers
SET number = number + 1;
COMMIT;
END;
END read_and_increment;