Execute stored procedure with a nullable parameter from SQL server - sql-server

I have a stored procedure, MYTASK1, with several parameters. It gets called something like this:
EXECUTE MYTASK1 '2021' , '1','1'
How do I run the stored procedure if the parameter is null. I tried
EXECUTE MYTASK1 '2021' , '' ,''
but it doesn't do anything.
my stored procedure
ALTER PROC MYTASK1 #Year varchar(6) = NULL,
#quarter varchar(6) = NULL,
#month varchar(6) = NULL
AS
BEGIN
SELECT 'Sell Out' AS name,
'Total Sales Amount' AS Description,
'down' AS status,
CAST(SUM(NetValue) AS decimal(9, 2)) AS amount
FROM SalesAndReturns_RPT
GROUP BY YEAR(Call_ActualStartDate),
DATEPART(QUARTER, Call_ActualStartDate),
MONTH(Call_ActualStartDate)
HAVING YEAR(Call_ActualStartDate) = #Year
AND DATEPART(QUARTER, Call_ActualStartDate) = #quarter
AND MONTH(Call_ActualStartDate) = #month;
END;

This is a guess, but I assume this is what you want:
ALTER PROC dbo.MYTASK1 #Year int = NULL, --Makes no sense as a varchar(6)
#Quarter int = NULL, --Makes no sense as a varchar(6)
#Month int = NULL --Makes no sense as a varchar(6)
AS
BEGIN
SELECT 'Sell Out' AS name,
'Total Sales Amount' AS Description,
'down' AS status,
CAST(SUM(NetValue) AS decimal(9, 2)) AS amount
FROM dbo.SalesAndReturns_RPT
WHERE ((Call_ActualStartDate >= DATEFROMPARTS(#Year, 1, 1) AND Call_ActualStartDate < DATEFROMPARTS(#Year+1, 1, 1)) OR #Year IS NULL)
AND (DATEPART(QUARTER, Call_ActualStartDate) = #Quarter OR #Quarter IS NULL)
AND (DATEPART(MONTH,Call_ActualStartDate) = #Month OR #Month IS NULL)
OPTION (RECOMPILE);
END;
I have made the clause for #Year SARGable, however, with no information about on how #month and #quarter should be handled if #Year is NULL then this will likely make little difference, as those clauses are not SARGable. I have assumed that if #Year is NULL but #Month isn't, you would want the SUM for all rows in that month, regardless of the year it falls in.
If you do want a SARGable solution for this requirement, you'll need to implement a Calendar table.

The code which you are executing: EXECUTE MYTASK1 '2021' , '' ,'' triggers procedure MYTASK1 with empty strings as parameters. Please note that empty string is it a different value than null... NULL is lacks of value, while empty string is totally valid string ;)
You can try
EXECUTE MYTASK1 '2021' , NULL ,NULL
In your case NULL is a defoult value, so you can also use:
EXECUTE MYTASK1 '2021'

Since you have NULL as default, you can just not provide a parameter value at all
EXECUTE MYTASK1 '2021'
is the same as
EXECUTE MYTASK1 '2021', NULL, NULL
You can also use named parameters like so:
EXECUTE MYTASK1 #quarter='Q1'
However, going by other answers and comments, you have substantial issues with the contect of the stored proc.

Related

Why does T-SQL print statement cause ResourceClosedError: This result object does not return rows. It has been closed automatically

I'm calling a SQL Server SP from Jupyter, and the SP looks like this:
ALTER
procedure [dbo].[proc_Report_QuarterlyDistribution02] (#quarter int, #year int, #group int)
as
declare #total int,
#date date
set #date = cast(#year as varchar(4)) + '-01-01'
set #date = dateadd(quarter, #quarter - 1, #date)
print #date
select #total = count(1)
from DimMedical
where ServiceDate between
DATEADD(quarter, -9,#date) and #date
and carriercode = #group
and Category = 'Physicians'
The SP goes on - that's not the issue.
The problem is the line
print #date
Question Why would the print statement cause the error:
ResourceClosedError: This result object does not return rows. It has been closed automatically.
Why would the print statement cause the error: "This result object does not return rows"
This is probably a limitation in the client library you are using. Some client libraries stop looking for a result set when they see a message.
Either remove the print statement, upgrade your client library (not mentioned), or have the stored procedure insert into a table using INSERT … EXEC, and then select from that in a subsequent query.

Stored procedure Date From and Date To

I am trying to create a stored procedure for filtering orders. Basically the users have the option of filtering the order by date from and date to. So they can do search via date from, date to or use both if it makes sense?
Anyhow here is my SQL Server stored procedure so far
ALTER PROCEDURE [dbo].[CN_GetOrderItemByCustID]
#CustomerID int,
#OrderItemWRClass varchar(max) = NULL,
#OrderItemSKUName varchar(50) = NULL,
#OrderItemDateFrom Datetime,
#OrderItemDateTo Datetime
AS
BEGIN
SET NOCOUNT ON;
IF DATEDIFF(d, #OrderItemDateFrom, '01/01/1970') = 0
SET #OrderItemDateFrom = null
IF DATEDIFF(d, #OrderItemDateTo, '01/01/1970') = 0
SET #OrderItemDateTo = null
-- Insert statements for procedure here
SELECT
COM_OrderItem.OrderItemID, COM_Order.OrderID,
COM_Order.OrderDate, COM_OrderItem.OrderItemUnitCount,
COM_OrderItem.OrderItemStatus, COM_OrderItem.OrderItemSKUNAME,
COM_OrderItem.OrderItemSKUID
FROM
COM_OrderItem
INNER JOIN
COM_Order ON COM_Order.OrderID = COM_OrderItem.OrderItemOrderID
WHERE
COM_Order.OrderCustomerID = #CustomerID
OR COM_OrderItem.OrderItemWRClass LIKE #OrderItemWRClass + '%'
OR COM_OrderItem.OrderItemSKUName LIKE #OrderItemSKUName + '%'
OR CONVERT(VARCHAR, COM_Order.OrderDate, 120) LIKE #OrderItemDateFrom + '%'
ORDER BY
COM_Order.OrderDate DESC
However I am not sure on how to put the date from (OrderItemDateFrom) and date to (OrderItemDateTo) in the final SQL statement?
Should I be using OR CONVERT(VARCHAR, COM_Order.OrderDate, 120) LIKE #OrderItemDateFrom + '%' -- which gives me an error
Conversion failed when converting date and/or time from character string.
I know in a normal SQL query I would use Between OrderItemDateFrom and OrderItemDateTo
Thanks
Use this logic
ALTER PROCEDURE [dbo].[CN_GetOrderItemByCustID]
-- Add the parameters for the stored procedure here
#CustomerID int,
#OrderItemWRClass varchar(max) = NULL,
#OrderItemSKUName varchar(50) = NULL,
#OrderItemDateFrom Datetime,
#OrderItemDateTo Datetime
AS
BEGIN
SET NOCOUNT ON;
IF DATEDIFF(d,#OrderItemDateFrom,'01/01/1970')=0 SET #OrderItemDateFrom = '01/01/1970';
IF DATEDIFF(d,#OrderItemDateTo,'01/01/1970')=0 SET #OrderItemDateTo = '31/12/2199';
-- Insert statements for procedure here
SELECT COM_OrderItem.OrderItemID, COM_Order.OrderID, COM_Order.OrderDate, COM_OrderItem.OrderItemUnitCount, COM_OrderItem.OrderItemStatus, COM_OrderItem.OrderItemSKUNAME,
COM_OrderItem.OrderItemSKUID
FROM COM_OrderItem
INNER JOIN COM_Order ON COM_Order.OrderID = COM_OrderItem.OrderItemOrderID
WHERE COM_Order.OrderCustomerID = #CustomerID OR COM_OrderItem.OrderItemWRClass LIKE #OrderItemWRClass + '%' OR COM_OrderItem.OrderItemSKUName LIKE #OrderItemSKUName + '%'
OR (COM_OrderDate>=#OrderItemDateFrom && COM_OrderDate<=#OrderItemDateTo )
ORDER BY COM_Order.OrderDate DESC
Try it . It should work.
Your logic can be simplified a little by allowing NULL values for #OrderItemDateFrom and #OrderItemDateTo. Also, if filters values and column values are all DATETIMEs, you should try to compare directly to allow indexes usages (if any applied on the DATETIME column):
ALTER PROCEDURE [dbo].[CN_GetOrderItemByCustID]
#CustomerID int,
#OrderItemWRClass varchar(max) = NULL,
#OrderItemSKUName varchar(50) = NULL,
#OrderItemDateFrom Datetime = NULL, -- TODO: change caller to not provide parameter, or leave it to null
#OrderItemDateTo Datetime = NULL -- TODO: change caller to not provide parameter, or leave it to null
AS
BEGIN
SET NOCOUNT ON;
-- when working with dates try to use an unambiguous format like 'YYYY-MM-DD'
SET #OrderItemDateFrom = ISNULL(#OrderItemDateFrom, '1970-01-01')
-- assign a very large date to act like not provided
-- going one day after to catch DATETIMEs with provided time
SET #OrderItemDateTo = DATEADD(day, 1, ISNULL(#OrderItemDateTo, '3000-01-01'))
-- Insert statements for procedure here
SELECT
COM_OrderItem.OrderItemID, COM_Order.OrderID,
COM_Order.OrderDate, COM_OrderItem.OrderItemUnitCount,
COM_OrderItem.OrderItemStatus, COM_OrderItem.OrderItemSKUNAME,
COM_OrderItem.OrderItemSKUID
FROM COM_OrderItem
INNER JOIN COM_Order ON COM_Order.OrderID = COM_OrderItem.OrderItemOrderID
WHERE COM_Order.OrderCustomerID = #CustomerID
OR COM_OrderItem.OrderItemWRClass LIKE #OrderItemWRClass + '%'
OR COM_OrderItem.OrderItemSKUName LIKE #OrderItemSKUName + '%'
-- between can be used
OR (COM_OrderDate BETWEEN #OrderItemDateFrom AND #OrderItemDateTo)
ORDER BY
COM_Order.OrderDate DESC
END
Another option is to use dynamic SQL and construct it based on parameters values (i.e. insert WHERE condition if filter value is provided). This is particularly useful when filters numbers is relatively low compared to the total number of filters, as ORs are not performance friendly.
NOTE: shouldn't your filters apply in conjuction (i.e. use AND instead of OR)? It would make sense to allow the user to filter by several value in the same time.
ALTER PROCEDURE [dbo].[CN_GetOrderItemByCustID]
#CustomerID int,
#OrderItemWRClass varchar(max) = NULL,
#OrderItemSKUName varchar(50) = NULL,
#OrderItemDateFrom Datetime,
#OrderItemDateTo Datetime
AS
BEGIN
SET NOCOUNT ON;
IF DATEDIFF(d, #OrderItemDateFrom, '01/01/1970') = 0
SET #OrderItemDateFrom = null
IF DATEDIFF(d, #OrderItemDateTo, '01/01/1970') = 0
SET #OrderItemDateTo = null
-- Insert statements for procedure here
SELECT
COM_OrderItem.OrderItemID, COM_Order.OrderID,
COM_Order.OrderDate, COM_OrderItem.OrderItemUnitCount,
COM_OrderItem.OrderItemStatus, COM_OrderItem.OrderItemSKUNAME,
COM_OrderItem.OrderItemSKUID
FROM
COM_OrderItem
INNER JOIN
COM_Order ON COM_Order.OrderID = COM_OrderItem.OrderItemOrderID
WHERE
COM_Order.OrderCustomerID = #CustomerID
OR COM_OrderItem.OrderItemWRClass LIKE #OrderItemWRClass + '%'
OR COM_OrderItem.OrderItemSKUName LIKE #OrderItemSKUName + '%'
OR (#OrderItemDateFrom IS NULL OR COM_Order.OrderDate >=#OrderItemDateFrom)
OR (#OrderItemDateTo IS NULL OR COM_Order.OrderDate <=#OrderItemDateTo)
ORDER BY
COM_Order.OrderDate DESC
You should Try this.
OR (#OrderItemDateFrom IS NULL OR COM_Order.OrderDate >=#OrderItemDateFrom)
OR (#OrderItemDateTo IS NULL OR COM_Order.OrderDate <=#OrderItemDateTo)
Just Edit and try this condition..

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'

Search Stored Procedure (SQL Server) -- Conversion failed when converting date and/or time from character string -- Simple?

I have a stored procedure that I pass 3 variables bankNumber, branchNumber and DateFrom to.
Based on these variables I want to query the table (seen in picture below stored procedure) to return all records that meet the criteria I pass (through variables).
Instead I am getting this error:
Conversion failed when converting date and/or time from character string
It seems to be failing when I pass it the DateFrom variable.
Thank you for your help
ALTER PROCEDURE [dbo].[Search_Records]
#bankNumber varchar(3),
#branchNumber varchar(3),
#dateCreated datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
DECLARE
#Bank_Number varchar(3) = #bankNumber,
#Branch_Number varchar(3) = #branchNumber,
#DateFrom datetime = #dateCreated,
#DateTo datetime = #dateCreated
SELECT DISTINCT
A.bankNumber,
A.branchNumber,
A.dateCreated
FROM
dbo.CENSORED A
WHERE
(#Branch_Number IS NULL OR bankNumber LIKE #BankNumber + '%')
AND (#Branch_Number IS NULL OR branchNumber LIKE #Branch_Number + '%')
AND (#DateFrom IS NULL OR dateCreated LIKE + #DateFrom + '%')
AND (#DateTo IS NULL OR dateCreated LIKE + #DateTo + '%')
END
You cannot use the LIKE operator with Datetime value. If you are matching only on month you would need to use MONTH() function. LIKE operator can only be used with string data types.
Dont see the point of all these Variables declared in your stored procedure, a simplified version should look something like ....
ALTER PROCEDURE [dbo].[Search_Records]
#bankNumber varchar(3),
#branchNumber varchar(3),
#dateCreated datetime
AS
BEGIN
SET NOCOUNT ON;
SELECT DISTINCT
A.bankNumber,
A.branchNumber,
A.dateCreated
FROM
dbo.CENSORED A
WHERE
(#Branch_Number IS NULL OR bankNumber LIKE #bankNumber + '%')
AND (#Branch_Number IS NULL OR branchNumber LIKE #branchNumber + '%')
AND (#dateCreated IS NULL OR (MONTH(dateCreated) = MONTH(#dateCreated)
AND
YEAR(dateCreated) = YEAR(#dateCreated)))
END
Note
this will produce a very inefficient execution plan, consider using dynamic sql for queries with optional parameters like this one.

Can a computed column in design reference another in sql server?

I have 2 computed columns, [StartTime] and [EndTime]
The [StartTime] and [EndTime] are calculated with a formula using the [Week] and [Year] columns.
Now I need another computed column, [Status] that is calculated using the first two. But it gives me an error in formula when I try to use one of them inside the formula of [Status]
I really need this to work as I have no alternative. But is this even possible?
Here you go Mr -1 :
(case when [IsVOR]=(1) then 'VOR'
when [MarkedAsCompleteOn] IS NULL AND [Year]<datepart(year,getdate()) then 'Overdue'
when [MarkedAsCompleteOn] IS NULL AND [Year]>datepart(year,getdate()) then 'Not Due'
when [MarkedAsCompleteOn] IS NULL AND [Year]=datepart(year,getdate()) AND [Week]<datepart(iso_week,getdate()) then 'Overdue'
when [MarkedAsCompleteOn] IS NULL AND [Year]=datepart(year,getdate()) AND [Week]=datepart(iso_week,getdate()) then 'Due'
when [MarkedAsCompleteOn] IS NULL AND [Year]=datepart(year,getdate()) AND [Week]>datepart(iso_week,getdate()) then 'Not Due'
when [MarkedAsCompleteOn] IS NOT NULL AND [Year]<datepart(year,[MarkedAsCompleteOn]) then 'Late'
when [MarkedAsCompleteOn] IS NOT NULL AND [Year]>datepart(year,[MarkedAsCompleteOn]) then 'Early'
when [MarkedAsCompleteOn] IS NOT NULL AND [Year]=datepart(year,[MarkedAsCompleteOn]) AND [Week]<datepart(iso_week,[MarkedAsCompleteOn]) then 'Late'
when [MarkedAsCompleteOn] IS NOT NULL AND [Year]=datepart(year,[MarkedAsCompleteOn]) AND [Week]=datepart(iso_week,[MarkedAsCompleteOn]) then 'On Time'
when [MarkedAsCompleteOn] IS NOT NULL AND [MarkedAsCompleteOn]<[AllocatedTimeStart] then 'Early' end)
The last part of it causes the error :
[MarkedAsCompleteOn]<[AllocatedTimeStart] then 'Early'
And the error is generic :
- Error validating the formula for column 'Status'.
No. One computed column can not be based on a different computed column. There's a specific error for this (1759):
Computed column '%s' in table '%s' is not allowed to be used in another computed-column definition.
You could create a view, based on the table, and perform a second round of computations within the view definition. Whether you then perform all activity against the view (adding triggers if required), or only use it for lookup is a design decision you'd need to make.
Of course, one you add the idea of using a view, you can build up multiple (not just 2) layers of computation by using a Common Table expression.
You do lose the ability to make the computed column persisted, unless the view is eligible for becoming an indexed view - not that this matters in this case, since it seems to be based on date calculations, so probably not persistable anyway.
I found my own solution in the end, by using a function, this kept my production app going without any changes.
USE [DBNAME]
GO
/****** Object: UserDefinedFunction [dbo].[GetStatus] Script Date: 01/02/2013 11:08:29 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[GetStatus](#CompletedOn DATETIME, #Year INT, #Week INT)
RETURNS NVARCHAR(MAX)
AS BEGIN
DECLARE #CurrentDate DATETIME
DECLARE #StartTime DATETIME
DECLARE #EndTime DATETIME
DECLARE #Status NVARCHAR(MAX)
SET #CurrentDate = GETDATE()
SET #StartTime = (dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT([varchar](4),#Year,(0))+'-01-01'),(1)))))
SET #EndTime = (dateadd(week,#Week-(1),dateadd(day,(-1),dateadd(week,datediff(week,(0),CONVERT([varchar](4),#Year,(0))+'-01-01'),(8)))))
SET #Status = '-'
IF(#CompletedOn IS NULL)
BEGIN
IF(#CurrentDate < #StartTime)
SET #Status = 'Not Due'
IF(#CurrentDate > #StartTime AND #CurrentDate < #EndTime)
SET #Status = 'Due'
IF(#CurrentDate > #EndTime)
SET #Status = 'Overdue'
END
ELSE
BEGIN
IF(#CompletedOn < #StartTime)
SET #Status = 'Early'
IF(#CompletedOn > #EndTime)
SET #Status = 'Late'
ELSE
SET #Status = 'On Time'
END
RETURN #Status
END
And in the status field I put the formula :
([dbo].[GetStatus]([MarkedAsCompleteOn],[Year],[Week]))
This is my first ever function in sql server, so it may not be optimum.

Resources