Stored procedure filtering - sql

Need to write a procedure to retrieve data in which some selection criteria were not taken into procedure if a user not enters a value. I am filtering :
WHERE #ts = [ts] or #username = [username]
or #ip = [ip] or #my_category = [my_category]
or #my_name = [my_name] or #nm1_name = [nm1_name]
or #param = [param] or #short_descr = [short_descr]
When I call a stored procedure and when passed one of the parameters that filtering works, and when I write some ,only one filtering work (because I have in my logic 'or'). Replaced by 'and' not satisfied unnecessarily have to pass all the parameters, and I need to transfer only those on which I want to filter out. Please help me to solve this problem

WHERE (#ts IS NULL OR [ts] = #ts)
and (#username IS NULL OR [username] = #username)
and (#ip IS NULL OR [ip] = #ip)
and (#my_category IS NULL OR [my_category] = #my_category)
and (#my_name IS NULL OR [my_name] = #my_name)
and (#nm1_name IS NULL OR [nm1_name] = #nm1_name)
and (#param IS NULL OR [param] = #param)
and (#short_descr IS NULL OR [short_descr] = #short_descr)
EDIT:
I've seen in comment to other answer that you are passing blank string instead of NULLs. In that case you need to handle them instead (or in addition) to NULLs
WHERE (#ts IS NULL OR #ts = '' OR [ts] = #ts)
and (#username IS NULL OR #username = '' OR [username] = #username)
and (#ip IS NULL OR #ip = '' OR [ip] = #ip)
and (#my_category IS NULL OR #my_category = '' OR [my_category] = #my_category)
and (#my_name IS NULL OR #my_name = '' OR [my_name] = #my_name)
and (#nm1_name IS NULL OR #nm1_name = '' OR [nm1_name] = #nm1_name)
and (#param IS NULL OR #param = '' OR [param] = #param)
and (#short_descr IS NULL OR #short_descr = '' OR [short_descr] = #short_descr)

You're setting your parameters to NULL when you're not using the related filter, right?
So you could use a query like this:
WHERE isnull(#ts, [ts]) = [ts]
and isnull(#username, [username]) = [username]
and isnull(#ip, [ip]) = [ip]
and isnull(#my_category, [my_category]) = [my_category]
and isnull(#my_name, [my_name]) = [my_name]
and isnull(#nm1_name, [nm1_name]) = [nm1_name]
and isnull(#param, [param]) = [param]
and isnull(#short_descr, [short_descr]) = [short_descr]
option (recompile)
I advocate using option (recompile) in such a scenario
EDIT:
with '' instead of NULL
WHERE #ts in ('', [ts])
and #username in ('', [username])
and #ip in ('', [ip])
and #my_category in ('', [my_category])
and #my_name in ('', [my_name])
and #nm1_name in ('', [nm1_name])
and #param in ('', [param])
and #short_descr in ('', [short_descr])
(more readable than using OR each and every time and it doesn't require recompile*)
(*) as parameters will not be sniffed anyway

Related

SQL Server Dynamic where clause query

I am trying to create a dynamic query I just to do with linq
from a in Customers
where (string.IsNullOrEmpty(name)? true : a.FirstName == name) && (string.IsNullOrEmpty(last)? true : a.LastName == last)
select a;
but now I need to do in on Stored Procedure and I don't want to concatenate for security reason and performance. The most close example I found is this query
declare #Name as varchar(100)
declare #GroupName as varchar(100)
set #Name = ''
set #GroupName = 'Manufacturing'
SELECT TOP (1000) [DepartmentID]
,[Name]
,[GroupName]
,[ModifiedDate]
FROM [AdventureWorks2017].[HumanResources].[Department]
where ([Name] = case
when #Name is null or #Name = '' then null
else #Name
end
)
and
(
[GroupName] = case
when #GroupName is null or #GroupName = '' then null
else #GroupName
end
)
This almost works. I think this should be the answer but the where clause fails for obvious reason.
I would like the where clause could produce '1=1' if the param "Name" or "GroupName" is null
example
#Name = "Somename"
#GroupName = null
where (Name = #Name) and (1 = 1)
--or
#Name = null
#GroupName = "Somegruopname"
where (1 = 1) and (GroupName = #GroupName)
--
#Name = null
#GroupName = null
where (1 = 1) and (1 = 1)
You want it to succeed if the variable is null or empty or a match, so I would just write that in your stored procedure.
WHERE (#FirstName is null OR #FirstName = '' OR [FirstName] = #FirstName)
AND (#LastName is null OR #LastName = '' OR [LastName] = #LastName)
Please try this:
declare #Name as varchar(100)
declare #GroupName as varchar(100) = 'Manufacturing'
set #Name = LTRIM(RTRIM(#Name))
set #GroupName = 'Manufacturing'
SELECT TOP (1000) [DepartmentID]
,[Name]
,[GroupName]
,[ModifiedDate]
FROM [AdventureWorks2017].[HumanResources].[Department]
where ([Name] = coalesce(#Name,'') = '' OR [Name] = #Name)
and
([GroupName] = coalesce(#GroupName, '') = '' OR [GroupName] = #GroupName)

Apply IF conditional at the end of the query

I'm new in sql server and I have WHERE clause like this:
WHERE[D].[IsLocked] = 0
AND(#StartDate IS NULL OR ISNULL([TA].[ModifiedDate], [TA].[CreationDate]) >= #StartDate)
AND(#EndDate IS NULL OR ISNULL([TA].[ModifiedDate], [TA].[CreationDate]) <= #EndDate)
AND((CASE WHEN[T].[TaskStatusId] = '09E02513-00AD-49E3-B442-A9ED2833FB25'
THEN 1 ELSE 0 END) = #Completed)
AND((#FilterEmpKey IS NULL AND[TA].[EmpKey] = #CurrentEmpKey)
OR (ISNULL([TA].[ModifiedAssignedBy], [TA].[AssignatedBy]) = #FilterEmpKey
AND[TA].[EmpKey] = #CurrentEmpKey))
But now I want to add if conditional in order to add more filters at the end of query like:
IF(#FilterEmpGuid IS NOT NULL)
AND[TA].[EmpKey] = #CurrentEmpKey
AND[TA].[AssignatedBy] = #CurrentEmpKey
AND[TA].[EmpKey] = #FilterEmpKey
But I get:
The multi-part identifier [TA].[EmpKey] could not be bound
What am I doing wrong?
IF conditionals are only for use outside sql queries, such as in procedures etc.
In a query itself you are limited to AND, OR and CASE statements, so you will need to rewrite your IF conditional for this:
AND (#FilterEmpGuid IS NULL
OR (
[TA].[EmpKey] = #CurrentEmpKey
AND[TA].[AssignatedBy] = #CurrentEmpKey
AND[TA].[EmpKey] = #FilterEmpKey
))
You could move the additional filter options into a scalar function.
If you know the additional fields that may be filtered, you may be able to get away with something like:
CREATE FUNCTION dbo.ExtendFilter(
#column_value VARCHAR(50), #param_value VARCHAR(50)
)
RETURNS BIT
AS
BEGIN
DECLARE #return BIT = 1; -- default RETURN to 1 ( include ).
IF ( NULLIF( #param_value, '' ) IS NOT NULL )
BEGIN
-- compare the column's value to the param value
IF ( #column_value <> #param_value )
SET #return = 0; -- don't include this record.
END
RETURN #return;
END
GO
And then use it like:
WHERE
{ other WHERE stuff }
AND dbo.ExtendFilter( [TA].[EmpKey], #CurrentEmpKey ) = 1
AND dbo.ExtendFilter( [TA].[AssignatedBy], #CurrentEmpKey ) = 1
AND dbo.ExtendFilter( [TA].[EmpKey], #FilterEmpKey ) = 1
Mind you this is just an example. You'd want to check #pram_value for NULL, etc...

Using COALESCE in Sql server

I have a stored procedure which will update the album details if a album id is present in the table , if not it will add a new record.
User some times can update all values, or some times leaves some values. So in case if the user leave the values for updating old data should be kept in table.
For this i found solution to make use of coasesce in sql...
I wrote my SP as:
UPDATE [dbo].[tbl_M_Album]
SET [AlbumName] = #AlbumName
,[ImageName] = #Imagename
,[Description] = coalesce(#Description,[Description])
,[Imagepath] = #Imagepath
,[UpdatedBy] = #CreatedBy
,[UpdatedDate] = #CreatedDate
where AlbumID =#AlbumId
end
If i did not send the Description the old data is not keeping, it is overriding by empty data.
Please some one help me if i have any mistakes..
The value of #Description you pass is blank or empty, not NULL.
COALESCE and other null checking functions treat '' as a non null value.
These are all true,
NULL IS NULL
'' IS NOT NULL
' ' IS NOT NULL
If you want to check for NULL, '' (empty) or ' ' (whitespace), you could use
COALESCE(LEN(TRIM(#Description)), 0) = 0
or, for just NULL and empty,
COALESCE(LEN(#Description), 0) = 0
but, it would be more efficient to avoid passing empty or blank values.
You could rewrite your SP like this
IF COALESCE(LEN(TRIM(#Description)), 0) = 0
UPDATE [dbo].[tbl_M_Album]
SET
[AlbumName] = #AlbumName
,[ImageName] = #Imagename
,[Imagepath] = #Imagepath
,[UpdatedBy] = #CreatedBy
,[UpdatedDate] = #CreatedDate
WHERE
[AlbumID] = #AlbumId;
ELSE
UPDATE [dbo].[tbl_M_Album]
SET
[AlbumName] = #AlbumName
,[ImageName] = #Imagename
,[Description] = #Description
,[Imagepath] = #Imagepath
,[UpdatedBy] = #CreatedBy
,[UpdatedDate] = #CreatedDate
WHERE
[AlbumID] = #AlbumID;
Check if the value assigned for #Description is an empty string or NULL. If you pass an empty string in #Description then that is the problem. Only passing NULL will "default" to the next non-null value (this applies to both COALESCE or ISNULL).
Hope this helps.
Use this query.
UPDATE [dbo].[tbl_M_Album]
SET [AlbumName] = #AlbumName
,[ImageName] = #Imagename
,[Description] = Case WHEN (#Description IS NULL OR LTRIM(RTRIM(#Description) = '')
THEN [Description]
ELSE #Description
,[Imagepath] = #Imagepath
,[UpdatedBy] = #CreatedBy
,[UpdatedDate] = #CreatedDate
where AlbumID =#AlbumId
end

Stored procedure (SQL Server), can't find the issue

This stored procedure is use for searching records.
When I only fill in the parameter #ID or #FirstName, it works. But not if I only fill in #LastName.
For example:
#ID = 1, the rest is NULL --> should give 1 row --> RESULT: 1 row (ok)
#ID = NULL, #FirstName = 'Tim', the rest is NULL --> should give 1 row --> RESULT: 1 row (ok)
#ID = NULL, #FirstName = NULL, #LastName = 'BlaBla', the rest is NULL
--> should give 1 row --> RESULT: all rows (Not OK)
Anyone know why?
Thanks in advance.
This is my procedure:
ALTER PROCEDURE lookupSubscriber
-- Add the parameters for the stored procedure here
#ID int,
#firstname nvarchar(50),
#lastname nvarchar(60),
#street nvarchar(80),
#housenumber nvarchar(6),
#companyname nvarchar(50),
#city nvarchar(50),
#ResultString nvarchar(80) OUTPUT,
#ResultValue int OUTPUT,
#ResultCount int OUTPUT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
--Replacing empty strings with NULL
IF #ID = 0 BEGIN SET #ID = NULL; END
IF #firstname = '' BEGIN SET #firstname = NULL; END
IF #lastname = '' BEGIN SET #lastname = NULL; END
IF #street = '' BEGIN SET #street = NULL; END
IF #companyname = '' BEGIN SET #companyname = NULL; END
IF #housenumber = '' BEGIN SET #housenumber = NULL; END
IF #city = '' BEGIN SET #city = NULL; END
-- Insert statements for procedure here
BEGIN TRY
SELECT s.ID, COALESCE(d.FirstName,'NONE'), COALESCE(d.LastName,'NONE'), d.Street, COALESCE(d.CompanyName,'NONE'), d.HouseNumber, c.name
FROM Subscriber s
INNER JOIN SubscriberDetail d ON d.ID = s.Detail_ID
INNER JOIN City c ON d.City_ID = c.ID
WHERE (s.ID = COALESCE(#ID, s.ID)
AND d.FirstName = COALESCE(#firstname, d.FirstName) OR d.FirstName = 'NONE'
AND d.LastName = COALESCE(#lastname, d.LastName) OR d.LastName = 'NONE'
AND d.Street = COALESCE(#street, d.Street)
AND d.CompanyName = COALESCE(#companyname, d.CompanyName) OR d.CompanyName = 'NONE'
AND d.HouseNumber = COALESCE(#housenumber, d.HouseNumber)
AND c.name = COALESCE(#city, c.name))
SET #ResultCount = ##ROWCOUNT
SET #ResultString = 'Lookup successful'
SET #ResultValue = 0
END TRY
BEGIN CATCH
SET #ResultString = 'ERROR: ' + ERROR_MESSAGE()
SET #ResultValue = 2
END CATCH
END
GO
Example data:
Subscriber:
ID = 1 | Type_ID = 1 | Detail_ID = 2
ID = 2 | Type_ID = 2 | Detail_ID = 3
SubscriberDetail:
ID = 1 | FirstName = 'Laurens' | LastName = 'De Neys' | CompanyName = NULL | Street = 'Ergens' | HouseNumber = 2 | City_ID = 1
ID = 2 | FirstName = 'Tim' | LastName = 'Blabla' | CompanyName = NULL | Street = 'Iets' | HouseNumber = 26 | City_ID = 2
City:
ID = 1 | name = 'Liedekerke' | postalCode = 1770
ID = 1 | name = 'Leuven' | postalCode = 3000
Perhaps you need to put your OR conditions in parentheses with your original conditions?
For all of these expressions:
AND d.LastName = COALESCE(#lastname, d.LastName) OR d.LastName = 'NONE'
Change to
AND (d.LastName = COALESCE(#lastname, d.LastName) OR d.LastName = 'NONE')
EDIT
Well, I can't know what is in your DB, but I have a feeling some of that logic is incorrect. Try this:
SELECT s.ID, COALESCE(d.FirstName,'NONE'), COALESCE(d.LastName,'NONE'), d.Street, COALESCE(d.CompanyName,'NONE'), d.HouseNumber, c.name
FROM Subscriber s
INNER JOIN SubscriberDetail d ON d.ID = s.Detail_ID
INNER JOIN City c ON d.City_ID = c.ID
WHERE (#id is null or s.ID = #ID)
AND (#firstname is null or d.FirstName = #firstname)
AND (#lastname is null or d.LastName = #lastname)
AND (#street is null or d.Street = #street)
AND (#companyname is null or d.CompanyName = #companyname)
AND (#housenumber is null or d.HouseNumber = #housenumber)
AND (#city is null or c.name = #city)
Your WHERE statement has its AND and OR conditions arranged in such a way that they are evaluating incorrectly for what I assume you want them to do. You can fix it by encapsulating your OR statments to account for the correct parts. Example:
WHERE (s.ID = COALESCE(#ID, s.ID)
AND (d.FirstName = COALESCE(#firstname, d.FirstName) OR d.FirstName = 'NONE')
AND (d.LastName = COALESCE(#lastname, d.LastName) OR d.LastName = 'NONE')
AND d.Street = COALESCE(#street, d.Street)
AND (d.CompanyName = COALESCE(#companyname, d.CompanyName) OR d.CompanyName = 'NONE')
AND d.HouseNumber = COALESCE(#housenumber, d.HouseNumber)
AND c.name = COALESCE(#city, c.name))
EDIT: Looking at the same data, there's an additional problem here. You're trying to check equality on Company name to COALESCE(#companyname, d.companyname), but you have cases where your CompanyName is NULL. When a value is NULL, it's unknown, so SQL won't treat it as being equal, even to itself.
This is one reason I generally prefer, rather than the COALESCE syntax above to do somethng like this:
AND (#companyname IS NULL OR d.CompanyName = #companyname)
The above doesn't care if the stored value for company name is null if the parameter is also null (and if the parameter isn't null, your equality still works out).

FireBird: How do you nest a select in an if?

I'm very new to FireBird, but I want to know how I can use a select statement as part of my conditional criteria. I feel like I've been to the internet in back trying to find a way to do this, but haven't come up with much. Below is my attempt at getting this to work. Thanks in advance for any help.
SET TERM ^ ;
ALTER PROCEDURE sp_test (
IPADD Varchar(32),
HN Varchar(32),
NOTE Varchar(200) )
RETURNS ( update_count integer )
AS
BEGIN
IF((SELECT COUNT(*)
FROM ADDRESSES a
WHERE a.ADDRESS_TYPE = 'Reserved'
AND a.ALIVE = 'N'
AND (a.HOST_NAME = '' OR a.HOST_NAME is NULL)
AND (a.DNS_NAME = '' OR a.DNS_NAME is NULL)
AND (a.SYSTEM_NAME = '' OR a.SYSTEM_NAME is NULL)) > 0)
THEN
UPDATE
ADDRESSES a
SET
a.HOST_NAME = :HN,
a.ADDRESS_TYPE = 'Assigned',
a.NOTES = :NOTE
WHERE
a.SHORT_IP_ADDRESS = :IPADD;
update_count = 1;
SUSPEND;
ELSE
update_count = 0;
SUSPEND;
END^
SET TERM ; ^
GRANT EXECUTE
ON PROCEDURE sp_test TO SYSDBA;
Using COUNT to check is there records to update is not the best way, use EXISTS instead, ie your IF would be
IF(EXISTS(SELECT 1 FROM ADDRESSES a
WHERE a.ADDRESS_TYPE = 'Reserved'
AND a.ALIVE = 'N'
AND (a.HOST_NAME = '' OR a.HOST_NAME is NULL)
AND (a.DNS_NAME = '' OR a.DNS_NAME is NULL)
AND (a.SYSTEM_NAME = '' OR a.SYSTEM_NAME is NULL)))
THEN
But there seems to be a problem with your return value, update_count - you return 1 if you execute the UPDATE, but the actual number of rows affected by the statement might be something else. I suggest you use ROW_COUNT context variable instead. So your procedure would be
ALTER PROCEDURE sp_test (
IPADD Varchar(32),
HN Varchar(32),
NOTE Varchar(200) )
RETURNS ( update_count integer )
AS
BEGIN
IF(EXISTS(SELECT 1 FROM ADDRESSES a
WHERE (a.ADDRESS_TYPE = 'Reserved')
AND (a.ALIVE = 'N')
AND (a.HOST_NAME = '' OR a.HOST_NAME is NULL)
AND (a.DNS_NAME = '' OR a.DNS_NAME is NULL)
AND (a.SYSTEM_NAME = '' OR a.SYSTEM_NAME is NULL)))
THEN BEGIN
UPDATE ADDRESSES a SET
a.HOST_NAME = :HN,
a.ADDRESS_TYPE = 'Assigned',
a.NOTES = :NOTE
WHERE a.SHORT_IP_ADDRESS = :IPADD;
update_count = ROW_COUNT;
END ELSE update_count = 0;
SUSPEND;
END^