SQLServerException when using StoredProcedureItemReader - sql-server

I am getting an error when I am trying to use StoredProcedureItemReader.
I am having a stored procedure written in Microsoft SQL server which has the following input and output parameters:
Stored Procedure Name: person_details
Input: #From, #To
Output is a combination of various columns from various tables.
Here is my code:
StoredProcedureItemReader<ClaimExtractDTO> reader = new StoredProcedureItemReader<>();
SqlParameter[] parameter = {new SqlParameter("#From", java.sql.Types.BIGINT), new SqlParameter("#To", java.sql.Types.BIGINT)};
PreparedStatementSetter statementValues = new PreparedStatementSetter() {
#Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setLong(1, minValue); //minValue - Input 1
ps.setLong(2, maxValue); //maxValue - Input 2
}
};
reader.setDataSource(dataSource);
reader.setProcedureName("dbo.person_details");
reader.setParameters(parameter);
reader.setPreparedStatementSetter(statementValues);
reader.setRowMapper(new BeanPropertyRowMapper<>(ClaimExtractDTO.class));
return reader;
When running the above code I am getting
Caused by: org.springframework.dao.TransientDataAccessResourceException: Executing stored procedure; SQL [{call dbo.person_details(?, ?)}]; The index 0 of the output parameter is not valid.; nested exception is com.microsoft.sqlserver.jdbc.SQLServerException: The index 0 of the output parameter is not valid.
at org.springframework.jdbc.support.SQLStateSQLExceptionTranslator.doTranslate(SQLStateSQLExceptionTranslator.java:110) ~[spring-jdbc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72) ~[spring-jdbc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:81) ~[spring-jdbc-5.2.7.RELEASE.jar:5.2.7.RELEASE]
at org.springframework.batch.item.database.StoredProcedureItemReader.openCursor(StoredProcedureItemReader.java:229) ~[spring-batch-infrastructure-4.2.3.BUILD-SNAPSHOT.jar:4.2.3.BUILD-SNAPSHOT]
at org.springframework.batch.item.database.AbstractCursorItemReader.doOpen(AbstractCursorItemReader.java:428) ~[spring-batch-infrastructure-4.2.3.BUILD-SNAPSHOT.jar:4.2.3.BUILD-SNAPSHOT]
at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.open(AbstractItemCountingItemStreamItemReader.java:150) ~[spring-batch-infrastructure-4.2.3.BUILD-SNAPSHOT.jar:4.2.3.BUILD-SNAPSHOT]
... 18 common frames omitted
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: The index 0 of the output parameter is not valid.
at com.microsoft.sqlserver.jdbc.SQLServerException.makeFromDriverError(SQLServerException.java:234) ~[mssql-jdbc-7.4.1.jre8.jar:na]
at com.microsoft.sqlserver.jdbc.SQLServerCallableStatement.getterGetParam(SQLServerCallableStatement.java:403) ~[mssql-jdbc-7.4.1.jre8.jar:na]
at com.microsoft.sqlserver.jdbc.SQLServerCallableStatement.getObject(SQLServerCallableStatement.java:705) ~[mssql-jdbc-7.4.1.jre8.jar:na]
at com.zaxxer.hikari.pool.HikariProxyCallableStatement.getObject(HikariProxyCallableStatement.java) ~[HikariCP-3.4.5.jar:na]
at org.springframework.batch.item.database.StoredProcedureItemReader.openCursor(StoredProcedureItemReader.java:222) ~[spring-batch-infrastructure-4.2.3.BUILD-SNAPSHOT.jar:4.2.3.BUILD-SNAPSHOT]
... 20 common frames omitted
when I am running the stored procedure on SQL server using exec dbo.person_details '1', '100'; it is running fine and getting me the output which consists of 20 columns from various tables.
I even set set nocount on on the Procedure.
I tried it with SqlParameter[] parameter = {new SqlOutParameter("personKey", java.sql.Types.INTEGER), new SqlParameter("#From", java.sql.Types.BIGINT), new SqlParameter("#To", java.sql.Types.BIGINT)}; thinking there should be outparameters defined. I still get the same error that's mentioned above.
Main contents of the procedure:
USE [employee]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
set nocount on
GO
ALTER PROCEDURE [dbo].[person_details]
#From BIGINT,
#To BIGINT
AS
....
Can anyone tell me how I can resolve this issue. Thanks in advance!

You need to ensure you set SET NOCOUNT ON WITHIN the stored procedure itself in order to avoid this error. No output parameters are expected but the rowcount is returned as one.
You are also running the store procedure directly again SQL Server as follows:
exec dbo.person_details '1', '100';
i.e. two input parameters and no output parameters.
Yet in your code you are setting up the parameters as follows:
SqlParameter[] parameter = {new SqlOutParameter("personKey", java.sql.Types.INTEGER), new SqlParameter("#From", java.sql.Types.BIGINT), new SqlParameter("#To", java.sql.Types.BIGINT)};
i.e. one output parameter and two input parameters.

It works after I put set nocount on inside my procedure
USE [employee]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[person_details]
#From BIGINT,
#To BIGINT
AS
set nocount on;
and my SQLParameters will look like
SqlParameter[] parameter = {new SqlParameter("#From", java.sql.Types.BIGINT), new SqlParameter("#To", java.sql.Types.BIGINT)};

Related

Getting "Procedure of function expects parameter which was not supplied" error, but all parameters are supplied

I'm trying to write a stored procedure to add data to three tables at once. The stored procedure calls three other stored procedures. I have data for each parameter, but I keep getting a missing parameter error in SQL Server. The problem is that the parameter given in the error message isn't a part of the EXECUTE statement of the main stored procedure. Running sp_help shows I'm passing all the parameters needed to the main procedure. The code is below.
Error message: Msg 201, Level 16, State 4, Procedure uspAddCustomerJob, Line 0 [Batch Start Line 157]
Procedure or function 'uspAddCustomerJob' expects parameter '#intJobID', which was not supplied.
-- =========================================
-- stored procedure to add customer record to customer table
GO
CREATE PROCEDURE uspAddCustomer
#intCustomerID AS INTEGER = 0 OUTPUT
,#strName AS VARCHAR(250)
,#strPhone AS VARCHAR(250)
,#strEmail AS VARCHAR(250)
AS
SET XACT_ABORT ON
BEGIN TRANSACTION
INSERT INTO TCustomers(strName, strPhone, strEmail)
VALUES (#strname, #strPhone, #strEmail)
COMMIT TRANSACTION
GO
-- =================================================
-- stored procedure to add job to job table
GO
CREATE PROCEDURE uspAddJob
#intJobID AS INTEGER = 0 OUTPUT
,#strJobDescription AS VARCHAR(250)
,#dtmStartDate AS DATETIME
,#dtmEndDate AS DATETIME
AS
SET XACT_ABORT ON
BEGIN TRANSACTION
INSERT INTO TJobs(strJobDescription, dtmStartDate, dtmEndDate)
VALUES (#strJobDescription, #dtmStartDate, #dtmEndDate)
COMMIT TRANSACTION
GO
-- ===================================================
-- stored procedure to add PK's from previous tables to a third table (many-to-many relationship)
GO
CREATE PROCEDURE uspAddCustomerJob
#intCustomerJobID AS INTEGER OUTPUT
,#intCustomerID AS INTEGER
,#intJobID AS INTEGER
AS
SET XACT_ABORT ON
BEGIN TRANSACTION
INSERT INTO TCustomerJobs(intCustomerID, intJobID)
VALUES (#intCustomerID, #intJobID)
COMMIT TRANSACTION
GO
-- =====================================================
-- main stored procedure that calls each component stored procedure, with execution code at the bottom. Running the EXECUTE statement yields the error mentioned above.
GO
CREATE PROCEDURE uspAddCustomerAndJob --main procedure
#intCustomerJobID AS INTEGER = 0 OUTPUT
,#strName AS VARCHAR(250)
,#strPhone AS VARCHAR(250)
,#strEmail AS VARCHAR(250)
,#strJobDescription AS VARCHAR(250)
,#dtmStartDate AS DATETIME
,#dtmEndDate AS DATETIME
AS
SET XACT_ABORT ON
BEGIN TRANSACTION
DECLARE #intCustomerID AS INTEGER = 0
DECLARE #intJobID AS INTEGER = 0
EXECUTE uspAddCustomer #intCustomerID OUTPUT, #strName, #strPhone, #strEmail;
EXECUTE uspAddJob #intJobID OUTPUT, #strJobDescription, #dtmStartDate, #dtmEndDate;
EXECUTE uspAddCustomerJob #intCustomerJobID OUTPUT, #intJobID;
COMMIT TRANSACTION
GO
--TEST CODE
DECLARE #intCustomerJobID AS INTEGER;
EXECUTE uspAddCustomerAndJob #intCustomerJobID OUTPUT, 'Joe Smith', '513-555-9644', 'Jsmith#yahoo.com', 'Fix cracked sewer pipe', '6/1/2019', '6/5/2019'
Error is happening within the procedure you executed.
Procedure definition for "uspAddCustomerJob" has 3 parameters:
CREATE PROCEDURE uspAddCustomerJob
#intCustomerJobID AS INTEGER OUTPUT
,#intCustomerID AS INTEGER
,#intJobID AS INTEGER
but it is called with 2, "#intCustomerID" is missing:
EXECUTE uspAddCustomerJob #intCustomerJobID OUTPUT, #intJobID;
The error message is misleading. In uspAddCustomerAndJob you're missing a parameter.
Your call looks like this:
EXECUTE uspAddCustomerJob #intCustomerJobID OUTPUT, #intJobID;
SQL Server is taking your first integer parameter and mapping it to the first integer parameter of the child proc, so it thinks it's getting #intCustomerID.
You can avoid this sort of confusion by explicitly naming your parameters in your call, which you should do anyway for the sake of whoever (including yourself) who might need to troubleshoot the code at 3:00AM some day. So that would look like this:
EXECUTE uspAddCustomerJob
#intCustomerJobID = #intCustomerJobID OUTPUT,
#intCustomerID = #intCustomerID
#intJobID = #intJobID;
It looks funny when your local variables are named the same thing as the parameters, but gives you the flexibility to name them differently when the situation is appropriate. Plus, no missing param surprises, or, at least, better messaging around which one is missing.

SSIS SQL Task Parameter mapping

I've a stored procedure which outputs a bit datatype. I'm trying to map the output of the stored procedure to a variable in the SSIS package (#myVar)
I'm running this from within a SSIS package and I'm trying to map the datatypes but I can't seem to figure out how to do this.
Output from Stored Procedure = bit
SSIS variable type #myVar = boolean
Mapped parameter = what??
There is no bit or bool in the tasks drop down menu, the closest is variant_bool which doesn't work, and I can't change the output type of the stored procedure -
as it is somebody's else's code.
Any Ideas, anyone?
Test code
create PROCEDURE dbo.uspGetTrueOrFalse
(
#CustomerID int,
#CustomerName nvarchar(101) output,
#IsItTrueOrFalse bit output
)
AS
BEGIN
SET NOCOUNT ON;
SET #CustomerName = 'Red Bicycle Company';
SET #IsItTrueOrFalse = 1; --Set to true
END;
GO
Setup SQL Tasks
Parameter mapping
Execution status of variables

Don't Understand SQL Procedure Error

I have a master table "Repairs" and a detail table "RepairDetails" I am trying to write a procedure to update both tables when I send the appropriate parameters from my application. Here is my SQL:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[UpdateRepair]
#RepairID bigint,
#TypeID bigint = NULL,
#Directions nvarchar(3000) = NULL,
#NewDetails NewDetails READONLY
AS
BEGIN
SET NOCOUNT ON;
UPDATE Repairs
SET
TypeID = ISNULL(#TypeID, TypeID),
Directions = ISNULL(#Directions, Directions),
LastUpdate = SYSDATETIME()
WHERE RepairID = #RepairID;
IF #NewDetails IS NOT NULL UpdateRepairDetails;
END
where "NewDetails" is a User-defined table type and "UpdateRepairDetails" is a different stored procedure that takes #RepairID and #NewDetails as parameters.
I have an error and a question. The error message is:
Must declare the scalar variable "#NewDetails"
which I don't understand because it is defined.
And my question is: will the parameters "#RepairID" and "#NewDetails" get automatically passed to the "UpdateRepairDetails" procedure. If not, what is the proper approach to accomplish this?
You cannot assign NULL to a table variable. Hence you can't check whether a table variable is NULL.
Second: no. You should call as follows:
EXEC UpdateRepairDetails #RepairID, #NewDetails;

SSIS Execute SQL Stored Procedure output parameter type mismatch

I'm getting the following error on an SSIS package:
[Execute SQL Task] Error: Executing the query "EXEC [spGetFileId] #zFileName, #Id" failed with the following error: "The type of the value being assigned to variable "User::FileId" differs from the current variable type. Variables may not change type during execution. Variable types are strict, except for variables of type Object.
I have created an Execute SQL Task which executes a simple stored procedure on the SQL server:
CREATE PROCEDURE [dbo].[spGetFileId]
#zFileName VARCHAR(255),
#Id INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT #Id = [Id]
FROM [tblFileLog]
WHERE [zFileName] = #zFileName
END
The SQLStatement property of the task is:
EXEC [spGetFileId] #zFileName, #Id
I have the following SSIS variables:
User::File (String)
User::FileId (Int)
and the parameters are mapped on the SQL task as follows:
Variable | Direction | Type | Parameter
User::File | Input | String | #zFileName
User::FileId | Output | Int32 | #Id
I've tried playing around with the variable types (int16, int32 and int64; even string) but the error persists. Please help!
EDIT:
Debugging the stored procedure I added a PRINT #Id before the END and this outputs the expected value but when doing SELECT #Id (the output param) this returns NULL.
EDIT2:
A coworker has identified the problem and offered his solution which I have marked as answer below.
The SQLStatement on the Execute SQL Task must include OUTPUT after the #Id parameter:
EXEC [spGetFileId] #zFileName, #Id OUTPUT
And you need to set your #User::FileID parameter an Output parameter to enable the variable as readwrite instead of readonly which lets it be written to.
Instead of using an output parameter, use a result set, set ResultSet to "Single row" and map the result to your variable:
CREATE PROCEDURE [dbo].[spGetFileId]
#zFileName VARCHAR(255)
AS
BEGIN
SET NOCOUNT ON;
SELECT [Id]
FROM [tblFileLog]
WHERE [zFileName] = #zFileName
END
Make sure you're using an OLE DB connection for your Execute SQL Task and read this article - http://www.bidn.com/blogs/BillBrannen/bidn-blog/612/stored-procedure-return-values-and-output-parameters-in-ssis

MS SQL: Suppress return value of stored procedure called in stored procedure

I think I have the same problem as kcrumley describes in the question "Problem calling stored procedure from another stored procedure via classic ASP". However his question does not really include an solution, so I'll give it another shot, adding my own observations:
I have two stored procedures:
CREATE PROCEDURE return_1 AS BEGIN
SET NOCOUNT ON;
SELECT 1
END
CREATE PROCEDURE call_return_1_and_return_2 AS BEGIN
SET NOCOUNT ON;
EXEC return_1
SELECT 2
END
Note that both procedures contain "SET NOCOUNT ON". When I execute "call_return_1_and_return_2" I still get two record sets. First the value 1, then the value 2.
That throws ASP (classic VBScript ASP) off the tracks.
Any hints on how I can suppress the first result set? Why is it there even with NOCOUNT?
Skipping the first record set in ASP is not an option. I need a "database only" solution.
As Matt points out in his comment, neither solution really 'swallow' the first resultset.
I don't know why you'd want this but you can 'swallow' the result of the first exec by using a table variable. It must match the exact amount and type of the result set's columns. Like so:
CREATE PROCEDURE return_1 AS
SET NOCOUNT ON;
SELECT 1
GO
CREATE PROCEDURE call_return_1_and_return_2 AS
SET NOCOUNT ON;
DECLARE #Result TABLE (res int)
insert into #Result EXEC return_1
SELECT 2
GO
Its not the NOCOUNT thats causing this, your stored procedures have a select each so each one is coming in its own result set. This could be avoided by changing your first stored procedure to use output parameters to pass the number 1 back rather than doing a select. The second stored procedure could then examine the output parameter to get the data it needs to run.
Try something like this
CREATE PROCEDURE Proc1
(
#RetVal INT OUTPUT
)
AS
SET NOCOUNT ON
SET #RetVal = 1
CREATE PROCEDURE Proc2
AS
SET NOCOUNT ON
DECLARE #RetVal int
EXEC [dbo].[Proc1]
#RetVal = #RetVal OUTPUT
SELECT #RetVal as N'#RetVal'
Those are not return variables, but output record sets. I guess that as soon as SQL server flushes output to client, you're screwed and can't take it back.
I would solve this by adding a parameter to SP return_1, that would control if return_1 would select records or just do stuff and silently exit.

Resources