Dynamic T-SQL stored procedure in where clause - sql-server

I need to write a sample stored procedure with 2 parameters like this:
sp_list_customers #locationid int, #category varchar
Users can pass in the #locationid = 0 to list down customers in ALL locations or specific location ID. Also they can pass null or empty string to p to slow ALL categories.
Here is my SQL code:
create procedure ...
as
select *
from customers
where locationid = (case
when isnull(#locationid, 0) = 0
then locationid
else #locationid
end)
and category = (case
when isnull(#category, '') = ''
then category
else #category
end)
However this kind of codes is running toooo slow with more parameters
I was looking for ways to fix the issue and then found out the dynamic T-SQL is the acceptable solution. But to migrate all my stored procedures is a nightmare job and error prone.
I need to write another user defined function to help me. What is the best user-defined function code for this?
Thanks

Try if the below simplified code works better...
create procedure ... as select * from customers where (nullif(#locationid,'') is null
or locationid = #locationid)
and ( nullif(#category,'') is null or category = #category)

Related

How to iterate through column name of a table and pass value to UDF in MSSQL while loop

TableName: Stocks
I am trying to fetch profit or loss of stock company in stocks table.(Refer output table in below screenshot)
I had created User defined function with passing parameter as stock company and return integer value which shows wither profit or loss.
CREATE FUNCTION FetchStockProfitLoss(
#stockCompany nvarchar(50)
)
RETURNS INT
AS
BEGIN
declare #buyStock as INT;
declare #sellStock as INT;
declare #profitLoss as INT;
Set #buyStock = (SELECT SUM(stockvalue) from stocks where stockcompany=#stockCompanyand transactiontype='buy');
Set #sellStock = (SELECT SUM(stockvalue) from stocks where stockcompany=#stockCompanyand transactiontype='sell');
set #profitLoss = (#buyStock) -(#sellStock);
RETURN #profitLoss
END;
Calling UDF by passing single StockCompany.
SELECT distinct stock_symbol,dbo.FetchIndStock('Google') as ProfitLoss from stocks where stock_symbol='Google'
How do I achieve same result(AS OUTPUT) for all stockcompanies using loop in stored procedure?
Sample Data:
TransactionID is primary column.
Output:
A UDF seems unnecessary here
A simple conditional aggregation should do the trick
Select StockCompany
,ProfitLoss = sum( StockValue * case when TransactionType = 'Buy' then -1 else 1 end)
From YourTable
Group By StockCompany

Select statement return if there is a value and if there is no value

Here's my code of the SQL Server stored procedure:
SELECT NOTES as DeletionNote
FROM STCRCHF_LOG
WHERE STHTR_ = #transferNo
IF ( ##ROWCOUNT = 0)
If there is data found, I just want to return the string of NOTES. Else if it doesn't have data, I just want to return an empty string or null.
Screenshot (executed stored procedure):
If there is data found. At my program on the web server side it gets the data.
If there is no data. In my program on the web server side it causes a NullReferenceException
If only a single record is possible then:
select coalesce((SELECT NOTES FROM STCRCHF_LOG
WHERE STHTR_ = #transferNo), '') as DeletionNote
If multiple records are possible then the following will ensure at least one row is returned:
SELECT NOTES as DeletionNote FROM STCRCHF_LOG WHERE STHTR_ = #transferNo
union all select '' /* or null if preferred */ where not exists (SELECT 1 FROM STCRCHF_LOG WHERE STHTR_ = #transferNo)
Another way which I like is to use a dummy value and OUTER APPLY like so.
-- sample data
DECLARE #table TABLE (someId INT IDENTITY, Col1 VARCHAR(100));
INSERT #table(Col1) VALUES ('record 1'),('record 2');
DECLARE #id INT = 11;
SELECT f.Col1
FROM (VALUES(NULL)) AS dummy(x)
OUTER APPLY
(
SELECT t.Col1
FROM #table AS t
WHERE t.someId = #id
) AS f;
Check If(DataTable.Rows.Count >0) check at your web programming level to avoid NullReferenceException. Based on the condition you can make the decision what to do with the further logic at web program.
It is always wise idea to handle such exception from programming level. Think of scenario where somebody else make changes in SQL query without knowing the effect of web usages of the code.

How to do a conditional JOIN with table valued parameter?

Okay so I have spent some time researching this but cannot seem to find a good solution.
I am currently creating a stored procedure that takes a set of optional parameters. The stored procedure will act as the "universal search query" for multiple tables and columns.
The stored procedure looks something like this (Keep in mind that this is just a stripped down version and the actual stored procedure has more columns etc.)
The '#ProductIdsParam IntList READONLY' is an example table valued parameter that I would like to JOIN if it is not empty. In other words, the query should only search by parameters that are not null/empty.
Calling the procedure and parsing the other parameters works just like it should. I might however have misunderstood and should not do a "universal search query" like this at all.
CREATE PROCEDURE [dbo].[usp_Search]
#ProductIdParam INT = NULL,
#CustomerNameParam NVARCHAR(100) = NULL,
#PriceParam decimal = NULL,
-- THIS IS WHAT I'D LIKE TO JOIN. BUT THE TABLE CAN BE EMPTY
#ProductIdsParam IntList READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT DISTINCT
CustomerTransactionTable.first_name AS FirstName,
CustomerTransactionTable.last_name AS LastName,
ProductTable.description AS ProductDescription,
ProductTable.price as ProductPrice
FROM dbo.customer AS CustomerTransactionTable
-- JOINS
LEFT JOIN dbo.product AS ProductTable
ON CustomerTransactionTable.product_id = ProductTable.id
WHERE
(ProductTable.id = #ProductIdParam OR #ProductIdParam IS NULL)
AND (CustomerTransactionTable.first_name = #CustomerNameParam OR #CustomerNameParam IS NULL)
AND (CustomerTransactionTable.price = #PriceParam OR #PriceParam IS NULL)
END
You can add the int table in LEFT join and then add a where condition based on the record count in the filter table. If #ProductIdsParam is declared as table, you should first count records in it and store the result in a varaible.
AND COALESCE(#ProductIdsParam.id, 0) = (CASE WHEN #ProductIdsCount = 0 THEN 0 ELSE ProductTable.id END)
In case #ProductIdsCount = 0 then you get always 0 = 0 so you get all the records, else you select only records where the productId in the filter table equals the ProductTable.id.
There are other (maybe cleaner) approaches possible though but I think this works.

Setting variable SQL Procedure

I am working on a database that contains customers, products, timesheets, etc for a store. The question I am working on involves creating a procedure that will change an "on/off" column to off (the product is available (1) by default, and this procedure turns it to 0) I have writen the procedure fine:
create proc p_fudgemart_deactivate_product
(
#product_id int
)
as
begin
update fudgemart_products
set product_is_active = 0
where product_id = #product_id
end
but the issue then comes when we are given a product NAME, and need to write a select statement to change that product to unavailable. I know that this requires the use of a variable, but I cannot figure out how to set the variable to the product id of that product. I was thinking something along the lines of:
Declare #prod_name_id int
set #prod_name_id= (select product_id from fudgemart_products
where product_name = 'Slot Screwdriver')
execute p_fudgemart_deactivate_product product_id #prod_name_id
Am I able to use a select in my variable declaration like this?
actually you're on the right track. try something like this:
declare #prod_name_id int
select #prod_name_id = product_id
from fudgemart_products
where product_name = 'Slot Screwdriver'
exec p_fudgemart_deactivate_product
#product_id = #prod_name_id
If you are using SQL Server 2008 or later, you can declare and assign in one statement:
DECLARE #prod_name_id int = ( SELECT product_id
FROM fudgemart_products
WHERE product_name = 'Slot Screwdriver'
);
EXECUTE p_fudgemart_deactivate_product #product_id = #prod_name_id;

Sql Server: Alternatives for conditional expressions in where condition

By writing client server applications typically one stumbles in cases where he needs to do a query that returns different records according to which is the logged in user.
I use this approach, but I wonder if there are alternatives, may you suggest?
(the reason I ask is that for more complx queries this becomes quite unreadable, while with some CASE statements it would be better).
declare #UserIsAdministrator bit;
declare #UserID integer;
set #UserIsAdministrator = 0 -- test value
set #UserID = 41; -- test value
SELECT * FROM employees
WHERE
(#UserIsAdministrator = 1) -- if user is admin return all records
OR
(
(#UserIsAdministrator = 0)
AND
(manager_ID = #UserID)
) -- if user is not admin return only "his" records
I think I would try to avoid embedding this type of authorization within the SQL queries altogether. Have separate queries, and resolve roles and privileges in a separate layer/component before you select which query to execute.
Options:
IF and ELSE
IF #UserIsAdministrator = 1
SELECT * FROM employees
ELSE
SELECT * FROM employees WHERE manager_ID = #UserID
Change parameters based on another parameter
IF #UserIsAdministrator = 1
SET #UserID = NULL
SELECT * FROM employees
WHERE manager_ID = ISNULL(#UserID, manager_ID)
Or different views/stored procs

Resources