SQL Server 2000 stored procedure branching with parameters - sql

I want to create a stored procedure. If the parameter is -1 then there should not be a where clause on that column else there should be a WHERE clause. What's the best way to do it without a lot of IF branching?
I checked the archive. There are a few similar questions but not exactly the same.
CREATE PROCEDURE report
(
#site int,
#promo int,
#type int
)
AS
SET NOCOUNT ON
-- I want to avoid this:
IF #site = -1 AND #promo = -1 and #type = -1
BEGIN
SELECT * from table
END
IF #site > -1 AND #promo = -1 and #type = -1
BEGIN
SELECT * from table WHERE site = #site;
END
... -- other cases
ELSE -- all parameters are > -1
BEGIN
SELECT * from table
WHERE site = #site AND promo = #promo AND type = #type
END

This works in many cases, (despite what the comments will say without trying it) because the optimiser will ignore the ISNULL bit. Only works for non-null columns
SELECT #site = NULLIF(#site, -1) ...
SELECT * from table
WHERE site = ISNULL(#site, site) ..
Otherwise, conditional WHERE which is usually bad because OR can not be optimised
SELECT * from table
WHERE (#site = -1 OR site = #site) AND (...
Or separate stored procedures (don't think you want that either)
Or use sp_executesql (avoids dynamic SQL)

How about:
SELECT * FROM table WHERE
((site = #site) OR (#site = -1)) AND
((promo = #promo) OR (#promo = -1)) AND
((type = #type) OR (#type = -1))
One caveat, though, you may find that SQL is not very intelligent in optimizing this sort of query.

why fight against the obvious, simplest solution?
seriously, the branching solution make the intent clear, and can easily be understood by others.

Related

Using Case When to Determine whether to update values or select values

Edit: The below question was framed incorrectly by attempting to utilize the CASE WHEN expression in place of an IF statement. Please see the answer provided by schmiel. Hopefully this helps others.
I'm making a tool/report in Report Builder. The main logic is dictated by a parameter that is manually selected with a dropdown called CheckOrUpdate.
What I'm trying to accomplish is if the CheckOrUpdate parameter is set to CHECK I want the report to run a simple Query. If set to UPDATE I want the report to run an update statement.
Here is the Query:
DECLARE #SITE AS NVARCHAR(30)
DECLARE #Password AS NVARCHAR (30)
DECLARE #CheckOrUpdate AS NVARCHAR(30)
--SET #Password = 'Resend'
--SET #SITE = 'SRVCS'
--SET #CASE = '123456'
--SET #CheckOrUpdate = 'CHECK'
SELECT
CASE
WHEN #CheckOrUpdate = 'CHECK' --Just check to verify that records have been updated
THEN
(SELECT
order_num
,is_extracted
,interface_batch
,trans.item_num
,item.desc_1
,item.desc_2
FROM trans
INNER JOIN item on item.item_num=trans.item_num
WHERE order_num=#CASE AND site_code = #SITE AND is_extracted = 1 AND #Password='Resend')
WHEN #CheckOrUpdate = 'UPDATE' --Run the update
THEN
(UPDATE trans
SET is_extracted = 0 , interface_batch = NULL
WHERE order_num=#CASE AND site_code = #SITE AND is_extracted = 1 AND #Password='Resend')
--ELSE NOTHING
END
I understand that the syntax should go SET > Select > CASE WHEN. Just trying to understand how to go about running a query or running an update.
There is only one dataset in the report I'm making and the dataset is using the query above.
The commented out portion is where I was testing the logic in SQL Server.
Any ideas or references someone can point me too? Should I create another dataset and split the two? I couldn't find much in the way of what I'm looking to do.
Background: Application didn't interface info for this record due to interface outage or error and queueing stuff to be resent. Now this is being done manually just creating a tool to speed the process up for end users.
As suggested in the comments by Dan Guzman and droebi you should use a if statement like:
if #CheckOrUpdate = 'CHECK' --Just check to verify that records have been updated
begin
(SELECT
order_num
,is_extracted
,interface_batch
,trans.item_num
,item.desc_1
,item.desc_2
FROM trans t
INNER JOIN item i
on i.item_num=t.item_num
WHERE order_num=#CASE
AND site_code = #SITE
AND is_extracted = 1
AND #Password='Resend')
end
else if #CheckOrUpdate = 'UPDATE'
begin
(UPDATE trans
SET is_extracted = 0 , interface_batch = NULL
WHERE order_num=#CASE AND site_code = #SITE AND is_extracted = 1 AND #Password='Resend')
end
Or you can use else instead of else if if you only have two options.
And maybe you don't need the begin and end statement but that's the way I'm writing if statements :)

SQL Script Needs improvements, Could use some tips

I recently got a test to figure out how to optimize this SQL Script, I have never made a script, and only create queries to get data at my work. This is the Script.
The tables have valid data and joins, the #GUID is already declared and has NVARCHAR values.
This is the SQL script
I am pretty new to this, I need to optimize this script, and from what I know, its better to Join the query instead of using Sub-queries. (correct me if I am wrong)
From What I understand from this script, it is asking information from 2 different Databases
CUSTOMER_CARDNUMBER and PROFILE
I can't see the information in these Fake databases, but that is not part of this test.
In these databases it is asking for multiple information GENERATED_CARDNUMBER, and CUSTOMER_CARDNUMBER with the values = #GUID (which I don't completely understand)
IT WIll Begin a Update if the REVISION_Status = A, if GUID = #GUID what I understand this Script is looking for in both databases. and If it does not find it, it will set a REVISION_STATUS with a Date if the query does not find GUID = #GUID; ( Please correct me if wrong)
I could use help, in improving this script, how and why.. even if there are little mistakes.
Thanks!!
IF EXISTS
(
SELECT *
FROM CUSTOMER_CARDNUMBER
WHERE GENERATED_CARDNUMBER IN
(
SELECT CARDNUMBER
FROM Profile
WHERE Cardnumber IN
(
SELECT GENERATED_CARDNUMBER
FROM CUSTOMER_CARDNUMBER
WHERE CUSTOMER_CARDNUMBER = #GUID
)
)
)
BEGIN
UPDATE GUID_ACTION
SET
REVISION_STATUS = 'A',
REVISION_DATE_CREATED = GETDATE()
WHERE GUID = #GUID;
END;
ELSE
BEGIN
UPDATE GUID_ACTION
SET
REVISION_STATUS = 'F',
REVISION_DATE_CREATED = GETDATE()
WHERE GUID = #GUID;
END;
``UPDATE GUID_ACTION
SET REVISION_STATUS =
CASE WHEN (0 < select count(*) from CUSTOMER_CARDNUMBERCUSTOMER_CARDNUMBER
inner join PROFILE
ON PROFILE.Cardnumber = CUSTOMER_CARDNUMBER.GENERATED_CARDNUMBER
AND PROFILE.Cardnumber = #GUID
)
THEN 'A'
ELSE 'F'
END;
,GUID_ACTION.REVISION_DATE_CREATED = GETDATE()
WHERE CUSTOMER_CARDNUMBER.GUID = #GUID;
This is a another more cleaner way, the query script with the subquery was difficult to understand with no insight of the tables or declared #GUID

Rewrite scalar UDF to table UDF or insert UDF in stored procedure

In my stored procedure, I have a temporary table which was created to increase performance.
With the actual select statement in the stored procedure, several scalar UDF's were used and the temp table replaces them:
INSERT INTO #BEDRAGEN
SELECT
DD.ColumnA, DD.ColumnB, DD.ColumnC,
ISNULL(DBO.SIF_get_SalesAmount(DD.ColumnA, DD.ColumnB, DD.ColumnC), 0) AS Totaalbedrag,
FROM
T_InvoiceDetailDosDet as IDD
My question is: I want to replace dbo.SIF_get_SalesAmount with code or make the scalar UDF a tabled one if that will increase performance.
What is in this UDF:
Returns an amount.
It reads an file and calculates several things before resulting in an total.
Function has 3 parameters going in and Amount going out.
Piece of UDF:
ALTER FUNCTION [dbo].[SIF_get_SalesAmountDosDetail]
(#A VARCHAR(20),
#B VARCHAR(20),
#C VARCHAR(20)
)
RETURNS NUMERIC(12,2)
AS
DECLARE #SalesAmount NUMERIC(12,2)
, #SalesUnitOfAccount TINYINT
, #Unit NCHAR(5)
, #SalesUnit NCHAR(5)
, #TotalUnits NUMERIC(15, 3)
SELECT
#unit = p.Unit,
#SalesUnit = p.SalesUnit,
#SalesUnitOfAccount = dd.SalesUnitOfAccount
FROM
dbo.T_table p
WHERE
p.ColumnA = #A AND p.ColumnB = #B AND p.ColumnC = #C
SELECT #rc = ##ROWCOUNT
IF #rc <> 1
BEGIN
SELECT #SalesAmount = 0
RETURN #SalesAmount
END
IF #SalesUnit = 0
BEGIN
SELECT #SalesUnit = 1
END
-- several calculations follow based on values of #Unit etc.
-- at the end of the UDF:
-- last if then else calculation and then returning the Amount.
IF #SalesUnitOfAccount = 4
BEGIN
SELECT #PricePerDesc = #SalesUnit
SELECT #SalesAmount = CONVERT(numeric(12, 2), round((#CurrPrice * (#TotalSalesUnits / #SalesComputQty)) - #DiscAmount, 2))
END
SELECT #TotalSalesAmount = #TotalSalesAmount + ISNULL(#SalesAmount, 0)
-- Return the result of the function
RETURN #TotalSalesAmount
What way could I insert this UDF-code in my stored procedure select? Or what way could I make it a UDF_table function?
Thanks for helping.
What you are asking for is exactly the focus of a recently announced feature in SQL Server 2019 (CTP2.1 onwards) called "Scalar UDF Inlining". This feature works by automatically embedding (or inlining) the logic of a UDF into the calling query. You could give it a try by downloading it for free.
If you want to know how it works behind the scenes, the details can be found in a recent research paper “Froid: Optimization of Imperative programs in a Relational Database“. That paper describes a systematic approach to express entire UDFs as SQL, which you can use. The Scalar UDF inlining feature is based on Froid, and can result in huge performance gains in many cases.
[Disclosure: I am a co-author of the Froid paper]

Configure dynamic stored procedure with ssrs reports

I have the following SP
CREATE PROCEDURE Studentrocedure
#Type varchar(50)
AS
BEGIN
If #Type = 'Student'
Select * from tblStudent
Else If #Type = 'Fee'
Select * from tblFee
End
END
The problem I'm having is that when I add a DataSet for a SSRS report, it pulls no fields/columns in the Fields section.
How can I resolve that?
Syntax error near the ELSE part, it should be ELSE IF.
Also no need of the extra End in the query
Instead of * explicitly mention the column names
CREATE PROCEDURE Studentrocedure
#Type varchar(50)
AS
BEGIN
IF #Type = 'Student'
Select * from tblStudent
ELSE IF #Type = 'Fee'
Select * from tblFee
END
You really can't resolve this assuming that your select * queries return different result sets. A dataset requires a deterministic set of fields. You can not dynamically determine the fields that will be included in a dataset.
If you are confident that your dynamically selected results will always and forever return the same set of columns regardless of the input given then you can explore using FMTONLY option.
Check out this link

SQL Search using case or if

Everyone has been a super help so far. My next question is what is the best way for me to approach this... If I have 7 fields that a user can search what is the best way to conduct this search, They can have any combination of the 7 fields so that is 7! or 5040 Combinations which is impossible to code that many. So how do I account for when the User selects field 1 and field 3 or they select field 1, field 2, and field 7? Is there any easy to do this with SQL? I dont know if I should approach this using an IF statement or go towards a CASE in the select statement. Or should I go a complete different direction? Well if anyone has any helpful pointers I would greatly appreciate it.
Thank You
You'll probably want to look into using dynamic SQL for this. See: Dynamic Search Conditions in T-SQL and Catch-all queries for good articles on this topic.
Select f1,f2 from table where f1 like '%val%' or f2 like '%val%'
You could write a stored procedure that accepts each parameter as null and then write your WHERE clause like:
WHERE (field1 = #param1 or #param1 is null)
AND (field2 = #param2 or #param2 is null) etc...
But I wouldn't recommend it. It can definitely affect performance doing it this way depending on the number of parameters you have. I second Joe Stefanelli's answer with looking into dynamic SQL in this case.
Depends on:
how your data looks like,
how big they are,
how exact result is expected (all matching records or top 100 is enough),
how much resources has you database.
you can try something like:
CREATE PROC dbo.Search(
#param1 INT = NULL,
#param2 VARCHAR(3) = NULL
)
AS
BEGIN
SET NOCOUNT ON
-- create temporary table to keep keys (primary) of matching records from searched table
CREATE TABLE #results (k INT)
INSERT INTO
#results(k)
SELECT -- you can use TOP here to norrow results
key
FROM
table
-- you can use WHERE if there are some default conditions
PRINT ##ROWCOUNT
-- if #param1 is set filter #result
IF #param1 IS NOT NULL BEGIN
PRINT '#param1'
;WITH d AS (
SELECT
key
FROM
table
WHERE
param1 <> #param1
)
DELETE FROM
#results
WHERE
k = key
PRINT ##ROWCOUNT
END
-- if #param2 is set filter #result
IF #param2 IS NOT NULL BEGIN
PRINT '#param2'
;WITH d AS (
SELECT
key
FROM
table
WHERE
param2 <> #param2
)
DELETE FROM
#results
WHERE
k = key
PRINT ##ROWCOUNT
END
-- returns what left in #results table
SELECT
table.* -- or better only columns you need
FROM
#results r
JOIN
table
ON
table.key = r.k
END
I use this technique on large database (millions of records, but running on large server) to filter data from some predefined data. And it works pretty well.
However I don't need all matching records -- depends on query 10-3000 matching records is enough.
If you are using a stored procedure you can use this method:
CREATE PROCEDURE dbo.foo
#param1 VARCHAR(32) = NULL,
#param2 INT = NULL
AS
BEGIN
SET NOCOUNT ON
SELECT * FROM MyTable as t
WHERE (#param1 IS NULL OR t.Column1 = #param1)
AND (#param2 IS NULL OR t.COlumn2 = #param2)
END
GO
These are usually called optional parameters. The idea is that if you don't pass one in it gets the default value (null) and that section of your where clause always returns true.