EF 6 optional parameter stored procedure with output parameter - sql-server

I am calling a stored procedure with optional params with an output parameter like below. I have tried ctx.Database.SqlQuery<string> and ExecuteSqlCommand. Both throws an error.
The stored procedure has these params with most of them being optional:
#Debug CHAR(1) = 'N',
#Yr VARCHAR(4),
#Collection_ID INT = NULL,
#SortOrder VARCHAR(100) = ' ',
#SrchDates VARCHAR(100) = '',
#FixedFormat CHAR(1) = 'C',
#ExtrctStrtFg CHAR(1) = 'D',
#APP_ID INT = NULL,
#MatchFound CHAR(1) = 'N' OUTPUT
My call is:
var retVal = frlentities.Database.SqlQuery<string>("exec pr_storedproc #Yr = 2000,#ExtrctStrtFg='F', #APP_ID=1234, #MatchFound OUTPUT;");
This complains that the fourth parameter #sortorder is provided as an output parameter. The same works when I do not have an output parameter and the I am getting a list with other stored procedures. Hope there is some way around this. I can provide all parameter by importing the stored procedure. But some stored procedures have too many parameters. Thank you

Related

SQL Server stored procedure with default values

I Have SQL Server stored procedure with ~100 parameters, with default values. I have to pass only 3 of them. Is it possible to use StoredProcedureQuery? Now if I have procedure:
create procedure MyProc(#Param1 varchar(30) = "test", #Param2 smallint = 1, #Param3 char = 'a', #Param4 char = 'b',
#Param5 varchar(50) = "test", #Param6 tinyint = 7, #Param7 varchar(3) = "tes", #Param8 varchar(3) = "ADS",
#Param9 varchar(50) = "TEST", #Param10 varchar(8) = "TEST", #Param11 varchar(222) = "TEST")
and executing procedure like this:
StoredProcedureQuery storedProcedureQuery = entityManagerFactory.createEntityManager()
.createStoredProcedureQuery("MyProc");
query.registerStoredProcedureParameter("Param9", String.class, ParameterMode.IN)
.setParameter("Param9", "test9");
query.registerStoredProcedureParameter("Param10", Character.class, ParameterMode.IN)
.setParameter("Param10", "test10");
query.registerStoredProcedureParameter("Param11", String.class, ParameterMode.IN)
.setParameter("Param11", "test11");
Unfortenatly value "test9" goes to #Param1, "test10" goes to #Param2 and "test11" goes to #Param3.
Is it possible to pass parameters by name, not by position?
It is possible to pass parameters by name using JPA and SQL Server using NativeQuery.
Query query = entityManagerFactory.createEntityManager().createNativeQuery("""
DECLARE #return_value int
EXEC #return_value = [dbo].[MyProc]
#Param9 = :Param9,
#Param10 = :Param10,
#Param11 = :Param11
""");
query.setParameter("Param9", "test9");
query.setParameter("Param10", "test10");
query.setParameter("Param11", "test11");

Getting an error when trying to execute procedure within a procedure

I'm trying to write a procedure that executes another procedure within it, but I'm getting this error
Incorrect syntax near '#PRIMARY_AM'
but I get this error only with the variables that have a CAST() OR ISNULL() OR REPLACE() function. If I comment out the line with #PRIMARY_AM it will then say
Incorrect syntax near 'ISNULL'
CREATE PROCEDURE [dbo].[RUN_PROCESS]
#PRIMARY_NO VARCHAR(20) = NULL,
#COMBINED_AM INT = NULL,
#PRIMARY_BOR VARCHAR(40) = NULL,
#PRIMARY_AM INT = NULL,
#SECONDARY_AM INT = NULL,
#SECONDARY_DT SMALLDATETIME = NULL,
#PRIMARY_CD VARCHAR(10) = NULL,
#O_ID INT OUTPUT
AS
EXEC dbo.LINK_PROCESS
#PRIMARY_NO = #PRIMARY_NO,
#COMBINED_AM = CAST(#PRIMARY_AM + #SECONDARY_AM AS VARCHAR),
#PRIMARY_BOR = REPLACE(ISNULL(#PRIMARY_BOR, ''), '''',' '),
#PRIMARY_AM = CAST(ISNULL(#PRIMARY_AM, 0) AS VARCHAR),
#SECONDARY_AM = CAST(ISNULL(#SECONDARY_AM, 0) AS VARCHAR),
#SECONDARY_DT = CAST(#SECONDARY_DT AS VARCHAR),
#PRIMARY_CD = ISNULL(#PRIMARY_CD, ''),
#O_ID = #O_ID OUTPUT;
If I remove those functions CAST(), REPLACE() and ISNULL() then I can execute the query without a problem. I don't know why this isn't working.
I'm using SQL Server 2005
This is the solution to my problem, thanks to Alex and his comment under my question
Stored procedures, accept parameters (variables and constants) but not
expressions. You need to do all casting before calling your SP. – Alex
CREATE PROCEDURE [dbo].[RUN_PROCESS]
#PRIMARY_NO VARCHAR(20) = NULL,
#COMBINED_AM INT = NULL,
#PRIMARY_BOR VARCHAR(40) = NULL,
#PRIMARY_AM INT = NULL,
#SECONDARY_AM INT = NULL,
#SECONDARY_DT SMALLDATETIME = NULL,
#PRIMARY_CD VARCHAR(10) = NULL,
#O_ID INT OUTPUT
AS
DECLARE #COMBINED VARCHAR
SET #COMBINED = CAST(#PRIMARY_AM + #SECONDARY_AM AS VARCHAR)
DECLARE #PRIM_BOR VARCHAR(40)
SET #PRIM_BOR = REPLACE(ISNULL(#PRIMARY_BOR, ''), '''',' ')
DECLARE #PRIM_AM VARCHAR
SET #PRIM_AM = CAST(ISNULL(#PRIMARY_AM, 0) AS VARCHAR)
DECLARE #SEC_AM VARCHAR
SET #SEC_AM = CAST(ISNULL(#SECONDARY_AM, 0) AS VARCHAR)
DECLARE #SEC_DT VARCHAR
SET #SEC_DT = CAST(#SECONDARY_DT AS VARCHAR)
DECLARE #PRIM_CD VARCHAR(10)
SET #PRIM_CD = ISNULL(#PRIMARY_CD, '')
EXEC dbo.LINK_PROCESS
#PRIMARY_NO = #PRIMARY_NO,
#COMBINED_AM = #COMBINED,
#PRIMARY_BOR = #PRIM_BOR,
#PRIMARY_AM = #PRIM_AM,
#SECONDARY_AM = #SEC_AM,
#SECONDARY_DT = #SEC_DT,
#PRIMARY_CD = #PRIM_CD,
#O_ID = #O_ID OUTPUT;

How to make the stored procedure run faster without using several SPs with different names instead of 1?

I have a stored procedure with all the fields of a table as input values as following. By sending any of items and letting other input items NULL from application, we can get the required data.
ALTER procedure [APP].[CRM_Action_Select]
#ID int = null ,
#DashbordRefrenceID int = null ,
#Refrence int = null ,
#Description NVARCHAR(max) = null ,
#AttemptCode UNIQUEIDENTIFIER = null ,
#AttemptName NVARCHAR(100),
#OrgAttemptCode INT = null ,
#OrgAttemptName NVARCHAR(100) = null ,
#OP_CheckerCode UNIQUEIDENTIFIER =NULL,
#OP_CheckerName NVARCHAR(100)=NULL,
#Org_ChekerCode INT=NULL,
#Org_ChekerName NVARCHAR(100)=NULL,
#ActionDate VARCHAR(50)=NULL,
#CheckDate VARCHAR(50)=NULL,
#ActionDescription NVARCHAR(max),
#RealyAction int = null,
#IsRead int = null
as
begin
select *
from [app].[CRM_Action] with(nolock)
where
(#ID is null or ID = #ID) and
(#DashbordRefrenceID is null or DashbordRefrenceID = #DashbordRefrenceID) AND
(#Refrence is null or Refrence = #Refrence) and
(#Description is null or Description = #Description) and
(#AttemptCode is null or AttemptCode = #AttemptCode) and
(#AttemptName is null or AttemptName = #AttemptName) AND
(#OrgAttemptCode is null or OrgAttemptCode = #OrgAttemptCode) and
(#OrgAttemptName is null or OrgAttemptName = #OrgAttemptName) and
(#OP_CheckerCode is null or OP_CheckerCode = #OP_CheckerCode) and
(#OP_CheckerName is null or OP_CheckerName = #OP_CheckerName) and
(#Org_ChekerCode is null or Org_ChekerCode = #Org_ChekerCode) AND
(#Org_ChekerName is null or Org_ChekerName = #Org_ChekerName) and
(#ActionDate is null or ActionDate = #ActionDate) and
(#CheckDate is null or CheckDate = #CheckDate) and
(#ActionDescription is null or #ActionDescription = #ActionDescription) and
(#RealyAction is null or RealyAction = #RealyAction) and
(#IsRead is null or IsRead = #IsRead)
order by ID
end
The problem is as the table grows this procedure slows down. By making other procedures, I found out that checking the IS NULL condition in big tables makes it slow down.
I searched for overloading stored procedures, but it seems it is not possible in SQL Server.
Is there any solution for this without having several stored procedures with different names?
This is an ideal candidate for the query hint OPTION(RECOMPILE), which will take into account the actual values of the parameters rather than create an execution plan for any possible parameter.
If the actual values are taken into account, the execution plan can eliminate the parts that should not be evaluated. E.g. if #ID is in fact NULL, that condition can be removed. That is because (TRUE OR X) is always TRUE, and in a series of conditions in the style of X AND Y AND Z ..., if X is TRUE this is equivalent to Y AND Z ....
At the end of the query, add the following:
OPTION(RECOMPILE)
See query hints for more details.
You can simply create a dynamic query by setting a base query and extending content based on parameters specified:
#Query = 'select * from [app].[CRM_Action] with(nolock)'
IF (#Id IS NOT NULL)
#Query += ' WHERE Id = #Id '
IF (#DashbordRefrenceID IS NOT NULL)
#Query += ' AND DashbordRefrenceID = #DashbordRefrenceID '
IF (#Description IS NOT NULL)
#Query += ' AND Description = #Description '
At the end only execute #Query:
EXECUTE sp_executesql #Query

Procedure with update new information

So I have been working on simple procedure for school that asks me to:
(Create a stored procedure called UpdateProduct that takes in a required Product ID parameter and optionally any other one or more fields in the Product table. Then the proc will update any fields passed in but leave any other fields as they were.
If executed like this: UpdateProduct #productID = 1, #name = ‘Steel Ball Bearing’; Only the name should change, all other fields should still contain the value there before the procedure
was called)
This is what I have but still I cant seem to get it to work properly. I am going nuts because I have been trying to figure this out for several days. Any help pointing out my mistake would be awesome.
USE AdventureWorks2012
GO
CREATE PROC UpdateProduct2
#ProductID INT ,
#Name nvarchar (50)= ISNULL,
#ProductNumber nvarchar (25) =ISNULL,
#Color nvarchar (15)=ISNULL
AS
BEGIN
UPDATE [Production].[Product]
SET
Name = ISNULL (#Name,Name),
ProductNumber = ISNULL (#ProductNumber,ProductNumber),
Color = ISNULL (#Color, Color)
WHERE #Name=Name
END
I think your where clause should be:
WHERE ProductID = #ProductID;
rather than
WHERE #Name=Name
Also you need to use NULL rather than ISNULL to set the parameter defaults:
CREATE PROC UpdateProduct2
#ProductID int,
#Name nvarchar(50) = NULL,
#ProductNumber nvarchar(25) = NULL,
#Color nvarchar(15) = NULL
AS
BEGIN
UPDATE [Production].[Product]
SET Name = ISNULL (#Name,Name),
ProductNumber = ISNULL (#ProductNumber,ProductNumber),
Color = ISNULL (#Color, Color)
WHERE ProductID = #ProductID;
END
EDIT
To answer the question about specifying NULL as the default, yes there is a reason, it allows you to call the procedure without passing the parameter.
Take the following two procedures:
CREATE PROCEDURE dbo.P1 #p1 VARCHAR(20), #p2 VARCHAR(20)
AS
BEGIN
SELECT P1 = #p1, P2 = #p2;
END;
GO
CREATE PROCEDURE dbo.P2 #p1 VARCHAR(20) = NULL, #p2 VARCHAR(20) = NULL
AS
BEGIN
SELECT P1 = #p1, P2 = #p2;
END;
The first using no default, and the latter with NULL as the default. The only way to call the first procedure is to send all parameters, e.g.
EXECUTE dbo.p1;
EXECUTE dbo.p1 #P2 = 'TEST';
EXECUTE dbo.p1 #P1 = 'TEST';
Will generate the following errors:
Msg 201, Level 16, State 4, Procedure P1, Line 0
Procedure or function 'P1' expects parameter '#p1', which was not supplied.
Msg 201, Level 16, State 4, Procedure P1, Line 0
Procedure or function 'P1' expects parameter '#p1', which was not supplied.
Msg 201, Level 16, State 4, Procedure P1, Line 0
Procedure or function 'P1' expects parameter '#p2', which was not supplied.
Whereas this:
EXECUTE dbo.p2;
EXECUTE dbo.p2 #P2 = 'TEST';
EXECUTE dbo.p2 #P1 = 'TEST';
Will generate:
P1 P2
NULL NULL
P1 P2
NULL TEST
P1 P2
TEST NULL

stored procedure confusing named parameters 'Error converting data type nvarchar to int.'

Running the exact same sql command, I get an error depending on what order the parameters are defined in the stored proc. This error was originally encountered using a stored proc mapped through entity framework, but that does not seem to be the cause of the issue.
The error message 'Error converting data type nvarchar to int.' makes it seem like the sproc is trying to jam the #CagIdList parameter into one of the nullable int parameters. Thoughts?
Sql command:
exec sp_executesql
N'rptAll.usp_SprocParameterTest #StartDate, #EndDate, #CAGIdList',
N'#StartDate datetime,#EndDate datetime,#CAGIdList nvarchar(1317)',
#StartDate='2014-11-16 00:00:00',#EndDate='2014-12-16 00:00:00',#CAGIdList=N'857,858,859'
The above command will fail with this stored proc:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [rptAll].[usp_SprocParameterTest]
(
#StartDate datetime,
#EndDate datetime,
#StartRow int = null, -- please note where this parameter started
#MaxRows int = null, -- me too
#Sort varchar(255)= null,
#mfgCode varchar(255) = null,
#CAGIdList varchar(max) = null
)
as
select 1
The same will succeed for this stored proc :
--Move the nullable int params to the end of the list
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [rptAll].[usp_SprocParameterTest]
(
#StartDate datetime,
#EndDate datetime,
#Sort varchar(255)= null,
#mfgCode varchar(255) = null,
#CAGIdList varchar(5000) = null,
#StartRow int = null, --look at mee
#MaxRows int = null --hey me too
)
as
select 1
That's because you are calling the procedure and providing values for the three first parameters, regardless of their names. The parameter names that you use in the query have no relation to the parameter names in the procedure.
If you want to specify parameter values for specific parameters, you have to name them:
rptAll.usp_SprocParameterTest #StartDate = #StartDate, #EndDate = #EndDate, #CAGIdList = #CAGIdList
This is the same difference as calling a procedure without parameter names:
rptAll.usp_SprocParameterTest '2014-11-16 00:00:00', '2014-12-16 00:00:00', N'857,858,859'
and with parameter names:
rptAll.usp_SprocParameterTest #StartDate = '2014-11-16 00:00:00', #EndDate = '2014-12-16 00:00:00', #CAGIdList = N'857,858,859'

Resources