Set where clause basis parameter value in SQL Server - sql-server

I wish to set a condition while making use of the WHERE clause basis #USER_ID input recorded from the user in my SQL server query, but I guess I am missing something essential here.
DECLARE #USER_ID NVARCHAR(255) = 'a,b,c' -- will be multi-select parameter; can be null as well
select * from <table_name>
{
if: #USER_ID is NULL then no WHERE condition
else: WHERE <table_name>.<col_name> in (SELECT item FROM SplitString(#USER_ID,','))
}
Can someone please help here?

Personally, I would suggest switching to a table type parameter, then you don't need to use STRING_SPLIT at all. I'm not going to cover the creation of said type of parameter here though; there's plenty already out there and the documentation is more than enough to explain it too.
As for the problem, if we were to be using STRING_SPLIT you would use a method like below:
SELECT {Your Columns} --Don't use *
FROM dbo.YourTable YT
WHERE YT.YourColumn IN (SELECT SS.Value
FROM STRING_SPLIT(#USER_ID,',') SS)
OR #USER_ID IS NULL
OPTION (RECOMPILE);
The RECOMPILE in the OPTION clause is important, as a query where #USER_ID has the value NULL is likely to be very different to that where it isn't.
You could use a dynamic SQL approach, but for one parameter, I doubt there will more anything more than a negligible benefit. Using the above is much easier for others to understand as well, and the cost of generating the plan every time the query is run should be tiny for such a simple query.
Using a table type parameter, it would actually likely be more performant (assuming you have a useable index on YourColumn) to use a UNION ALL query like the below:
SELECT {Your Columns} --Don't use *
FROM dbo.YourTable YT
JOIN #Users U ON YT.YourColumn = U.UserId
UNION ALL
SELECT {Your Columns} --Don't use *
FROM dbo.YourTable YT
WHERE NOT EXISTS (SELECT 1 FROM #Users U);

You could avoid using STRING_SPLIT entirely here:
WHERE ',' + #USER_ID + ',' LIKE '%,' + <table_name>.<col_name> + ',%'

If the #UserID is NULL, then the condition (1=1) is executed, this is always true. If the #UserID is not NULL, then the OR condition is executed in which all <col_name> in (a, b, c) are present.
SELECT *
FROM <table_name>
WHERE (1=1)
AND ((#USER_ID IS NULL AND (1=1)) -- always true
OR (<table_name>.<col_name> in (SELECT [value] FROM SplitString(#USER_ID,',')))
)

Related

SQL Select value as (Subquery)

This is the query I am trying to get to work, but I'm getting a syntax error.
SELECT Column1 as (SELECT Value2 from Table2 Where ConditionA, ConditionB, ConditionC)
from Table1
I am self taught, and haven't found anything that says explicitly that this can't be done, but if that is the case then thanks for the closure.
You cannot use the value returned from a subquery for an alias, there is no way to make that work.
Your only option is to build the sql dynamically, and use sp_executesql, but thats usually an indication something else has gone wrong!
Start by getting the value you want, a couple of points
ensure you only get 1 result, either using TOP 1 or appropriate filter conditions
multiple conditions are separated with AND or OR, not a comma
declare #alias NVARCHAR(100) -- whatever is appropriate
SELECT TOP 1 #alias = value2 FROM Table2
WHERE someField = 'some value'
Then build up the sql dynamically, and execute it.
DECLARE #sql NVARCHAR(100) = 'SELECT Column1 AS ' + #alias + ' FROM Table1'
EXEC sp_executesql #sql
You can see this in action here: http://sqlfiddle.com/#!3/71f8d6/3
But, I stress, this is a bad solution.
try
SELECT (SELECT Value2 from Table2 Where ConditionA, ConditionB, ConditionC) Column1 from Table1

Issue with the cross apply statement and from

i'm triying to make something but i'm stuck on an issue. The problem is that i need to make a stored procedure where if a param of the procedure is the actual date or older, 2 params in a query and a part of the where would be ignored . I use initially an if else to ignore the cases but the code is repeated and inconsistent to maintain. So i need to make the same thing but without the if /else statement. any help would be apreciated.
select
Cats.*,
Zone.[D] as [De], -- this part would be avoided if #Min and #Max are null
Zone.[R] as [Re] -- this part would be avoided if #Min and #Max are null
from
flv
CROSS APPLY --all the cross apply would be avoided if #Min and #Max are null
(
select
x
from
XXXX
where
XXXX.IDx= 1
) as Data
where
---where clause
I just want to avoid the cross apply in case that the 2 params are null
Change XXXX.IDx= 1 into #Min IS NOT NULL AND #Max IS NOT NULL AND XXXX.IDx= 1 and your cross apply won't hurt a bit. However it would mean no rows will be returned from the CROSS APPLY and thus the whole query. So you'll als need the CROSS APPLY into an OUTER APPLY
For example the following script:
DECLARE #test INT
SELECT * FROM dbo.tmpTable AS TT
OUTER APPLY (
SELECT * FROM dbo.test AS T
WHERE #test IS NOT NULL
) AS T
Gives the following execution plan, pay special attention the the Filter component here.
When you hover the filter component you may notice the following
Meaning dbo.test in this case would only be scanned if the startup predicate is met. Put OPTION (RECOMPILE) on you query and look at the actual query plan and the table is even not even in the query plan if #test is null.

Is there such thing as a conditional operator in SQL Server?

I have the below SQL code snippet and I want to select sales from all customers by manipulating the value of the #Customer parameter. Is there any way to do this in SQL Server 2008 R2? I've tried setting #Customer = '%' but obviously that didn't work since there is no LIKE operator.
I only want to manipulate the parameter because there will be other times where I will
need to select only a single customer. Is there such a thing as using an IF/ELSE in the WHERE clause?
DECLARE #Customer varchar(5) = ''
SELECT *
FROM SalesData
WHERE Customer=#Customer
Is there such a thing as using an IF/ELSE in the WHERE clause
Absolutely, there is a CASE expression - not only in the WHERE clause, but also in other parts of your query. However, a more common approach is to use logical expressions in the WHERE clause that force SQL Server to take one condition or the other depending on the parameter setting.
my condition would be if #Customer = '' then select all
If you would like to select all customers when the parameter is set to empty, or select all customers where the parameter is not set, you can do this:
SELECT *
FROM SalesData
WHERE #Customer = ''
OR Customer = #Customer
If #Customer is set to '', the first clause of the OR expression will be TRUE for all rows, so all customers would be returned. However, when #Customer is non-empty, the second part of the expression would be evaluated.
Not quite sure how you need to perform search
But can try something like below
SELECT *
FROM SalesData
WHERE Customer like '%' + #Customer + '%'
Or
SELECT *
FROM SalesData
WHERE (LEN(#Customer)>0 AND Customer =#Customer)
OR (LEN(#Customer)=0)
In here I havent trim assuming you always pass ''. But you can always use LEN(LTRIM(RTRIM(#Customer)))
If you default #Customer to NULL rather than '' then you could just have
WHERE Customer = ISNULL(#Customer, Customer)

Use of the IN condition

I can easily create a stored procedure in SQL Server with parameters that I use with =, LIKE and most operators. But when it comes to using IN, I don't really understand what to do, and I can't find a good site to teach me.
Example
CREATE PROCEDURE TEST
#Ids --- What type should go here?
AS BEGIN
SELECT * FROM TableA WHERE ID IN ( #Ids )
END
Is this possible and if so how ?
With SQL Server 2008 and above, you can use Table Valued Parameters.
You declare a table type and can use that as a parameter (read only) for stored procedures that can be used in IN clauses.
For the different options, I suggest reading the relevant article for your version of the excellent Arrays and Lists in SQL Server, by Erland Sommarskog.
I've done this in the past using a Split function that I add to my schema functions as described here
Then you can do the following:
CREATE PROCEDURE TEST
#Ids --- What type should go here?
AS BEGIN
SELECT * FROM TableA WHERE ID IN ( dbo.Split(#Ids, ',') )
END
Just remember that the IN function always expects a table of values as a result. SQL Server is smart enough to convert strings to this table format, so long as they are specifically written in the procedure.
Another option in your specific example though, could be to use a join. This will have a performance improvement, but often does not really meet a real-world example you need. The join version would be:
SELECT *
FROM TableA AS ta
INNER JOIN dbo.Split(#Ids, ',') AS ids
ON ta.Id = ids.items
If your asking what I think your asking, I do this every day..
WITH myData(FileNames)
AS
(
SELECT '0608751970%'
UNION ALL SELECT '1000098846%'
UNION ALL SELECT '1000101277%'
UNION ALL SELECT '1000108488%'
)
SELECT DISTINCT f.*
FROM tblFiles (nolock) f
INNER JOIN myData md
ON b.FileNames LIKE md.FileNames
Or if your doing this based on another table:
WITH myData(FileNames)
AS
(
SELECT RTRIM(FileNames) + '%'
FROM tblOldFiles
WHERE Active=1
)
SELECT DISTINCT f.*
FROM tblFiles (nolock) f
INNER JOIN myData md
ON b.FileNames LIKE md.FileNames

Is there a way to optimize the query given below

I have the following Query and i need the query to fetch data from SomeTable based on the filter criteria present in the Someothertable. If there is nothing present in SomeOtherTable Query should return me all the data present in SomeTable
SQL SERVER 2005
SomeOtherTable does not have any indexes or any constraint all fields are char(50)
The Following Query work fine for my requirements but it causes performance problems when i have lots of parameters.
Due to some requirement of Client, We have to keep all the Where clause data in SomeOtherTable. depending on subid data will be joined with one of the columns in SomeTable.
For example the Query can can be
SELECT
*
FROM
SomeTable
WHERE
1=1
AND
(
SomeTable.ID in (SELECT DISTINCT ID FROM SomeOtherTable WHERE Name = 'ABC' and subid = 'EF')
OR
0=(SELECT Count(1) FROM SomeOtherTable WHERE spName = 'ABC' and subid = 'EF')
)
AND
(
SomeTable.date =(SELECT date FROM SomeOtherTable WHERE Name = 'ABC' and subid = 'Date')
OR
0=(SELECT Count(1) FROM SomeOtherTable WHERE spName = 'ABC' and subid = 'Date')
)
EDIT----------------------------------------------
I think i might have to explain my problem in detail:
We have developed an ASP.net application that is used to invoke parametrize crystal reports, parameters to the crystal reports are not passed using the default crystal reports method.
In ASP.net application we have created wizards which are used to pass the parameters to the Reports, These parameters are not directly consumed by the crystal report but are consumed by the Query embedded inside the crystal report or the Stored procedure used in the Crystal report.
This is achieved using a table (SomeOtherTable) which holds parameter data as long as report is running after which the data is deleted, as such we can assume that SomeOtherTable has max 2 to 3 rows at any given point of time.
So if we look at the above query initial part of the Query can be assumed as the Report Query and the where clause is used to get the user input from the SomeOtherTable table.
So i don't think it will be useful to create indexes etc (May be i am wrong).
SomeOtherTable does not have any
indexes or any constraint all fields
are char(50)
Well, there's your problem. There's nothing you can do to a query like this which will improve its performance if you create it like this.
You need a proper primary or other candidate key designated on all of your tables. That is to say, you need at least ONE unique index on the table. You can do this by designating one or more fields as the PK, or you can add a UNIQUE constraint or index.
You need to define your fields properly. Does the field store integers? Well then, an INT field may just be a better bet than a CHAR(50).
You can't "optimize" a query that is based on an unsound schema.
Try:
SELECT
*
FROM
SomeTable
LEFT JOIN SomeOtherTable ON SomeTable.ID=SomeOtherTable.ID AND Name = 'ABC'
WHERE
1=1
AND
(
SomeOtherTable.ID IS NOT NULL
OR
0=(SELECT Count(1) FROM SomeOtherTable WHERE spName = 'ABC')
)
also put 'with (nolock)' after each table name to improve performance
The following might speed you up
SELECT *
FROM SomeTable
WHERE
SomeTable.ID in
(SELECT DISTINCT ID FROM SomeOtherTable Where Name = 'ABC')
UNION
SELECT *
FROM SomeTable
Where
NOT EXISTS (Select spName From SomeOtherTable Where spName = 'ABC')
The UNION will effectivly split this into two simpler queries which can be optiomised separately (depends very much on DBMS, table size etc whether this will actually improve performance -- but its always worth a try).
The "EXISTS" key word is more efficient than the "SELECT COUNT(1)" as it will return true as soon as the first row is encountered.
Or check if the value exists in db first
And you can remove the distinct keyword in your query, it is useless here.
if EXISTS (Select spName From SomeOtherTable Where spName = 'ABC')
begin
SELECT *
FROM SomeTable
WHERE
SomeTable.ID in
(SELECT ID FROM SomeOtherTable Where Name = 'ABC')
end
else
begin
SELECT *
FROM SomeTable
end
Aloha
Try
select t.* from SomeTable t
left outer join SomeOtherTable o
on t.id = o.id
where (not exists (select id from SomeOtherTable where spname = 'adbc')
OR spname = 'adbc')
-Edoode
change all your select statements in the where part to inner jons.
the OR conditions should be union all-ed.
also make sure your indexing is ok.
sometimes it pays to have an intermediate table for temp results to which you can join to.
It seems to me that there is no need for the "1=1 AND" in your query. 1=1 will always evaluate to be true, leaving the software to evaluate the next part... why not just skip the 1=1 and evaluate the juicy part?
I am going to stick to my original Query.

Resources