Setting double value to NULL in Database using Delphi / FireDac - delphi

I am reading and writing data into an Microsoft Access database using Delphi 11.2 and FireDac.
I'd like to be able to set numeric values in the database to NULL based on the (double) value in delphi.
I am currently reading values as strings from the DB
myQuery.FieldByName('myNumericField').AsString
into my delphi object fields and set double values on application level to NAN if I find empty strings. When I write them back to the DB and the user has not set the value on application level the value is obviously set to NAN in the DB as well, but not to NULL, which would be preferred.
Does anybody know a feasible way to set double / numeric fields to NULL in an (microsoft Access or presumably any other) database via firedac?
Help is much appreciated!
Simplified code samples for update and insert I am currently using
update:
dbMain.ExecSQL('update MyTable set ' + 'myNumericField = :P1 ' + 'where myIDField = :P2', [myDataObject.myDoubleValue, myDataObject.myId]);
insert:
dbMain.ExecSQL
('insert into MyTable(myIDField, myNumericField) '
+ 'values(:P1, :P2)',
[myDataObject.myId, myDataObject.myDoubleValue]);

Try something like this:
var
varValue: Variant;
if IsNan(myDataObject.myDoubleValue) then
varValue := Null
else
varValue := myDataObject.myDoubleValue;
dbMain.ExecSQL('update MyTable set myNumericField = :P1 where myIDField = :P2', [varValue, myDataObject.myId]);
Alternatively:
if IsNan(myDataObject.myDoubleValue) then
dbMain.ExecSQL('update MyTable set myNumericField = NULL where myIDField = :P1', [myDataObject.myId])
else
dbMain.ExecSQL('update MyTable set myNumericField = :P1 where myIDField = :P2', [myDataObject.myDoubleValue, myDataObject.myId]);
Alternatively:
var
Params: TFDParams;
Param: TFDParam;
Params := TFDParams.Create;
try
Param := Params.Add('P1', ftFloat);
if IsNan(myDataObject.myDoubleValue) then
begin
Param.Bound := True;
Param.Clear;
end else
Param.Value := myDataObject.myDoubleValue;
Params.Add('P2', myDataObject.myId);
dbMain.ExecSQL('update MyTable set myNumericField = :P1 where myIDField = :P2', Params);
finally
Params.Free;
end;

Related

Error -206 creating Interbase stored procedure

I am attempting to create a stored procedure using an IBConsole interactive SQL window. The intent of the procedure is to gather data from two tables, do some processing on that data, insert rows containing that data into a working table, then return the rows from the working table as the stored procedure result. The stored procedure:
SET TERM ^;
CREATE PROCEDURE BIBLIO_SUBJECT
RETURNS (ID INTEGER, SUBJECT VARCHAR(200))
AS
DECLARE VARIABLE CUR_SUBJECT VARCHAR(200);
DECLARE VARIABLE SUBJECT_LIST VARCHAR(500);
DECLARE VARIABLE BIBLIOID INTEGER;
DECLARE VARIABLE NEXTPOS INTEGER;
BEGIN
DELETE FROM TSUBJECTS;
FOR SELECT BIBLIO.ID, BIBLIO.SUBJECTS
FROM BIBLIO
WHERE BIBLIO.PUBLISH = TRUE
INTO :BIBLIOID, :SUBJECT_LIST
DO
BEGIN
SUBJECT_LIST = BIBLIO.SUBJECTS + ';';
NEXTPOS = LOCATE(";", SUBJECT_LIST);
WHILE (:NEXTPOS > 1) DO
BEGIN
CUR_SUBJECT = SUBSTR(:SUBJECT_LIST, 1, NEXTPOS - 1);
SUBJECT_LIST = SUBSTR(:SUBJECT_LIST, NEXTPOS + 1, STRLEN(SUBJECT_LIST));
INSERT INTO TSUBJECTS (SUBJECT, ID) VALUES(:CUR_SUBJECT, :BIBLIOID);
NEXTPOS = LOCATE(";", SUBJECT_LIST);
END
END
FOR SELECT BIBLIO_BEAST.BIBLIO_ID, BEAST.BEAST_NAME
FROM BIBLIO_BEAST
INNER JOIN BEAST ON (BEAST.ID = BIBLIO_BEAST.BEAST_ID)
INNER JOIN BIBLIO ON (BIBLIO.ID = BIBLIO_BEAST.BIBLIO_ID)
WHERE BIBLIO.PUBLISH = TRUE
INTO :BIBLIOID, :CUR_SUBJECT
DO
BEGIN
INSERT INTO TSUBJECTS (SUBJECT, ID) VALUES(:CUR_SUBJECT, :BIBLIOID);
END
FOR SELECT ID, SUBJECT
FROM TSUBJECTS
INTO :ID, :SUBJECT
DO
SUSPEND;
END^
SET TERM ;^
When I execute the above code in IBConsole I get the error:
Error at line 2
Dynamic SQL Error
SQL error code = -206
Column unknown
SQL - CREATE PROCEDURE BIBLIO_SUBJECT
RETURNS (ID INTEGER, SUBJECT VARCHAR(200))
AS
DECLARE VARIABLE CUR_SUBJECT VARCHAR(200);
DECLARE VARIABLE SUBJECT_LIST VARCHAR(500);
DECLARE VARIABLE BIBLIOID INTEGER;
DECLARE VARIABLE NEXTPOS INTEGER;
BEGIN
DELETE FROM TSUBJECTS;
FOR SELECT BIBLIO.ID, BIBLIO.SUBJECTS
FROM BIBLIO
WHERE BIBLIO.PUBLISH = TRUE
INTO :BIBLIOID, :SUBJECT_LIST
DO
BEGIN
SUBJECT_LIST = BIBLIO.SUBJECTS + ';';
NEXTPOS = LOCATE(";", SUBJECT_LIST);
WHILE (:NEXTPOS > 1) DO
BEGIN
CUR_SUBJECT = SUBSTR(:SUBJECT_LIST, 1, NEXTPOS - 1);
SUBJECT_LIST = SUBSTR(:SUBJECT_LIST, NEXTPOS + 1, STRLEN(SUBJECT_LIST));
INSERT INTO TSUBJECTS (SUBJECT, ID) VALUES(:CUR_SUBJECT, :BIBLIOID);
NEXTPOS = LOCATE(";", SUBJECT_LIST);
END
END
FOR SELECT BIBLIO_BEAST.BIBLIO_ID, BEAST.BEAST_NAME
FROM BIBLIO_BEAST
INNER JOIN BEAST ON (BEAST.ID = BIBLIO_BEAST.BEAST_ID)
INNER JOIN BIBLIO ON (BIBLIO.ID = BIBLIO_BEAST.BIBLIO_ID)
WHERE BIBLIO.PUBLISH = TRUE
INTO :BIBLIOID, :CUR_SUBJECT
DO
BEGIN
INSERT INTO TSUBJECTS (SUBJECT, ID) VALUES(:CUR_SUBJECT, :BIBLIOID);
END
FOR SELECT ID, SUBJECT
FROM TSUBJECTS
INTO :ID, :SUBJECT
DO
SUSPEND;
END
It doesn't say which column is "unknown", and line 2 is (apparently) the RETURNS clause of the CREATE PROCEDURE. Not at all helpful.
Each of the SQL statements in the stored procedure (3 SELECTs, a DELETE, 2 INSERTs) work without error if executed separately in an Interactive SQL window, so all of their columns are "known". BIBLIO_SUBJECT is not the name of an existing stored procedure, nor is it the name of an existing table, or anything else in the database.
This has me baffled. Online searches provide no answer. So I am hoping the clever Stack Overflow people can help me get this working.
David
Well, I figured it out. It was the statement:
SUBJECT_LIST = BIBLIO.SUBJECTS + ';';
The SUBJECT_LIST variable is already used in the preceding select. A silly mistake, but I am old.
Not that it matters since the Interbase UDF functions Locate() and Substr() can only handle 80 character strings, which makes them really useless to me... and probably useless to most.

Dynamic Insert through Teradata Stored procedure

I am trying to run Insert statement to load data into table using Teradata Stored procedure. Here I am trying to Input Table Name, Databasename as Parameter. My stored procedure compiled and ran well. But its not inserting any data into table. Could someone please help me with this. Below is the query I am using..
REPLACE PROCEDURE DB.TEST_SP
(
IN SRC_DB_NM VARCHAR(30)
, IN SRC_TBL_NM VARCHAR(30)
, OUT MESSAGE VARCHAR(200)
)
DYNAMIC RESULT SETS 1
BEGIN
DECLARE QUERY1 VARCHAR(200);
DECLARE RESULT1 VARCHAR(200);
DECLARE REC_COUNT INTEGER DEFAULT 0;
DECLARE STATUS CHAR(10) DEFAULT '00000';
DECLARE C1 CURSOR FOR S1;
DECLARE EXIT HANDLER FOR SQLEXCEPTION,SQLWARNING
BEGIN
SET STATUS = SQLCODE;
IF(TRIM(STATUS)) = '3807' THEN
SET MESSAGE = 'PASSED TABLE '||SRC_DB_NM||'.'||SRC_TBL_NM||' DOES NOT EXIST';
ELSE
SET MESSAGE = 'LOADED';
END IF;
END;
BEGIN
SET QUERY1 ='INSERT INTO TABLE1 SELECT ColX , count(*) from DB.table2 where Col in ( SELECT Col1 FROM ' || SRC_DB_NM || '.' || SRC_TBL_NM || ' where ColY = 999 ) group by 1;' ;
EXECUTE IMMEDIATE QUERY1;
PREPARE S1 FROM QUERY1;
OPEN C1 USING SRC_DB_NM,SRC_TBL_NM;
FETCH C1 INTO RESULT1;
SET MESSAGE = RESULT1;
END;
END;

Can you access the return of a stored proc using cfstoredproc, even if return is not defined as an out parameter?

I have a stored procedure whose return I want to access, using the Coldfusion cfstoredproc tag. However, the return variable is not listed as an out param, along with the 6 "in" params. See procedure's code below:
CREATE PROCEDURE [dbo].[sp_Hire_AddEVerifyEmpCloseCase]
(
#e_verify_id bigint
,#ClientSftwrVer varchar(30)=null
,#CaseNbr char(15)
,#CloseStatus varchar(50)
,#CurrentlyEmployed varchar(1)
,#submit_user_id int
//THIS LINE IS MISSING: #EmpCloseCase_Id int OUTPUT
)
AS
BEGIN
DECLARE #EmpCloseCase_id int
SET #EmpCloseCase_id=0
SELECT #EmpCloseCase_id = EmpCloseCase_id FROM Appl_Hired_EVerify_EmpCloseCase WITH(NOLOCK) WHERE e_verify_id = #e_verify_id
BEGIN TRANSACTION EmpCloseCase
BEGIN TRY
IF(#EmpCloseCase_id = 0) BEGIN
INSERT INTO Appl_Hired_EVerify_EmpCloseCase(e_verify_id,ClientSftwrVer,CaseNbr,CloseStatus,CurrentlyEmployed, systemdate,submit_user_id)
VALUES (#e_verify_id,#ClientSftwrVer,#CaseNbr,#CloseStatus,#CurrentlyEmployed,GETDATE(),#submit_user_id)
SET #EmpCloseCase_id=ISNULL(SCOPE_IDENTITY(),0)
END ELSE BEGIN
UPDATE Appl_Hired_EVerify_EmpCloseCase
SET ClientSftwrVer = #ClientSftwrVer,
CaseNbr = #CaseNbr,
CloseStatus = #CloseStatus,
CurrentlyEmployed = #CurrentlyEmployed,
systemdate = GETDATE(),
submit_user_id = #submit_user_id
WHERE EmpCloseCase_id = #EmpCloseCase_id
END
COMMIT TRANSACTION EmpCloseCase
END TRY
BEGIN CATCH
SET #EmpCloseCase_id=0
ROLLBACK TRANSACTION EmpCloseCase
END CATCH
RETURN #EmpCloseCase_id
END
Because that "OUTPUT" line is missing, it throws an error if I try to include <cfprocparam type="out" variable="empCloseCaseId"> in my cfstoredproc. Is there any way to access/store the value of this return variable #EmpCloseCase_id, using cfstoredproc, without having to add in that missing "OUTPUT" line or otherwise change the proc's code?
Change
RETURN #EmpCloseCase_id
to
SELECT #EmpCloseCase_id as emp_close_case_id
and in your cfstoredproc call, add
<cfprocresult name="foo">
This defines the variable foo as a query with a single row and a column emp_close_case_id.
<cfoutput>
#foo.emp_close_case_id#
</cfoutput>
EDIT: No way to access that data without properly declaring the output variable or returning a data set with a select statement. SQL Server docs: Return Data from a Stored Procedure

FireDAC Conversion not supported for query parameter on Interbase timestamp column

I'm having a problem opening a query that uses a timestamp parameter to an interbase table. After checking stackoverflow and implementing the conversion mappings for MapRules, I get a result of "Conversion is not supported" when opening the query.
Any help is deeply appreciated, Thanks!
Here is the code that sets the parameters:
with FDQuery1 do
begin
Close;
FDQuery1.FormatOptions.OwnMapRules := True;
with FormatOptions.MapRules.Add do
begin
SourceDataType := dtDateTime;
TargetDataType := dtDateTimeStamp;
end;
FDQuery1.Params.ParamByName('BalanceDate').AsDateTime := StrToDateTime('10/03/2017');
FDQuery1.ParamByName('User').AsInteger := 1;
FDQuery1.OpenOrExecute;
end;
Here is the query:
SELECT
AC.ACCOUNT_ID,
AC.ACCOUNT_NAME,
ACT.ACCOUNT_TYPE_NAME,
(Select Sum(P1.TOTAL_VAL) From PMNTDOCTRANS P1, DAYBATCH D1 Where
(P1.ACCOUNT_ID=AC.ACCOUNT_ID)
And (D1.DAY_BATCH_ID=P1.DAY_BATCH_ID)
And (D1.DAY_BATCH_DATE <= :BalanceDate)
And (D1.DAY_BATCH_STORE in (Select Id From ExtUserList Where List_Id=9 and User_Id =:User))
) As Val1,
(Select Sum(P2.TOTAL_VAL) From PMNTDOCTRANS P2, DAYBATCH D2 Where
(P2.OPOSIT_ACCOUNT=AC.ACCOUNT_ID)
And (D2.DAY_BATCH_ID=P2.DAY_BATCH_ID)
And (D2.DAY_BATCH_DATE <= :BalanceDate)
And (D2.DAY_BATCH_STORE in (Select Id From ExtUserList Where List_Id=9 and User_Id =:User))
) As Val2
FROM
ACCOUNT AC ,
ACCOUNTTYPE ACT
WHERE
(ACT.ACCOUNT_TYPE_ID = AC.ACCOUNT_TYPE_ID)
And
(AC.ACCOUNT_TYPE_ID in (Select Id From ExtUserList Where List_Id=3 and User_Id =:User))
ORDER BY ACCOUNT_TYPE_NAME
this problem solves like this
FDQuery1.Params.ParamByName('BalanceDate').Value:= StrToDateTime('10/03/2017');

I cannot get the output parameter when use function import by Entity Framework

Here's my SQL Server stored procedure :
ALTER PROCEDURE [dbo].[SearchUser]
(#Text NVARCHAR(100),
#TotalRows INT = 0 OUTPUT)
AS
BEGIN
SELECT #TotalRows=1000
SELECT * from Users
END
And my C# code
using (var context = new TestDBEntities())
{
var outputParameter = new ObjectParameter("TotalRows", typeof(Int32));
context.SearchUser("", outputParameter);
Response.Write(outputParameter.Value);
}
However outputParameter.Value always is null.
Could anybody tell me why?
Output parameters filled by its actual values during the execution of the stored procedure.
But table-valued stored procedure actually get executed only in moment when you're trying to iterate resulting recordset, but not calling a wrapper method.
So, this DOES'T work:
using (var context = new TestDBEntities())
{
var outputParameter = new ObjectParameter("TotalRows", typeof(Int32));
context.SearchUser("", outputParameter);
// Paremeter value is null, because the stored procedure haven't been executed
Response.Write(outputParameter.Value);
}
This DOES:
using (var context = new TestDBEntities())
{
var outputParameter = new ObjectParameter("TotalRows", typeof(Int32));
// Procedure does not executes here, we just receive a reference to the output parameter
var results = context.SearchUser("", outputParameter);
// Forcing procedure execution
results.ToList();
// Parameter has it's actual value
Response.Write(outputParameter.Value);
}
When you're working with stored procedures what don't return any recordset, they execute immediately after a method call, so you have actual value in output parameter.
We had a simular issue due to defered excecution our unit tests failed. In short if you have a stored proc that does NOT return anything you need to be sure to set the response type as 'None' when set as 'None' it will be excecuted when called and not defered.
In case you return anything (E.g. Scalar type of String results) it will excecute it when you use the result even if that .Count() or .ToList() is outside of the method that contains the function call.
So try not to force excecution if not need, when needed it should excecute but be sure to declare it correctly or it might not work.
I have same problem before. The main reason I think that the entities framework has the bug in case the user stored procedure has output parameter and return a result set. For example:
ALTER PROCEDURE [dbo].[SearchTest]
(
#RowTotal INT = 0 OUTPUT,
#RowCount INT = 0 OUTPUT
)
AS
BEGIN
SET NOCOUNT ON
SELECT * FROM SomeThing
SELECT #RowTotal = 1233, #RowCount = 5343
END
However if you change the user stored procedure as following, you can get the output params
ALTER PROCEDURE [dbo].[SearchTest]
(
#RowTotal INT = 0 OUTPUT,
#RowCount INT = 0 OUTPUT
)
AS
BEGIN
SET NOCOUNT ON
SELECT #RowTotal = 1233, #RowCount = 5343
END
You can workaround as following:
ALTER PROCEDURE [dbo].[SearchTest]
AS
BEGIN
DECLARE #RowTotal INT, #RowCount INT
SET NOCOUNT ON
SELECT #RowTotal = 1233, #RowCount = 5343
SELECT #RowTotal AS RowTotal, #RowCount AS RowCount, s.*
FROM SomeThing s
END
If anybody has better solution, please tell me

Resources