Null field not inserting NULL from empty TEdit - mysql

I add new order details to a table in my db.
One of the columns there is named email, in the form I created to add the new order in my delphi application and subsequently the db, I use TEdits to pass the data along.
This is the code:
procedure TForm2.actAddComandaExecute(Sender: TObject);
begin
if dbmodule.comenziConnection.Connected then
begin
if addOrderForm.ShowModal=mrok then
begin
dbmodule.comenziQuery.SQL.Clear;
dbmodule.comenziQuery.SQL.Add( 'insert into `r33758pi_tipotask`.`comenzi` ( stare, client, telefon, email, detalii, livrare, pret, user, status, observatii ) values ( :stare, :client, :telefon, :email, :detalii, :livrare, :pret, :user, :status, :observatii ) ' ) ;
dbmodule.comenziQuery.Params.ParamByName( 'stare' ).AsString := addOrderForm.ComboBoxEx1.Text ;
dbmodule.comenziQuery.Params.ParamByName( 'client' ).AsString := addOrderForm.Edit2.Text ;
dbmodule.comenziQuery.Params.ParamByName( 'telefon' ).AsString := addOrderForm.Edit3.Text ;
dbmodule.comenziQuery.Params.ParamByName( 'email' ).AsString := addOrderForm.Edit4.Text ;
dbmodule.comenziQuery.Params.ParamByName( 'detalii' ).AsString := addOrderForm.Edit5.Text ;
dbmodule.comenziQuery.Params.ParamByName( 'livrare' ).AsString := addOrderForm.ComboBoxEx6.Text ;
dbmodule.comenziQuery.Params.ParamByName( 'pret' ).AsString := addOrderForm.Edit7.Text ;
dbmodule.comenziQuery.Params.ParamByName( 'user' ).AsString := addOrderForm.ComboBoxEx8.Text ;
dbmodule.comenziQuery.Params.ParamByName( 'status' ).AsString := addOrderForm.ComboBoxEx9.Text ;
dbmodule.comenziQuery.Params.ParamByName( 'observatii' ).AsString := addOrderForm.Edit10.Text ;
dbmodule.comenziQuery.ExecSQL ;
end;
end;
end;
If I leave Edit4 empty on the form everything inserts but the field email for that order doesn't have a Null value, it shows just as being empty - not null but no data either.
The column email is set as Null by default in db so that's not the problem.
Screenie from workbench:
There should be 2 NULL showing up between the values there but it's just empty.
Any ideas why?
Using Rad Studio 10 Seattle and dbExpress components
EDIT
comenziQuery is a TSQLQuery
dbmodule is the name of a data module that holds the db components
the dataset is a TSimpleDataSet
the comenziConnection is the name of a TSQLConnection
For any future readers, please do read all the answers comments also, great stuff in there.

An empty string is not the same thing as a NULL string. When it comes to SQL, you need to understand the difference.
You need to add some sort of logic to write string values vs null values, such as...
if addOrderForm.Edit5.Text <> '' then
dbmodule.comenziQuery.Params.ParamByName('detalii').AsString := addOrderForm.Edit5.Text
else
dbmodule.comenziQuery.Params.ParamByName('detalii').Value := NULL;
This way, if the edit control is empty, a NULL will get written to the table field instead of an empty string.
As suggested, this can further be wrapped inside a common function to save you on writing a ton of code:
function NullIfEmpty(const S: string): Variant;
begin
if S <> '' then
Result := S
else
Result := NULL;
end;
And then use it like...
dbmodule.comenziQuery.Params.ParamByName('detalii'):=
NullIfEmpty(addOrderForm.Edit5.Text);

The problem you are having is due to using ParamByName( 'email' ).AsString, which will set the email column to a blank string. If you want it to remain null I would use code like this to clear the parameter,
if Trim(addOrderForm.Edit4.Text) = '' then
dbmodule.comenziQuery.Params.ParamByName('email').Clear
else
dbmodule.comenziQuery.Params.ParamByName('email').AsString := Trim(addOrderForm.Edit4.Text);

You should use TDB* components which handle automatically such problems.

Related

Mysql Query to Stored Procedure

i have got access to a view of a datatable (mysql 8) which has the following values:
Values DB
now I tried to write an mysql query to fill up the Null values with the last value, because our visualisation tool cannot use null values and the lines in the graph will disappear.
SELECT UTCTIME,
case when Flammtemperatur1 is NULL then
#vorheriger_wert_Flammtemperatur1
else
#vorheriger_wert_Flammtemperatur1 := Flammtemperatur1
end as Flammtemperatur_1,
case when Flammtemperatur2 is NULL then
#vorheriger_wert_Flammtemperatur2
else
#vorheriger_wert_Flammtemperatur2 := Flammtemperatur2
end as Flammtemperatur_2,
case when Rauchgasventilator is NULL then
#vorheriger_wert_Rauchgasventilator
else
#vorheriger_wert_Rauchgasventilator := Rauchgasventilator
end as Rauchgasventilator_,
case when Rezirkulation is NULL then
#vorheriger_wert_Rezirkulation
else
#vorheriger_wert_Rezirkulation := Rezirkulation
end as FRezirkulation_
FROM pivot_test
order by utctime asc
That works fine, but i need to save this in a view or stored procedure, so that our tool can have access to it. Views are not possible because of the session variables.
Can someone please help me creating a Stored procedure for my problem? I have never written a SP before.
Thanks!
Result
CREATE PROCEDURE XXXXXName #Flammtemperatur1 DECIMAL (3,2), #Flammtemperatur2 DECIMAL (3,2)
, #Rauchgasventilator DECIMAL (3,2) ,#Rezirkulation DECIMAL (3,2)
AS
SELECT UTCTIME,
case when Flammtemperatur1 is NULL then
#vorheriger_wert_Flammtemperatur1
else
#vorheriger_wert_Flammtemperatur1 := Flammtemperatur1
end as Flammtemperatur_1,
case when Flammtemperatur2 is NULL then
#vorheriger_wert_Flammtemperatur2
else
#vorheriger_wert_Flammtemperatur2 := Flammtemperatur2
end as Flammtemperatur_2,
case when Rauchgasventilator is NULL then
#vorheriger_wert_Rauchgasventilator
else
#vorheriger_wert_Rauchgasventilator := Rauchgasventilator
end as Rauchgasventilator_,
case when Rezirkulation is NULL then
#vorheriger_wert_Rezirkulation
else
#vorheriger_wert_Rezirkulation := Rezirkulation
end as FRezirkulation_
FROM pivot_test
order by utctime asc

Extracting very long string from JSON to CLOB

I'm trying to extract a very long string into clob from json_object_t and got some weird database behaviour (12.2c) with json_object_t.get_clob(key) method.
There is a sample code than does following:
DECLARE
l_data CLOB := '{"text": "very long string about 1M chars"}';
l_json json_object_t;
l_text CLOB := EMPTY_CLOB();
BEGIN
l_json := json_object_t.parse(l_data);
l_text := l_json.get_clob('text');
dbms_output.put_line('got ' || dbms_lob.getlength(l_text) || ' chars');
END;
When string length in a 'text' key is less than 32k chars, get_clob method works just fine and shows appropriate result, but with longer strings it produces an empty clob with zero length, just like get_string, but without 'character string buffer too small' exception.
I've tried to get same data via json_table query, but it cannot extract data to clob column at all, only varchar/number is allowed.
Is that a bug or am I doing something wrong? Is there any other ways to extract long strings from JSON keys?
I work with Oracle Database JSON Store group and would be happy to assist you with this issue you're facing. Could you try the alternate get_Clob procedure instead of this function and tell us what the behavior is?
Signature:
MEMBER PROCEDURE get_Clob(key VARCHAR2, c IN OUT CLOB)
Please try this:
DECLARE
content_json CLOB := '{"value":"';
content_json_end CLOB := '"}';
content_tmp CLOB := 'ab';
l_json json_object_t;
l_text CLOB := EMPTY_CLOB();
tmp clob;
BEGIN
-- 13 gives 16K
-- 14 gives 32K
FOR count IN 1 .. 14
loop
dbms_lob.append(content_tmp, content_tmp); -- a bad append for now
END loop;
dbms_lob.append(content_json, content_tmp);
dbms_lob.append(content_json, content_json_end);
l_json := json_object_t.parse(content_json);
l_json.get_clob('value', l_text); -- !!! TRY THIS PROC get_Clob
--l_text := l_json.get_clob('value');
dbms_output.put_line('Lob size in Kb: ');
dbms_output.put_line(dbms_lob.getLength(l_text) / 1024);
END;
/
Looking forward to your findings..
This works as well. Instead using the the get_clob method, use c:
DECLARE
CURSOR crsrJSON IS
SELECT
json_object( 'employee_id' VALUE employee_id,
'first_name' VALUE first_name,
'last_name' VALUE last_name,
'email' VALUE email,
'phone_number' VALUE phone_number,
'hire_date' VALUE to_char(hire_date,'MM/DD/YYYY'),
'job_id' VALUE job_id,
'salary' VALUE nvl(salary,0),
'commission_pct' VALUE nvl(commission_pct,0),
'manager_id' VALUE NVL(manager_id,0),
'department_id' VALUE NVL(department_id,0),
'department_name' VALUE (select department_name from departments x where x.department_id = hr.department_id),
'job_title' VALUE (select job_title from jobs x where x.job_id = hr.job_id)) emp_data
FROM
employees hr;
js_array JSON_ARRAY_T := new JSON_ARRAY_T;
json_obj JSON_OBJECT_T := JSON_OBJECT_T();
json_clob CLOB := EMPTY_CLOB();
BEGIN
FOR data_rec IN crsrJSON LOOP
js_array.append(JSON_ELEMENT_T.parse(data_rec.emp_data));
END LOOP;
json_obj.put('data',js_array);
IF json_obj.has('data') THEN
json_clob := json_obj.to_clob;
DBMS_OUTPUT.PUT_LINE(json_clob);
ELSE
DBMS_OUTPUT.PUT_LINE('Nope');
END IF;
END;
with data as
( select
xmlelement(e,regexp_replace('{"name":"'||colname||'"}', '[[:cntrl:]]', ''),',') col1
from tblname
)
select
rtrim(replace(replace(replace(xmlagg(col1).getclobval(),'&'||'quot;','"'),'<E>',''),'</E>',''),',')
as very_long_json
from data;

MySQL prepared statements result metadata

I have selective procedure
CREATE PROCEDURE sp_d_test()
NOT DETERMINISTIC
CONTAINS SQL
SQL SECURITY DEFINER
COMMENT ''
BEGIN
SELECT * FROM my_table;
END;
and I want to get metadata results (field names) using prepared statements
procedure TForm1.btnLowAPIClick(Sender: TObject);
var
LConn: Pointer;
LSQL: AnsiString;
LStmt: PMYSQL_STMT;
LCnt: Integer;
LFld: PMYSQL_FIELD;
LRes: PMYSQL_RES;
begin
LConn := MySQLInit;
mysql_real_connect(LConn, 'localhost', 'root', '', 'test', 0, nil, CLIENT_MULTI_RESULTS);
MySQLQuery(LConn, 'SET character_set_results=ucs2');
LSQL := 'CALL sp_d_test()';
LStmt := mysql_stmt_init(LConn);
if mysql_stmt_prepare(LStmt, PAnsiChar(LSQL), Length(LSQL)) <> 0 then
MySQLStmtError(LStmt);
if mysql_stmt_execute(LStmt) <> 0 then
MySQLStmtError(LStmt);
LCnt := mysql_stmt_field_count(LStmt); // Returns corrected number
LRes := mysql_stmt_result_metadata(LStmt);
LFld := mysql_fetch_field(LRes);
while LFld <> nil do begin
LStr := UCS2BytesToUTF16(PByte(LFld^.name), LFld^.name_length);
LFld := mysql_fetch_field(LRes);
end;
end;
The fields LFld^.name, LFld^.name_length contains garbage. The field LFld^.type contains corrected type
If I do
MySQLQuery(LConn, 'SET character_set_results=utf8');
then the field LFld^.name contains corrected value, but LFld^.name_length still contains garbage.
For simple select
LSQL := 'SELECT * FROM my_table';
The structure is filled correctly and for UCS2 and for UTF8. With one BUT. The documentation says
char * name
The name of the field, as a null-terminated string. If the field was given an alias with an AS clause, the value of name is the alias. For a procedure parameter, the parameter name.
For UCS2 the field name is NOT null-terminated, but name_length contains real size in bytes
Server version 5.1.41

Inserting null and string 'Null in trigger condition

I'm creating a new trigger and want to have both null value and NULL string in :new.SCO_NUMBER validation. I'm getting error when i'm using both (as shown below) but when i use ':new.SCO_NUMBER IS NULL', it works fine. How to use or in this validation.
CREATE OR REPLACE TRIGGER TRIG_SCONUMBER_INSERT AFTER
INSERT ON S_SYN_EAI_SCO_IN FOR EACH row DECLARE XYZ BEGIN XYZ
SELECT XYZ
FROM xyz
WHERE xyz IF inserting
AND :new.SCO_NUMBER IS (NULL
OR 'NULL') THEN varError_Msg := 'SCO Number cannot be NULL in';
varError_id := 1;
varSucceeded := 'N' ;
varErrorExists :=1;
END IF;
In case of T-SQL,
replace new.SCO_NUMBER IS (NULL OR 'NULL') with new.SCO_NUMBER IS NULL OR new.SCO_NUMBER = 'NULL'. This should work for you.
Try this
IF inserting
AND ( :new.SCO_NUMBER IS NULL
OR :new.SCO_NUMBER = 'NULL') THEN
Try this,
(:new.SCO_NUMBER IS NULL OR :new.SCO_NUMBER = 'NULL')

How can I avoid storing duplicates in the database in this code?

I have a user registration process which stores user info to my database using MYDAC components. Currently it allow duplicate users, which is not my intention. My code is below, but I don't know where the problem is.
procedure TForm1.Button1Click(Sender: TObject);
begin
if (edit1.Text <> '') and (edit2.Text <> '') and (edit3.Text <> '') and
(edit4.Text <> '') then
begin
MyQuery1.Close;
MyQuery1.SQL.Text := 'select * from uyeler '+
'where nick=:0 and mail=:0 and site=:0';
MyQuery1.Params[0].AsString:=edit1.text;
MyQuery1.Params[0].AsString:=edit2.text;
MyQuery1.Params[0].AsString:=edit3.text;
MyQuery1.open;
if MyQuery1.RecordCount = 0 then
MessageDlg('The same information! Try again.', mtError, [mbOK], 0)
else
MyQuery1.Close;
MyQuery1.SQL.Text := 'INSERT INTO uyeler (nick, mail, site, sifre) VALUES '+
'(:nick, :mail, :site, :sifre)';
MyQuery1.ParamByName('nick').AsString := Edit1.text;
MyQuery1.ParamByName('mail').AsString := Edit2.text;
MyQuery1.ParamByName('site').AsString := Edit3.text;
MyQuery1.ParamByName('sifre').AsString := Edit4.text;
MyQuery1.Execute;
Button1.Enabled := False;
MessageDlg('Mission complate!', mtInformation, [mbOK], 0);
Edit1.Clear;
Edit2.Clear;
Edit3.clear;
Edit4.Clear;
PageControl2.Visible := False;
PageControl1.Visible := True;
end
else
begin
MessageDlg('Information is missing! Try again.', mtWarning,[mbOK],0);
end;
end;
How can I prevent signing up with the same? What should I do in this case?
I would typically use unique indexes on the underlying MySQL table to enforce this.
You're checking the wrong result. You need to change your test to
if MyQuery1.RecordCount > 0 then // At least one match found already
Better yet, if MyDac supports it, is to use
if not MyQuery1.IsEmpty then // row already exists.
Actually, you have more issues than that, though. You have a mismatched begin and end block, because right now you're always running the insert part of the method. As #TLama says, you're also using the same pameter multiple times, assigning nick, mail, and site the all the same value. Use named parameters instead (shown below in both the SQL and the parameter assignments).
procedure TForm1.Button1Click(Sender: TObject);
var
UserExists: Boolean;
begin
Button1.Enabled:=false;
if (edit1.Text <> '') and (edit2.Text <> '') and
(edit3.Text <> '') and (edit4.Text <> '') then
begin
MyQuery1.Close;
MyQuery1.SQL.Text :=' select* from uyeler '+
'where nick=:nick and mail=:mail and site=:site';
MyQuery1.ParamByName('nick').AsString:=edit1.text;
MyQuery1.ParamByName('mail').AsString:=edit2.text;
MyQuery1.ParamByName('site').AsString:=edit3.text;
MyQuery1.open;
try
UserExists := not MyQuery1.IsEmpty;
finally
MyQuery1.Close;
end;
if UserExists then
MessageDlg('The same information! Try again.', mtError,[mbOK],0)
else
begin // <<--- Added begin
MyQuery1.SQL.Text :=' INSERT INTO uyeler (nick, mail, site, sifre) VALUES '+
'(:nick, :mail, :site, :sifre)';
MyQuery1.ParamByName('nick').AsString := Edit1.text;
MyQuery1.ParamByName('mail').AsString := Edit2.text;
MyQuery1.ParamByName('site').AsString := Edit3.text;
MyQuery1.ParamByName('sifre').AsString := Edit4.text;
try
MyQuery1.Execute;
finally
MyQuery1.Close;
end;
end; // <------------ Moved end from below where marked
MessageDlg('Mission complate!', mtInformation,[mbOK],0);
Edit1.Clear;
Edit2.Clear;
Edit3.clear;
Edit4.Clear;
PageControl2.Visible:=false;
PageControl1.Visible:=true;
end // <------------- removed extra end caused by addition above
else
MessageDlg('Information is missing! Try again.', mtWarning,[mbOK],0);
end;
IMO the answer posted by #Ken White should work fine, but since you are finding troubles. I suggest you to try with this code. Its just the difference in the executing the queries.
I am considering the field datatypes to be Char or VarChar, hence " " while entering the data values
procedure TForm1.Button1Click(Sender: TObject);
begin
Button1.Enabled:=false;
if (edit1.Text <> '') and (edit2.Text <> '') and
(edit3.Text <> '') and (edit4.Text <> '') then
begin
MyQuery1.SQL.Clear;
MyQuery1.SQL.Add(' select* from uyeler where nick="'+edit1.text+'"' +
'and mail="'+edit2.text+'" and site="'+edit3.text+'"');
MyQuery1.Execute;
if not MyQuery1.IsEmpty then //--- can also use MyQuery1.RecordCount >0
MessageDlg('The same information! Try again.', mtError,[mbOK],0)
else
begin //--- no duplicates present
MyQuery1.SQL.Clear;
MyQuery1.SQL.Add(' INSERT INTO uyeler (nick, mail, site, sifre) VALUES '+
'("'+edit1.text+'", "'+edit2.text+'","'+edit3.text+'", "'+edit4.text+'")');
try
MyQuery1.Execute;
finally
MyQuery1.SQL.Clear;
end;
MessageDlg('Mission complate!', mtInformation,[mbOK],0);
Edit1.Clear;
Edit2.Clear;
Edit3.clear;
Edit4.Clear;
PageControl2.Visible:=false;
PageControl1.Visible:=true;
end;
end;
else
MessageDlg('Information is missing! Try again.', mtWarning,[mbOK],0);
end;