if (conpassedt.text = '') or (regpassedt.text = '') or (regaccedt.text = '') or (regpassedt.text <> conpassedt.text) then
begin
showmessage('Please fill in all fields for input and confirm passwords match');
end else
begin
DM.qryDB.sql.clear;
DM.qrydb.sql.add('INSERT INTO tblUsers(AccountName, Password) ');
DM.qrydb.sql.add('VALUES ("'+ regaccedt.text +'", "' + THashMD5.GetHashString(regpassedt.text) + '")');
DM.qrydb.ExecSQL;
end;
Most likely, your DB requires single quotes instead of double quotes on string literals, eg:
DM.qrydb.sql.add('INSERT INTO tblUsers(AccountName, Password) ');
DM.qrydb.sql.add('VALUES (''' + regaccedt.text + ''', ''' + THashMD5.GetHashString(regpassedt.text) + ''')');
In which case, you should use QuotedStr() instead to handle quoting and escaping for you (which will better help you avoid SQL Injection attacks properly), eg:
DM.qrydb.SQL.Add('INSERT INTO tblUsers (AccountName, Password) ');
DM.qrydb.SQL.Add('VALUES (' + QuotedStr(regaccedt.text) + ', ' + QuotedStr(THashMD5.GetHashString(regpassedt.text)) + ')');
Though, you really should use a parameterized query instead, and let the DB engine work out any necessary quoting and escaping that it needs, eg:
DM.qrydb.SQL.Add('INSERT INTO tblUsers (AccountName, Password) ');
DM.qrydb.SQL.Add('VALUES (:PAccountName, :PPassword)');
DM.qrydb.ParamByName('PAccountName').AsString := regaccedt.text;
DM.qrydb.ParamByName('PPassword').AsString := THashMD5.GetHashString(regpassedt.text);
Modern versions of Delphi with Firedac have overloaded versions of various SQL methods to allow removal of a lot of the boilerplate in your question. Also as already mentioned in comments use parameters vs constructing a string.
FDQuery1.ExecSQL('INSERT INTO tblUsers(AccountName, Password) VALUES (:AccountName, :Password);',
[regaccedt.text,THashMD5.GetHashString(regpassedt.text)],
[ftWideString,ftWideString]);
I have a table in which there is a column name associated_ids with datatype as varchar2(4000). It contains a value and i need to append the new value with existing value. Using below update query:
update ncl_getafix.service_graph
set Associated_ids = Associated_ids + ',' + '95d4980b-d12c-4854-97c6-bd9854f8f003'
where SERVICE_ID='075d7a58-7fad-4e1d-9822-83a2fa1a0d05';
I am getting below error:
Error starting at line : 5 in command - update
ncl_getafix.service_graph set
Associated_ids=Associated_ids+','+'95d4980b-d12c-4854-97c6-bd9854f8f003'
where SERVICE_ID='075d7a58-7fad-4e1d-9822-83a2fa1a0d05'
Error report - ORA-01722: invalid number.
Can anyone help to debug this.
You need to use concat operator || not + as follows:
update ncl_getafix.service_graph
set Associated_ids = Associated_ids || ',' || '95d4980b-d12c-4854-97c6-bd9854f8f003'
where SERVICE_ID='075d7a58-7fad-4e1d-9822-83a2fa1a0d05';
i found the answer.
Use || symbol instead of + in query
Is it possible to update a variable with a concatenation of variables(columns of VARCHARS2)?
UPDATE ARTICLE
SET DESCRIPTION = (CPV_DESCRIPTION + '/' LEVEL1_DESCRIPTION + LEVEL2_DESCRIPTION+LEVEL3_DESCRIPTION)
WHERE ID_ARTICULO = 209;
UPDATE ARTICLE
SET DESCRIPTION = concat(CPV_DESCRIPTION,'/',LEVEL1_DESCRIPTION,' ',LEVEL2_DESCRIPTION' 'LEVEL3_DESCRIPTION)
WHERE ID_ARTICULO = 209;
Both cases it gives me an error.
As mentioned by #a_horse... concat() function only takes 2 parameters. When you specify more that 2 parameters to be concated, you need to use || operator. Also + is a logical operator in Oracle unlike its used in Java for concatenation. Try this:
UPDATE ARTICLE
SET DESCRIPTION = CPV_DESCRIPTION
|| '/'
||LEVEL1_DESCRIPTION
||' '
||LEVEL2_DESCRIPTION
||' '
||LEVEL3_DESCRIPTION
WHERE ID_ARTICULO = 209;
I am currently coding a Delphi 7 program that utilises SQL and an Access 2003 database.
The unit receives a 5 digit code from a previous unit, via a public variable (this is frmLogin.sCode). On form activation, the program will execute an SQL query to display the record from tblStudents that matches sCode. This statement uses a ParamByName line and works perfectly.
If no match is found, a message is displayed and the user is left with no option, but to click on the add user button. The user is then prompted to enter all of his details into the program, which are then passed to a class which sets out the SQL Insert Statement. A problem occurs now, however, as a message is displayed stating that Parameter Username is not found. I cannot understand why, as it is found when the Select statement is run. Please could someone help with this?
procedure TfrmProfilePage.FormActivate(Sender: TObject);
begin
//Instantiates the object.
objProfilePage := TProfilePage.Create;
sSQL := objProfilePage.SelectSQL;
ExecuteSQL(sSQl);
end;
procedure TfrmProfilePage.ExecuteSQL(sSQL : String);
begin
With dmTextbookSales do
Begin
dbgrdDisplay.DataSource := dsProfilePage;
qryProfilePage.SQL.Clear;
qryProfilePage.SQL.Add(sSQL);
qryProfilePage.Parameters.ParamByName('Username').Value := frmLogin.sCode;
qryProfilePage.Open;
If qryProfilePage.RecordCount = 0
Then
Begin
ShowMessage('Please click on the "Add Details" button to get started.');
btnChange.Enabled := False;
btnSelling.Enabled := False;
btnBuying.Enabled := False;
End;
End;
end;
procedure TfrmProfilePage.GetValues(VAR sStudentName, sStudentSurname, sCellNumber, sEmailAddress : String; VAR iCurrentGrade : Integer);
begin
ShowMessage('Fields may be left blank, but users wishing to sell textbooks should enter at least one contact field.');
sStudentName := InputBox('Name','Please enter your first name:','');
sStudentSurname := InputBox('Surame','Please enter your surname:','');
iCurrentGrade := StrToInt(InputBox('Current Grade','Please enter your current grade:',''));
sCellNumber := InputBox('Cellphone Number','Please enter your cellphone number:','');
sEmailAddress := InputBox('Email Address','Please enter your email address:','#dainferncollege.co.za');
end;
procedure TfrmProfilePage.btnAddClick(Sender: TObject);
begin
GetValues(sStudentName, sStudentSurname, sCellNumber, sEmailAddress, iCurrentGrade);
sSQL := objProfilePage.InsertSQL;
ExecuteSQL(sSQL);
btnChange.Enabled := True;
btnSelling.Enabled := True;
btnBuying.Enabled := True;
end;
The following code is obtained from the linked class, clsProfilePage:
function TProfilePage.InsertSQL: String;
begin
Result := 'INSERT INTO tblStudents (' + '[StudentID]' + ',' + '[StudentName]' + ',' + '[StudentSurname]' + ',' + '[CurrentGrade]' + ',' + '[CellNumber]' + ',' + '[EmailAddress]' + ') VALUES (' + 'Username' + ',' + QuotedStr(fStudentName) + ',' + QuotedStr(fStudentSurname) + ',' + IntToStr(fCurrentGrade) + ',' + QuotedStr(fCellNumber) + ',' + QuotedStr(fEmailAddress) + ')';
end;
function TProfilePage.SelectSQL: String;
begin
Result := 'SELECT * FROM tblStudents Where StudentID = Username';
end;
Your INSERT statement is wrong. You need to add parameters before you can set parameter values. In Delphi, you do that using : before the parameter name in your SQL statement.
In addition, Open is only used when performing a SELECT. INSERT, UPDATE, and DELETE don't return a rowset, and therefore you must use ExecSQL (the dataset method, not your function with the conflicting name) instead.
(While we're at it, never use RecordCount on a SQL dataset - it requires all rows to be retrieved in order to obtain a count, and it's not relevant when performing anything other than a SELECT anyway. Operations performed by ExecSQL should use RowsAffected instead, which tells you the number of rows that were affected by the operation.
(If you need a count of the number of rows, perform a SELECT COUNT(*) AS NumRecs FROM YourTable WHERE <some condition> instead, and access the NumRecs field using FieldByName.)
Change your function that returns the INSERT statement to something like this (the #13 in Result is a carriage return, which prevents having to manually insert spaces at the end of each line to separate SQL words):
function TProfilePage.InsertSQL: String;
begin
Result := 'INSERT INTO tblStudents ([StudentID],'#13 +
'[StudentName], [StudentSurname],'#13 +
'[CurrentGrade], [CellNumber], [EmailAddress])'#13 +
'VALUES (:Username, :StudentName,'#13 +
':StudentSurname, :CurrentGrade,'#13 +
':CellNumber, :EMailAddress)';
end;
You can then use it with ParamByName, without jumping through all of the hoops with QuotedStr and concatenation:
procedure TfrmProfilePage.AddUser;
begin
with dmTextbookSales do
begin
qryProfilePage.SQL.Text := InsertSQL;
qryProfilePage.Parameters.ParamByName('Username').Value := frmLogin.sCode;
qryProfilePage.Parameters.ParamByName('StudentName').Value := frmLogin.sUserName;
// Repeat for other parameters and values - you have to set every
// single parameter. To skip, set them to a null variant.
// Execute the INSERT statement
qryProfilePage.ExecSQL;
// See if row was inserted, and do whatever.
If qryProfilePage.RowsAffected > 0 then
ShowMessage('User added successfully');
// Perform a SELECT to populate the grid contents with the new
// rows after the update
end;
end;
I'd highly suggest you rethink this code. It's extremely convoluted, and it requires too much to accomplish a simple task (adding a new user).
If it were me, I'd use a separate query that was dedicated only to performing the INSERT operation, where you can set the SQL at design-time, and set the proper types for the parameters in the Object Inspector. Then at runtime, you simply set the parameter values and call ExecSQL on that insert query, and refresh your SELECT query to reflect the new row. It avoids all of the noise and clutter (as well as some unnecessary function calls and convoluted SQL building, opening and closing your SELECT query, etc.).
(It would also allow you to remove that horrific with statement, which leads to hard-to-find bugs and difficult to maintain code.)
You also have some bad linkages between forms, where you're referencing frmLogin (a specific form instance) from a second form. This means that you can never have more than one instance of either form in use at the same time, because you've hard-coded in that reference. I'd rethink that to use either parameters passed in when you create the form, or as properties that are set by the login form when creating the profile page form (or whatever TProfilePage is - your post doesn't say).
The best solution would be to move all of the non-UI related code into a separate unit (such as a TDataModule, which is designed to be used to work with non-visual components such as ADO queries) and remove it from the user-interface related forms anyway, which
Eliminates the coupling between forms, allowing the code to be reused.
Removes the non-visual data related components from cluttering the form and form code.
Separates the business logic (the part not related to interacting with the user) in a separate, distinct location, making it (and the code that uses it) easier to maintain.
Your insert statement is wrong. You need to replace the value for [StudentID].
'INSERT INTO tblStudents ('
+ '[StudentID]' + ','
+ '[StudentName]' + ','
+ '[StudentSurname]' + ','
+ '[CurrentGrade]' + ','
+ '[CellNumber]' + ','
+ '[EmailAddress]' + ')
VALUES ('
+ 'Username' + ',' // <-- UserName will never be replaced by a QuotedStr
// Access is looking for a column with the name
// 'UserName' which can not be found
+ QuotedStr(fStudentName) + ','
+ QuotedStr(fStudentSurname) + ','
+ IntToStr(fCurrentGrade) + ','
+ QuotedStr(fCellNumber) + ','
+ QuotedStr(fEmailAddress) + ')';
I have the following code which is constructs an SQL statement based on a number of variables:
SQL.Add('SELECT CDbl(Answer) as An FROM v_Outcomes_FirstLastOneYear ' +
'WHERE ' + GetTool(ReportGrid.Cells[col, 0]) + ' ' +
'AND (Collector = ' + QuotedStr('Patient') + ') ' +
'AND (Question=' + GetQuestion(ReportGrid.Cells[col, 0]) + ') ' +
'AND (Answer is not null) ' +
'AND (Answer <> ''null '') ');
if Copy(ReportGrid.Cells[col, 0], Length(ReportGrid.Cells[col, 0]), 1) = '1' then
SQL.Add('AND ('+GetTPoint(first)+') ')
else
SQL.Add('AND ('+GetTPoint(second)+') ');
SQL.Add('ORDER BY CDbl(Answer)');
The top row of ReportGrid (a TStringGrid) has already been populated with a series of strings such as 'Peds phys 1', or 'Peds phys 2'. The last number is either 1 or 2.
The variables 'first' and 'second' a strings defined further up.
col is an integer - this statement is constructed as part of a for loop which goes through each of the columns and populates each row with data corresponding to the header.
GetTool(), GetQuestion() and GetTPoint() are functions I have defined. The code for GetTPoint() is as follows:
function GetTPoint(timepoint: string): string;
begin
if (timepoint = '0') or (timepoint = 'discharge') then
begin
if timepoint = '0' then
Result := 'FirstAxData=''TRUE'' and DischargeData=''FALSE''';
if timepoint = 'discharge' then
Result := 'DischargeData=''TRUE'' and FirstAxData=''FALSE''';
end
else
begin
timepoint := FormatFloat('0', StrToInt(timepoint)*30.4368);
Result := '[Date] BETWEEN ([Date of First CPC]+' + timepoint +
')-61 AND ([Date of First CPC]+'+timepoint+')+61';
end;
end;
Each of the custom functions are similar and simply returns a string. GetTool() and GetQuestion() both work fine but GetTPoint() throws the following compiler error:
E2010 Incompatible types: 'string' and 'procedure, untyped pointer or
untyped parameter'
Does anyone know why this is being thrown?
The code you've shown appears inside a with statement for the query object you're operating on. Something like this:
with SomeQuery do begin
SQL.Add(...);
...
end;
You're able to refer to the SQL property alone like that because the with statement brings the query object's members into the current scope. That means all its members, including the First method (used for selecting the first query result). Anything introduced in the scope of the with statement will hide things of the same name introduced in earlier scopes. That includes the first variable you say was declared elsewhere.
When you have the expression GetTPoint(first), the compiler interprets the name first as referring to the query method, not the variable. The method is not a string, and calling it doesn't return a string, so the compiler rightfully complains.
The best solution is to stop using with. It just introduces brittleness to code. If you simply must continue using with, then rename your first variable so it doesn't interfere with names brought into scope later. There is no way to refer to local variables that have been hidden by another scope. (If first were global, you could qualify it with the name of the unit, but global variables should be avoided almost as much as with statements.)