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..
Related
I am trying to create a stored procedure which passes values from select statement of joined tables as parameters to another stored procedure inside to create return credit for returned purchase which are assigned in the WHERE clause of joined tables (#RANO and #returndate). I want to pass many customers (as #Customer) from the joined tables to the stored procedure CreateManCreditHDFromReturn but it picks the first #RANO in the WHERE clause of the joined statement!
How can I pass multiple values (like customers, divisions,...) from all RANOs in the where clause of the joined table?
Note: RANO mean return authorization of returned purchase.
ALTER PROCEDURE [dbo].[CreateEllieAutoCreditHDFromReturn]
AS
BEGIN
DECLARE #ARReason nVarChar(5),
#DocumentDate Datetime,
#ARDocumentType VarChar(5),
#TRANSACTIONID INT,
#TIMELASTMOD DATETIME,
#USERIDLASTMOD nvarchar(5),
#CompanyCode nVarChar(10),
#Customer nVarChar(10),
#Division nVarChar(5),
#Warehouse nVarChar(10),
#Salesman1 nVarChar(10),
#Currency nVarChar(3),
#ReturnNo int,
#RANO int,
#Style nvarchar(15),
SELECT #Pkey = #CompanyCode = rh.companycode,
#Customer = rh.CUSTOMER,
#Division = rh.DIVISION,
#Warehouse = rh.WAREHOUSE,
#Salesman1 = rh.SALESMAN1,
#Currency = rh.CURRENCY,
#ReturnNo = rh.returnno,
#RANO = rh.RANO
FROM
ReturnDetail rd
INNER JOIN
ReturnHeader rh ON rh.RETURNNO = rd.RETURNNO
WHERE
rh.RETURNDATE >= '2020-03-27 00:00:00'
AND rh.RETURNDATE < '2020-03-28 00:00:00'
AND rd.RANO IN (79383, 79820)
AND rd.ARDOCUMENTNOCOMPANY = 0
SET #ARReason = 'RM'
SET #ARDocumentType = 'RCRDT'
SET #DocumentDate = GETDATE()
SET #TRANSACTIONID = 0
SET #TIMELASTMOD = GETDATE()
SET #USERIDLASTMOD = 'WSchan'
SET #ReferenceNo = 0
DECLARE #CurrDocumentNo Int,#CurrDocumentNoCompany Int
EXEC dbo.CreateManCreditHDFromReturn
#CompanyCode,
#ARReason,
#Customer,
#Division,
#Warehouse,
#Salesman1,
#Currency,
#DocumentDate,
#ReferenceNo,
#ARDocumentType,
#CurrDocumentNo OUTPUT,
#CurrDocumentNoCompany OUTPUT,
#TRANSACTIONID,
#TIMELASTMOD,
#USERIDLASTMOD
END
You can't assign multiple values to scalar variables. If you are restricted in that you have no access to modify dbo.CreateManCreditHDFromReturn then you have to use a cursor or loop and iterate through each record in your query. The stored procedure is coded to accept one value at a time for its input parameters.
If you have the ability to modify dbo.CreateManCreditHDFromReturn then you can update it to accept a table variable, JSON, or XML and then inside that stored procedure you can unpack the input and process it accordingly.
If you have to use a cursor, which I'm guessing is going to be the case, it would look something like this:
DECLARE #CompanyCode NVARCHAR(10);
[...and other variables...]
DECLARE RANOCURS CURSOR FOR
SELECT CompanyCode, [...and other fields...]
FROM ReturnDetail rd
INNER JOIN ReturnHeader rh ON rh.RETURNNO = rd.RETURNNO
WHERE rh.RETURNDATE >= '2020-03-27 00:00:00'
AND rh.RETURNDATE< '2020-03-28 00:00:00'
AND rd.RANO IN (79383, 79820)
AND rd.ARDOCUMENTNOCOMPANY = 0;
OPEN RANOCURS;
FETCH NEXT FROM RANOCURS INTO #CompanyCode, [...other variables...]
DECLARE #CurrDocumentNo INT, #CurrDocumentNoCompany INT;
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC dbo.CreateManCreditHDFromReturn
#CompanyCode ,
[...other variable parameters...],
#CurrDocumentNo OUTPUT,
#CurrDocumentNoCompany OUTPUT;
[...do something with the return values...]
FETCH NEXT FROM RANOCURS INTO #CompanyCode, ...[other fields]
END;
CLOSE RANOCURS;
DEALLOCATE RANOCURS;
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.
I have the following stored procedure. It is very simple
ALTER PROCEDURE [dbo].[SPU_IsoSearch]
#searchby varchar(4),
#userinput varchar(25)
AS
IF(#userinput IS NOT NULL)
BEGIN
if(#searchby = 'fname')
BEGIN
SET NOCOUNT ON;
SELECT CustID, CustFirstName, CustLastName, CustCity, CustEmail
FROM Customers where CustFirstName like '%' + #userinput + '%'
ORDER BY CustFirstName
END
END
Then, I'm executing it:
exec spu_isoSearch 'fname','de'
And it does not return anything, just a message "Command(s) completed successfully."
But if I just write:
select * from Customers where CustFirstName like '%de%'
I will get the data.
What am I doing wrong here?
Thank you
Your parameter 'searchby' is varchar(4), try changing to varchar(5) or the appropriate length
Since it is declared as varchar(4), your if statement compares 'fnam' to 'fname', and since those are not equal, the select statement does not execute
As Ghost(User) has mentioned the parameter length and on a side note your procedure can be cleaned up a bit more something like this..
ALTER PROCEDURE [dbo].[SPU_IsoSearch]
#searchby varchar(5), --<-- As pointed out by OP "Ghost"
#userinput varchar(25)
AS
BEGIN
SET NOCOUNT ON;
IF(#searchby = 'fname' AND #userinput IS NOT NULL)
BEGIN
SELECT CustID, CustFirstName, CustLastName, CustCity, CustEmail
FROM Customers where CustFirstName like '%' + #userinput + '%'
ORDER BY CustFirstName
END
END
I’m executing a procedure like this:
EXEC myProcedure #name = 'Smith', #dateDeleted = NULL
I need all the Smith that don’t have a deleted date.
And this is the procedure:
CREATE PROCEDURE [dbo].[myProcedure]
#name VARCHAR(8000) = NULL
,#dateDeleted VARCHAR(8000) = NULL
AS
BEGIN
SET NOCOUNT ON;
WITH t
(name
,dateDeleted
)
AS
(
SELECT DISTINCT
name,dateDeleted FROM mytable
WHERE
(#name IS NULL OR #name = name)
AND (#dateDeleted IS NULL OR CONVERT(DATETIME, #dateDeleted, 102) = dateDeleted)
)
After the execution I have all the Smith but it does not pay any attention to the dateDeleted = NULL .. I ge all the Smiths no matter if they have a deleted date
How can I fix this?
Thanks
In your request if you pass NULL then a condition always true.Try this WHERE clause with COALESCE expression:
WHERE (#name IS NULL OR #name = name)
AND (COALESCE(#dateDeleted, dateDeleted) IS NULL
OR CONVERT(DATETIME, #dateDeleted, 102) = dateDeleted)
You need to set ansi nulls on
Execute:
set ansi_nulls on
And then execute the query.
Here is the explantion for this behaviour
http://msdn.microsoft.com/en-us/library/ms188048.aspx
I am working on a large project with a lot of stored procedures. I came into the following situation where a developer modified the arguments of a stored procedure which was called by another stored procedure.
Unfortunately, nothing prevents the ALTER PROC to complete.
Is there a way to perform those checks afterwards ?
What would be the guidelines to avoid getting into that kind of problems ?
Here is a sample code to reproduce this behavior :
CREATE PROC Test1 #arg1 int
AS
BEGIN
PRINT CONVERT(varchar(32), #arg1)
END
GO
CREATE PROC Test2 #arg1 int
AS
BEGIN
DECLARE #arg int;
SET #arg = #arg1+1;
EXEC Test1 #arg;
END
GO
EXEC Test2 1;
GO
ALTER PROC Test1 #arg1 int, #arg2 int AS
BEGIN
PRINT CONVERT(varchar(32), #arg1)
PRINT CONVERT(varchar(32), #arg2)
END
GO
EXEC Test2 1;
GO
DROP PROC Test2
DROP PROC Test1
GO
Sql server 2005 has a system view sys.sql_dependencies that tracks dependencies. Unfortunately, it's not all that reliable (For more info, see this answer). Oracle, however, is much better in that regard. So you could switch. There's also a 3rd party vendor, Redgate, who has Sql Dependency Tracker. Never tested it myself but there is a trial version available.
I have the same problem so I implemented my poor man's solution by creating a stored procedure that can search for strings in all the stored procedures and views in the current database. By searching on the name of the changed stored procedure I can (hopefully) find EXEC calls.
I used this on sql server 2000 and 2008 so it probably also works on 2005. (Note : #word1, #word2, etc must all be present but that can easily be changed in the last SELECT if you have different needs.)
CREATE PROCEDURE [dbo].[findWordsInStoredProceduresViews]
#word1 nvarchar(4000) = null,
#word2 nvarchar(4000) = null,
#word3 nvarchar(4000) = null,
#word4 nvarchar(4000) = null,
#word5 nvarchar(4000) = null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- create temp table
create table #temp
(
id int identity(1,1),
Proc_id INT,
Proc_Name SYSNAME,
Definition NTEXT
)
-- get the names of the procedures that meet our criteria
INSERT #temp(Proc_id, Proc_Name)
SELECT id, OBJECT_NAME(id)
FROM syscomments
WHERE OBJECTPROPERTY(id, 'IsProcedure') = 1 or
OBJECTPROPERTY(id, 'IsView') = 1
GROUP BY id, OBJECT_NAME(id)
-- initialize the NTEXT column so there is a pointer
UPDATE #temp SET Definition = ''
-- declare local variables
DECLARE
#txtPval binary(16),
#txtPidx INT,
#curText NVARCHAR(4000),
#counterId int,
#maxCounterId int,
#counterIdInner int,
#maxCounterIdInner int
-- set up a double while loop to get the data from syscomments
select #maxCounterId = max(id)
from #temp t
create table #tempInner
(
id int identity(1,1),
curName SYSNAME,
curtext ntext
)
set #counterId = 0
WHILE (#counterId < #maxCounterId)
BEGIN
set #counterId = #counterId + 1
insert into #tempInner(curName, curtext)
SELECT OBJECT_NAME(s.id), text
FROM syscomments s
INNER JOIN #temp t
ON s.id = t.Proc_id
WHERE t.id = #counterid
ORDER BY s.id, colid
select #maxCounterIdInner = max(id)
from #tempInner t
set #counterIdInner = 0
while (#counterIdInner < #maxCounterIdInner)
begin
set #counterIdInner = #counterIdInner + 1
-- get the pointer for the current procedure name / colid
SELECT #txtPval = TEXTPTR(Definition)
FROM #temp
WHERE id = #counterId
-- find out where to append the #temp table's value
SELECT #txtPidx = DATALENGTH(Definition)/2
FROM #temp
WHERE id = #counterId
select #curText = curtext
from #tempInner
where id = #counterIdInner
-- apply the append of the current 8KB chunk
UPDATETEXT #temp.definition #txtPval #txtPidx 0 #curtext
end
truncate table #tempInner
END
-- check our filter
SELECT Proc_Name, Definition
FROM #temp t
WHERE (#word1 is null or definition LIKE '%' + #word1 + '%') AND
(#word2 is null or definition LIKE '%' + #word2 + '%') AND
(#word3 is null or definition LIKE '%' + #word3 + '%') AND
(#word4 is null or definition LIKE '%' + #word4 + '%') AND
(#word5 is null or definition LIKE '%' + #word5 + '%')
ORDER BY Proc_Name
-- clean up
DROP TABLE #temp
DROP TABLE #tempInner
END
You can use sp_refreshsqlmodule to attempt to re-validate SPs (this also updates dependencies), but it won't validate this particular scenario with parameters at the caller level (it will validate things like invalid columns in tables and views).
http://www.mssqltips.com/tip.asp?tip=1294 has a number of techniques, including sp_depends
Dependency information is stored in the SQL Server metadata, including parameter columns/types for each SP and function, but it isn't obvious how to validate all the calls, but it is possible to locate them and inspect them.