SQL Server : Optional Parameter Behavior Logic - sql

I am implementing a stored procedure in SQL Server 2014, with two parameters: #CLIENTID and #CONTRACTID. One of the parameters is optional, so, when the stored procedure receives only the Client ID, it should return all the information related to that client, and when it receives both Client ID and Contract ID, it should return only the information related to that particular contract, from that particular client.
Here's some example code...
CREATE PROCEDURE SP_EXAMPLE_STACKOVERFLOW
#CLIENTID INT,
#CONTRACTID INT = NULL
AS
SELECT
*
FROM
Table T
WHERE
T.CLIENTID = #CLIENTID
AND (T.CONTRACTID = #CONTRACTID OR ISNULL(#CONTRACTID, 0) = 0)
The code above works, however my first attempt was to make the last line like this:
AND T.CONTRACTID = ISNULL(#CONTRACTID, T.CONTRACTID)
However this didn't work... It basically considered this last line to be evaluated to FALSE all the time.
I can't figure out why... And I'd appreciate some help

I think you want:
SELECT T.*
FROM Table T
WHERE T.CLIENTID = #CLIENTID AND
(#CONTRACTID IS NULL OR T.CONTRACTID = #CONTRACTID)
This will return all contract for a client if #CONTRACTID is NULL. It will return only the specified contract, if it is not NULL.
The fact that this doesn't work as expected:
T.CONTRACTID = ISNULL(#CONTRACTID, T.CONTRACTID)
suggests that T.CONTRACTID can be NULL. That is the only value that would not be equal to itself.

Related

Make Stored Procedure return a VarChar

First of all I am new to SQL, yet I have a great Java background. My problem is that I am trying to make this procedure return a varchar, yet it is not letting me.
I tried using the RETURN statement (I now know it only returns INTS) and the SELECT statement, but for some reason it continues to return an int.
Here is my code
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE get_TopGuildLeader
AS
--Variables
DECLARE #LeaderUID VARCHAR(20);
DECLARE #GuildID INT
DECLARE #MaxPoints INT
--Selecting Leader
SET #MaxPoints = (SELECT MAX(GuildPoint)
FROM PS_GameData.dbo._GuildRankPoint)
SET #GuildID = (SELECT GuildID
FROM PS_GameData.dbo._GuildRankPoint
WHERE GuildPoint = #MaxPoints);
SET #LeaderUID = (SELECT MasterName
FROM PS_GameData.dbo._GuildsBack
WHERE GuildID = #GuildID);
--Return Leader Name
SELECT #LeaderUID;
You need to return data using OUTPUT parameters instead of SELECT.
Refer link on how to achieve this:
https://technet.microsoft.com/en-us/library/ms187004(v=sql.105).aspx
Hope this helps!
You can use an output parameter. However, be careful with your SQL query. I'm not familiar with your data model, but it looks like your query could result in a run time error. For example, is it possible that there could be more than one GuildID with the same number of GuildPoints? If so, the second query in your stored procedure will fail and return a message like below.
Msg 512, Level 16, State 1, Line 2
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, , >= or when the subquery is used as an expression.
There are many ways to approach and protect against that potential issue. Below is an example of how to consolidate the logic down to one compact query and guarantee that there will always be one row returned.
create proc dbo.GetTopGuildLeader
#LeaderId varchar(20) output
as
set #LeaderId = (
select top 1 gb.MasterName
from PS_GameData.dbo._GuildRankPoint grp
join PS_GameData.dbo._GuildsBack gb
on grp.GuildID = gb.GuildID
order by GuildPoint);
go
Hope this helps.

Parameterized WHERE clause in SQL Server Stored Procedure

I have a Stored Procedure to get the details of Invoices
Some occasions I get the list of invoices by sending only the InvoiceID
But in some other occasions I need to get the list of invoices as per the search fields supplied by the user. To do this I send all the fields to the Stored Procedure and use those parameters as below. I included only 2 columns but there are more.
SELECT * FROM INVOICES I
WHERE
(#InvoiceNumber is null or I.InvoiceNumber = #InvoiceNumber)
and
(#PONo is null or I.PONo = #PONo)
Is there a way to send the condition for the WHERE clause as one parameter?
Yes, it is possible with Dynamic SQL, but I highly discourage to do that.
SELECT * FROM tbl WHERE #condition:
If you are considering to write the procedure
CREATE PROCEDURE search_sp #condition varchar(8000) AS
SELECT * FROM tbl WHERE #condition
Just forget it. If you are doing this, you have not completed the transition to use stored procedure and you are still assembling your
SQL code in the client.
It will also open your application to SQL Injection attacks.
You can use custom type to pass table as parameter https://msdn.microsoft.com/pl-pl/library/bb510489(v=sql.110).aspx or you can use default parameters
If you're using SQL Server 2016 or similar (check by calling select compatibility_level, name from sys.databases and seeing that your DB is 130 or higher) then you can use the string_split builtin function.
I found it works best like this (spread out for clarity)
CREATE PROCEDURE [dbo].[GetInvoices]
#InvoiceNumber int = NULL
#PONo nvarchar(1024) = NULL
AS
SELECT * from [Invoices] AS [i]
WHERE
i.InvoiceNumber = ISNULL(#InvoiceNunber, i.InvoiceNunber)
AND CASE
WHEN #PONo is null
THEN 1
ELSE (CASE
WHEN i.PONo IN (select value from string_split(#PONo, ','))
THEN 1
ELSE 0
END)
END
= 1
So if you pass in a null to either parameter it gets translated as where x = x which is always true, and if you pass in a CSV value, it selects it from a split table of values that, if present, results in the where clause being where 1=1, which is true or 0=1 if the value is not present in the input list.
So here you can pass in an invoice number, or PO number, or both, or neither and it should return what you expect.

T-SQL Querying a table using a variable

I have been tasked with updating a stored procedure and I'm trying to figure out the most efficient way of performing the select. Here is the current code, vaultID and clientID are input parameters:
SELECT
#siteID = Site.siteID,
#siteGroupID = Site.siteGroupID,
#customerID = Customer.customerID
FROM
Customer
INNER JOIN
Site ON Customer.siteID = Site.siteID
WHERE
Site.phoneNumberIVR = #vaultID
AND Site.production = 1
AND Customer.clientID = #clientID
The update I'm working on has to do with the #clientID variable. If a record has a specific #siteGroupID, the clientID needs to be passed in as it was received. If the record has a different type of #siteGroupID, the #clientID variable needs to be appended with a specific prefix. That prefix is also going to be stored on the site table.
I realize I can make a call to the site table initially to get the prefix, and then modify the #clientID variable, but I'm trying to figure out if there is a way to do this with just one call. I've been trying different case statements but I'm not sure this is even feasible.
If I understand your issue correctly, then you should be able to throw a CASE in your SELECT with your condition to do the appending:
SELECT
#siteID = Site.siteID,
#siteGroupID = Site.siteGroupID,
#customerID = Customer.customerID,
#clientID =
CASE
WHEN Site.siteGroupID = 1234 THEN Site.Prefix + #clientID
ELSE #clientID
END
FROM
Customer
inner join Site on Customer.siteID = Site.siteID
WHERE
Site.phoneNumberIVR = #vaultID
and Site.production = 1
and Customer.clientID = #clientID
Of course, depending on the datatypes of #clientID and Site.Prefix, you might have to do something other than a simple + to do the appending. For example if both were integer datatypes, you can append then with some CONVERT calls:
#clientID = CONVERT(integer, CONVERT(varchar, Site.Prefix) + CONVERT(varchar, #clientID))

How to deal with Stored Procedure?

Hello I am new in creating stored procedure can you help me how to do this.
Error:
Incorrect syntax near the keyword 'AS'.
Must declare scalar variable #Serial.
CREATE PROCEDURE sp_SIU
-- Add the parameters for the stored procedure here
#Serial varchar(50),
#Part varchar(50),
#Status varchar(50),
AS
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
/*SET NOCOUNT ON;*/
-- Insert statements for procedure here
--where in my form if i enter serial number it will show select values
Select SerialNumber,PartNumber,Status from Table1 where SerialNUmber = #Serial
--Then if is correct it will Update Status on combobox
Update Table1 SET
Status=#Status
where SerialNumber=#SerialNumber
--then Insert Serial Number,Parnumber to Table 2
DECLARE #Count int
select #Count = Count(SerialNumber) from Table1 WHERE SerialNumber = #Serial
IF #Count = 0
BEGIN
INSERT INTO Table2 (SerialNumber,PArtNumber)
VALUES
(#Serial, #Part)
END
RETURN #Count
RETURN
Edit: Moved Updated info posted as an answer into Question
Oops my post is not that kind a miss.
It is possible to join this 3 sql string in one stored procedure?
Scenario:
{
What i have to do in my form is that i will enter serial number to txtserial.text by using the select sql it will show serialnumber,partnumber and status on lblserial.text,lblpartnumber.text and lblstatus.text.
And i will compare:
txtserial.text == lblserial.text
txtpartnumber.text == lblpartnumber.text
for my error handler.
{
Select SerialNumber,PartNumber,Status from Table1 where SerialNUmber = #Serial
}
Then if they are equal then:
I will update my Status from cbostatus.text if serial and part is correct then use sql upate.
{
Update Table1 SET
Status=#Status,
Modifiedby=#username,
DateModified=#Date
where SerialNumber=#Serial
}
Then insert serialnumber, using sql insert to another table.
{
INSERT INTO Table2 (SerialNumber,DateCreated,Createdby)
VALUES
(#Serial,#date,#username)
}
something likethis.
")
You have a rogue comma here
#Status varchar(50),
AS
and the name lurches between #Serial and #SerialNumber are these intended to be 2 different parameters?
Also what is the purpose of this line?
Select SerialNumber,PartNumber,Status from Table1 where SerialNUmber = #Serial
Currently it will just send back a 3 column result set to the calling application. Is that what it is intended to do (it doesn't seem to match the following comment which seems to imply it is meant to be some kind of check)?
Yes, you can execute 3 SQL statements inside one stored procedure. You probably want to declare some local variables inside your sproc to hold the intermediate results, i.e.
CREATE PROCEDURE BLAHBLAH
#SerialNumber VarChar(50)
AS
BEGIN
DECLARE #partnumber varchar(50);
SELECT #partnumber = partnumber FROM Table WHERE serialnumber = #SerialNumber;
...
SELECT #partnumber; --- return as recordset
RETURN #partnumber; --- return as return value
END
Then you can later insert #partnumber, test #partnumber, return #partnumber etc. I don't quite understand what you want to do; seems like you mostly want to look up a partnumber based on a serial number, but you want to do some uniqueness tests also. It would help if you could clarify the goal a bit more.
I recommend you ignore the user interface stuff for the moment. Write yourself some nice clean stored procedures that encapsulate the transaction and will do the right thing even if fired off at the same time from two different connections. Get everything working to your satisfaction in your SQL environment. Then go back to the user interface.
Oops my post is not that kind a miss.
It is possible to join this 3 sql string in one stored procedure?
Scenario:
What I have to do in my form is that I will enter serial number to txtserial.text by using the select sql it will show serialnumber,partnumber and status on lblserial.text,lblpartnumber.text and lblstatus.text.
AndI will compare:
txtserial.text == lblserial.text
txtpartnumber.text == lblpartnumber.text
for my error handler.
{
Select SerialNumber,PartNumber,Status from Table1 where SerialNUmber = #Serial
}
Then if they are equal then:
I will update my Status from cbostatus.text if serial and part is correct then use sql update.
{
Update Table1
SET Status = #Status,
Modifiedby = #username,
DateModified = #Date
where SerialNumber = #Serial
}
Then insert serialnumber, using sql insert to another table.
{
INSERT INTO Table2(SerialNumber, DateCreated, Createdby)
VALUES(#Serial, #date, #username)
}
something like this.

How would you build one Select stored procedure to handle any request from a table?

I want to build a single select stored procedure for SQL 2005 that is universal for any select query on that table.
**Columns**
LocationServiceID
LocationID
LocationServiceTypeID
ServiceName
ServiceCode
FlagActive
For this table I may need to select by LocationServiceID, or LocationID, or LocationServiceTypeID or ServiceName or a combination of the above.
I'd rather not have a separate stored procedure for each of them.
I assume the best way to do it would be to build the 'WHERE' statement on NOT NULL. Something like
SELECT * FROM LocationServiceType WHERE
IF #LocationID IS NOT NULL (LocationID = #LocationID)
IF #LocationServiceID IS NOT NULL (LocationServiceID = #LocationServiceID)
IF #LocationServiceTypeID IS NOT NULL (LocationServiceTypeID = #LocationServiceTypeID)
IF #ServiceName IS NOT NULL (ServiceName = #ServiceName)
IF #ServiceCode IS NOT NULL (ServiceCode = #ServiceCode)
IF #FlagActive IS NOT NULL (FlagActive = #FlagActive)
Does that make sense?
here is the most extensive article I've ever seen on the subject:
Dynamic Search Conditions in T-SQL by Erland Sommarskog
here is an outline of the article:
Introduction
The Case Study: Searching Orders
The Northgale Database
Dynamic SQL
Introduction
Using sp_executesql
Using the CLR
Using EXEC()
When Caching Is Not Really What You Want
Static SQL
Introduction
x = #x OR #x IS NULL
Using IF statements
Umachandar's Bag of Tricks
Using Temp Tables
x = #x AND #x IS NOT NULL
Handling Complex Conditions
Hybrid Solutions – Using both Static and Dynamic SQL
Using Views
Using Inline Table Functions
Conclusion
Feedback and Acknowledgements
Revision History
First of all, your code will not work. It should look like this:
SELECT * FROM LocationServiceType WHERE
(#LocationID IS NULL OR (LocationID = #LocationID)
... -- all other fields here
This is totally valid and known as 'all-in-one query'. But from a performance point of view this is not a perfect solution as soon as you don't allow SQL Server to select optimal plan. You can see more details here.
Bottom line: if your top priority is 'single SP', then use this approach. In case you care about the performance, look for a different solution.
SELECT *
FROM LocationServiceType
WHERE LocationServiceID = ISNULL(#LocationServiceID,LocationServiceID)
AND LocationID = ISNULL(#LocationID,LocationID)
AND LocationServiceTypeID = ISNULL(#LocationServiceTypeID,LocationServiceTypeID)
AND ServiceName = ISNULL(#ServiceName,ServiceName)
AND ServiceCode = ISNULL(#ServiceCode,ServiceCode)
AND FlagActive = ISNULL(#FlagActive,FlagActive)
If a null value is sent in it will cancel out that line of the where clause, otherwise it will return rows that match the value sent in.
What I've always done is is set the incoming parameters to null if should be ignored in query
then check variable for null first, so if variable is null condition short circuits and filter is not applied. If variable has value then 'or' causes filter to be used. Has worked for me so far.
SET #LocationID = NULLIF(#LocationID, 0)
SET #LocationServiceID = NULLIF(#LocationServiceID, 0)
SET #LocationServiceTypeID = NULLIF(#LocationServiceTypeID, 0)
SELECT * FROM LocationServiceType WHERE
(#LocationID IS NULL OR LocationID = #LocationID)
AND (#LocationServiceID IS NULL OR LocationServiceID = #LocationServiceID)
AND (#LocationServiceTypeID IS NULL OR #LocationServiceTypeID = #LocationServiceTypeID)
etc...