I have a stored procedure which calculates the average of several values upon the parameters.
Here's a snippet of the code:
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT
#valFound = AVG(FilterSurface)
FROM
[tblVAR_FilterSurface]
WHERE
refDate >= DATEADD(DAY, -30, #refDate)
AND refDate <= DATEADD(DAY, 0, #refDate)
AND patientId = #patientId
INSERT INTO __TEMP(PatientId, RefDate, ModifyDate, FilterSurface)
VALUES(#patientId, #refDate, #modifyDate, #valFound);
SET #valFound = NULL
FETCH NEXT FROM db_cursor INTO #patientId, #refDate, #modifyDate
END
Sometimes this stored procedure fails as the system try to calculate the average of NULL values and assign the result to #valFound variable. I think the problem is the assignment of result to that variables.
How can I modify my stored procedure in order to handle correctly null values?
Either change the select to #valFound = avg(isnull(FilterSurface, 0)) or change your where clause to include and FilterSurface is not null, depending if you want to count nulls as zero or exclude them
Related
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;
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
Is there a way to fetch a query result inside a stored procedure and then iterate over the rows in BigQuery? Having something like a cursor with each row.
This is my stored procedure, it takes 6 parameters and I take those parameters from a table. I would like to call the procedure X times (X being the number of rows of my input table). So something like :
FOR device_id, nb_measures, delta_t_min, delta_t_last_rec, date_cr, frame IN (SELECT device_id, nb_measures, delta_t_min, delta_t_last_rec, date_cr, frame FROM my_project.my_dataset.my_table)
BEGIN
DECLARE count INT64 DEFAULT 0;
SET temp_list = [];
WHILE count < nb_measures DO
SET temperature = `bdz-dts-datascience-dev.fonctions.hexStringToInt`(frame, 5 + count, 1, 0, 8);
IF temperature != 127 THEN
IF count = 0 THEN
SET measure_time = TIMESTAMP_SUB(date_cr, INTERVAL delta_t_last_rec MINUTE);
ELSE
SET measure_time = TIMESTAMP_SUB(date_cr, INTERVAL delta_t_last_rec + count * delta_t_min MINUTE);
END IF;
INSERT `20191218_temperature_repeteurs.step_2`(device_id, measure_time, temperature)
VALUES(measure_time, temperature);
END IF;
END WHILE;
END;
Or on the other hand is there a way to execute a stored procedure inside a SELECT query to iterate through the results ?
SELECT
device_id, nb_mesures, delta_t_min, delta_t_last_rec, date_cr, frame
CALL `my-dataset.my_procedure`(device_id, nb_mesures, delta_t_min, delta_t_last_rec, date_cr, frame)
FROM `my_project.my_dataset.my_table`)
Here is an example of how you can iterate over rows using store procedure taken from here
-- The input variable is employee’s employee_id (target_employee_id)
-- The output variable (OUT) is employee_hierarchy which lists
-- the employee_id of the employee’s manager
CREATE PROCEDURE dataset.GetEmployeeHierarchy(
target_employee_id INT64, OUT employee_hierarchy ARRAY<INT64>)
BEGIN
-- Iteratively search for this employee's manager, then the manager's
-- manager, etc. until reaching the CEO, who has no manager.
DECLARE current_employee_id INT64 DEFAULT target_employee_id;
SET employee_hierarchy = [];
WHILE current_employee_id IS NOT NULL DO
-- Add the current ID to the array.
SET employee_hierarchy =
ARRAY_CONCAT(employee_hierarchy, [current_employee_id]);
-- Get the next employee ID by querying the Employees table.
SET current_employee_id = (
SELECT manager_id FROM dataset.Employees
WHERE employee_id = current_employee_id
);
END WHILE;
END;
It's not possible to call a stored procedure within a SELECT. What you can do it's to create a User Defined Function[1], e.g:
CREATE TEMP FUNCTION my_udf(a INT64, b INT64, c INT64, d INT64) AS (....);
Then:
SELECT
device_id, nb_mesures, delta_t_min, delta_t_last_rec, date_cr, frame,
my_udf(device_id, nb_mesures, delta_t_min, delta_t_last_rec, date_cr, frame)
FROM `my_project.my_dataset.my_table`)
[1] https://cloud.google.com/bigquery/docs/reference/standard-sql/user-defined-functions
I have a stored procedure in which I am checking if a table returns any rows. If no rows are returned then just return an empty/dummy result set. If rows are returned then return the same result set.
Here's the procedure
BEGIN
Declare #HAS_ROWS int
SELECT #HAS_ROWS(SELECT COUNT(*) AS HAS_ROWS FROM Table1)
If(#HAS_ROWS <= 0)
BEGIN
SELECT TOP 1 NULL FROM Table1
Return
END
Else
BEGIN
SELECT * FROM Table1
Return
END
END
Upon running this procedure I get the following result
(No column name)
0
HAS_ROWS
NULL
Title
This is title of row1
This is title of row2
As you can see it is returning 3 result sets and I expect it to return on only based on IF condition. What am I doing wrong?
#Aaron is right with the #Has_Rows - it needs to be assigned with SELECT #HAS_ROWS = ... or SET #HAS_ROWS = ... (the latter has more restrictions on use).
Here is a fixed / simplified version that should work for you:
BEGIN
IF((SELECT COUNT(*) FROM Table1) = 0)
SELECT NULL
ELSE
SELECT * FROM Table1
RETURN
END
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