Conditional where clause? - sql

I want to apply the conditional where clause That is if my barcode parameter comes null then i want to fetch all the records and if it comes with value then i want to fetch only matching records for the second part i am able to fetch the matching records but i am stuck at fetching the all records in case of null value i have tried as below ,
SELECT item
FROM tempTable
WHERE
((ISNULL(#barcode,0)=1)
// but this is not fetching all the records if barcode is null
OR
ISNULL(#barcode,0!= 1 AND tempTable.barcode LIKE #barcode+'%'))
//THis is working perfect
so any help will be great

I might have misunderstood what you ask, but the logic OR operator might help:
SELECT item
FROM tempTable
WHERE
#barcode IS NULL OR tempTable.barcode LIKE #barcode+'%'
If #barcode is NULL, it returns all the records, and when it is not NULL, it returns all of the records that fulfill the condition LIKE #barcode+'%'
Important
Also, bear in mind that using the OR operator can seemingly cause funny results when used with several complex conditions AND-ed together, and not enclosed properly in braces:
<A> AND <B> AND <C> OR <D> AND <E> AND <F>
Should most likely actually be formulated as:
(<A> AND <B> AND <C>) OR (<D> AND <E> AND <F>)
Remember, the parser does not know what you want to achieve, you have to describe your intents properly...

I think you could simplify it to:
SELECT item
FROM tempTable
WHERE #barcode IS NULL OR tempTable.barcode LIKE #barcode+'%'
so when #barcode is null you'll get everything - i.e. the Like part of the where won't need to execute. If #barcode has a value then the Like will be executed.

If the barcode field is non-null, then this is the method I would use -
SELECT item
FROM tempTable
WHERE barcode like isnull(#barcode, barcode) + '%'
If #barcode is null all records are returned and if it is non null then only matching records are returned.
If the barcode field is nullable then -
SELECT item
FROM tempTable
WHERE isnull(barcode, '') like isnull(#barcode, isnull(barcode, '')) + '%'
Same as the first but here we convert the null values in the barcode field to blank strings before doing the compare.

An alternate answer and an attempt at the bounty
declare #barcode nvarchar(10) -- chose nvarchar not necessarily should be nvarchar
select #barcode= NULL
--select #barcode='XYZ'
if #barcode is null
select item from temptable;
else
select item from temptable where temptable.barcode like #barcode+'%';

If I have to do this, I would have done like
SELECT item
FROM tempTable
WHERE
( ( ISNULL(#barcode,'') <> '') AND ( tempTable.barcode LIKE #barcode+'%' ) )
( ISNULL(#barcode,'') <> '') would also check if the variable is blank then it should not return anything. But if you just check for null, then in case when the #barcode is blank, you will be getting all item selected from the tempTable.

If column barcode would be non-nullable, you could greatly simplify the query.
This is based on the fact that the pattern '%' matches any string; even an empty (i.e. zero-length) string.
Consequently, the following WHERE clause matches all records:
WHERE barcode LIKE '%'
You may notice that this has a very close resemblance to the WHERE clause you are using to filter records on a specific barcode:
WHERE barcode LIKE #barcode + '%'
In fact, they are so similar that we may as well use a single WHERE clause for both cases; after all, '' + '%' equals '%'!
IF #barcode IS NULL SET #barcode = ''
SELECT item FROM tempTable WHERE barcode LIKE #barcode + '%'
There is an even shorter version, which preserves the original value of #barcode:
SELECT item FROM tempTable WHERE barcode LIKE ISNULL(#barcode, '') + '%'
As mentioned earlier, this works only if column barcode is non-nullable.
If column barcode is nullable (and you are genuinely interested in records where barcode IS NULL), then the following query might work for you:
SELECT item FROM tempTable
WHERE ISNULL(barcode, '') LIKE ISNULL(#barcode, '') + '%'
However, this version has two disadvantages:
It may perform much slower, because the query optimizer may not benefit from an index on column barcode.
If #barcode = '', then it will match not only the non-null barcodes, but also the records with barcode IS NULL; whether this is acceptable, is up to you.
One last simplification: you may want to reach consensus with the outside world that they should set #barcode = '' instead of NULL to retrieve all records. Then you could replace ISNULL(#barcode, '') by #barcode.

Apart of ppeterka's solution (which will causes an Index/Table Scan) there are at least three other solutions. These solutions could use an Index Seek if #barcode isn't NULL and, also, if there is an index on barcode column:
Solution #2: The execution plan isn't cached and reused:
SELECT item
FROM tempTable
WHERE #barcode IS NULL OR tempTable.barcode LIKE #barcode+'%'
OPTION(RECOMPILE);
Solution #3: The execution plan is cached (it can be used if the num. of optional parameters is small):
IF #barcode IS NULL
SELECT item
FROM tempTable;
ELSE
SELECT item
FROM tempTable
WHERE tempTable.barcode LIKE #barcode+'%';
Solution #4: The execution plans are cached (it can be used if the num. of optional parameters is high):
DECLARE #SqlStatement NVARCHAR(MAX);
SET #SqlStatement = N'
SELECT item
FROM tempTable
WHERE 1=1 '
+ CASE WHEN #barcode IS NULL THEN N'' ELSE N'AND tempTable.barcode LIKE #pBarcode+''%''; ' END;
-- + CASE WHEN #anotherparam IS NULL THEN N'' ELSE 'AND ...' END ;
EXEC sp_executesql #SqlStatement, N'#pBarcode VARCHAR(10)', #pBarcode = #barcode;
Note: Use the proper type and max. lenght/precision & scale for #pBarcode parameter.

IF #barcode is null
begin
SELECT item FROM tempTable
end
else
SELECT item FROM tempTable where tempTable.barcode LIKE #barcode+'%'

Related

In SQL Server How do I use "in" statement in a case condition?

declare #itemList nvarchar(100) = '43,78'
SELECT * FROM ItemMaster
WHERE ItemID = case when #itemList = '0' THEN ItemID ELSE IN (#itemList)
I want all the items from ItemMaster table when #itemList is 0 and when #itemList has a comma separated value, get only those ids. I tried to do this with the code above, but it is not working. How do I do this?
You should be passing the values using a table of some sort (probably a table variable). That would make this simpler. But you can do:
SELECT im.*
FROM ItemMaster
WHERE #ItemList = '' OR -- a much more reasonable value for "all"
im.ItemID IN (SELECT value FROM SPLIT_STRING(#itemList, ','));
In older versions of SQL Server, you can use:
SELECT im.*
FROM ItemMaster
WHERE #ItemList = '' OR -- a much more reasonable value for "all"
',' + #itemList + ',' LIKE '%,' + CONVERT(VARCHAR(255), im.ItemID) + ',%';
Don't. CASE is an expression that returns a Scalar value, not an expression. Using a CASE is the WHERE will effect the SARGability of your query to. Stick to using proper boolean Logic.
In this case, it seems like you're simply after:
WHERE #ItemList = 0
OR ItemID IN (SELECT item FROM STRING_SPLIT(#ItemList','));
Personally, however, I prefer passing NULL rather than 0 for the "catch all" parameter. Also, if this is a catch-all query, you may well want to add the RECOMPILE option to avoid poor plan caching:
WHERE #ItemList IS NULL
OR ItemID IN (SELECT item FROM STRING_SPLIT(#ItemList','))
OPTION (RECOMPILE);

When using CASE inside a WHERE clause, without ELSE, what is meant by NULL?

declare #Name varchar(45)
SELECT
*
FROM
table law
WHERE
--law.emp = case when isnumeric(#Name) = 1 then #Name else null end
law.emp = case when isnumeric(#Name) = 1 then #Name end
or coalesce(law.lname + ', ' + law.fname, '') like '%' + #Name + '%'
My question here is focusing on the CASE statement when it evaluates to something other than a numeric value...
If #Name is anything other than a numerical value, what is really happening? law.emp = null??? If so, it shouldn't return any results anyway, because the proper syntax is something like where law.emp is null, right?
So, just for clarification, my emp column doesn't actually contain any nulls.
I'm just trying to figure out what is really happening to my WHERE statement when #Name evaluates to something not numeric...
What actual evaluation is being done? Is SQL ignoring the condition?
That's kind of what I want: For SQL not to do anything with law.emp unless #Name is numeric. (I have other conditions that will use the #Name variable if it is text.)
The expression:
law.emp = NULL
always returns NULL. In a where clause, this is treated as false, and the row is not included in the result set.
I much prefer an explicit conversion:
law.emp = try_convert(int, #Name)
Or to whatever type emp is.

How can I search a SQL database with multiple "%" wildcards?

I am trying to write a SQL query in SQL Server 2008 R2 that will allow a user to search a database table by a number of parameters. The way this should work is, my user enters his criteria and the query looks for all close matches, while ignoring those criteria for which the user did not enter a value.
I've written my query using LIKE and parameters, like so:
select item
from [item]
where a like #a and b like #b and c like #c ...
where 'a', 'b', and 'c' are table columns, and my # parameters all default to '%' wildcards. This goes on for about twenty different columns, and that leads to my problem: if this query is entered as is, no input, just wildcards, it returns no results. Yet this table contains over 30,000 rows, so an all-wildcard query should return the whole table. Obviously I'm going about this the wrong way, but I don't know how to correct it.
I can't use 'contains' or 'freetext', as those look for whole words, and I need to match user input no matter where it occurs in the actual column value. I've tried breaking my query up into individual steps using 'intersect', but that doesn't change anything. Does anyone know a better way to do this?
To allow for null inputs, this is a good pattern:
select * from my table where ColA LIKE isnull(#a, ColA) AND ColB like isnull(#b, ColB)
This avoids having to construct and execute a dynamic SQL statement (and creating possible SQL injection issues.)
my # parameters all default to '%' wildcards
Don't do this. Default them to null. The way to disregard empty parameters is with a short circuit:
(#a IS NULL OR #a LIKE a)
Depending on how you want to handle missing data in the column, you might want a third term, because null will not match LIKE statements:
(#a IS NULL OR a IS NULL OR #a LIKE a)
How can I search a SQL database with multiple "%" wildcards?
Slowly. SQL is suboptimal for doing text comparisons. The best approach is to perform this search somewhere else, or at least structure your data to facilitate these kinds of queries. If you know you'll be performing a lot of these queries, consider redesigning your schema in the shape of a suffix tree. At an absolute bare minimum, do something so that every LIKE match is suffix-only, meaning LIKE 'xxx%' and never LIKE '%xxx' or LIKE 'x%x'. The latter two preclude the use of indexes. And put an index on every column you need to search.
Thanks for the guidance, all. It turns out that the table I'm querying can easily contain null values in the columns I'm searching against, so I expanded my query to say "where (a like #a or a is null) and ... " and it works now.
Personally, I'd do this in the application layer (assuming you have one), and build the query around the parameters the user supplies, eliminating the ones they don't.
For example, the following bit of code builds the query in SQL, where only the parameters the user has supplied (not null) are included in the where clause.
NOTE: This is very crude, as it doesn't take into account AND's if the first parameter if null, and it doesn't remove the WHERE clause if no parameters are supplied. If you let me know what language your application layer is built in, I'll provide a better example. (This is purely pseudo-code!)
DECLARE #a VARCHAR(100) = '''%SomeValue%''', #b VARCHAR(100)= '''%AnotherValue%''', #c VARCHAR(100)
DECLARE #SQL VARCHAR(MAX) = 'SELECT * FROM MyTable WHERE'
IF #a IS NOT NULL
BEGIN
SET #SQL += ' ColA LIKE ' + #a
END
IF #b IS NOT NULL
BEGIN
SET #SQL += ' AND ColA LIKE ' + #b
END
IF #c IS NOT NULL
BEGIN
SET #SQL += ' AND ColC LIKE ' + #c
END
PRINT #SQL
--EXEC(#SQL)
Output:
SELECT * FROM MyTable WHERE ColA = '%SomeValue%' AND ColB = '%AnotherValue%'

XQuery Comparison Expressions in SQL Column

I would like to have a table that I can store XQuery Comparison Expressions in, so that I can evaluate them in a query.
I've been doing a bit of R&D into if it is possible, and I'm struggling.
If I put an XQuery expression in a column, then it seems to evaluate differently to if I put the XQuery expression directly into the query. For example, when I run the below query:
declare
#x xml = ''
create table #condition
(
condition nvarchar(255)
)
insert into #condition
values
('''1''=''1''')
select
condition,
#x.query('sql:column("condition")'),
#x.query('''1''=''1''')
from #condition
I would expect this to return:
'1'='1', true, true
However it actually returns:
'1'='1', '1'='1', true
Does anybody know how I can evaluate comparison expressions that are stored in a column?
The eventual plan is to be able to use this technique to filter down rows of a table based on XQuery conditions present. So ultimately I'd want to be able to do this in the where clause of a select statement.
I've put the above example into an sql fiddle encase it is useful.
Many thanks
Short answer: Unfortunately you can't.
sql:column("condition") will be evaluated to a suitable XML primitive data type based on the table column type. In this case the value from condition column will always be evaluated as XML string type instead of an XQuery statement, as you have figured out from running your sample query. And I can't see anyway of evaluating dynamic XQuery statement, unless you want to construct the entire query dynamically and execute it later on possibly using sp_executesql.
Try this query:
declare
#x xml = ''
create table #condition
(
condition nvarchar(255)
)
insert into #condition
values
('''1''=''1''')
select
condition,
case when col1 like col2 then 'True' else 'False' END col,
quer
from
(
select
condition,
PARSENAME(REPLACE(condition,'=','.'),2) col1,
PARSENAME(REPLACE(condition,'=','.'),1) col2 ,
#x.query('''1''=''1''') as quer
from #condition
)base

sql server: MERGE has unexpected results

The way these rows usually come into the target table the first time are with a sparse number of columns populated with mostly text data with the remainder of the columns set to NULL. On subsequent passes, the fresh data populates existing known (non null) and unknown (NULL) data. I've ascertained that the fresh data ( #pld) do indeed contain different data. The data does not appear to change. Here's what I have:
BEGIN TRANSACTION
BEGIN TRY
MERGE INTO [metro].listings AS metroList
USING #pld as listnew
ON metroList.id = listnew.id
AND metroList.sid = listnew.sid
WHEN MATCHED AND (
metroList.User != listnew.User
or metroList.Email != listnew.Email
or metroList.LocName != listnew.LocName
) THEN
UPDATE SET
metroList.User = listnew.User,
metroList.Email = listnew.Email,
metroList.LocName = listnew.LocName,
WHEN NOT MATCHED THEN
INSERT
( User,
Email,
LocName
)
VALUES
(
listnew.User,
listnew.Email,
listnew.LocName
);
COMMIT TRANSACTION
END TRY
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH
I've tried replacing the != to under the update portion of the statement with <> . Same results. This has to be related to a comparison of a possible (likely) null value against a string--maybe even another null? Anyway, I'm calling on all sql-geeks to untangle this.
Also you can use option with NULLIF() function.
NULLIF returns the first expression if the two expressions are not equal. If the expressions are equal, NULLIF returns a null value of the type of the first expression.
WHEN MATCHED AND (
NULLIF(ISNULL(metroList.[User],''), listnew.[User]) IS NOT NULL
OR NULLIF(ISNULL(metroList.Email, ''), listnew.Email) IS NOT NULL
OR NULLIF(ISNULL(metroList.LocName, ''), listnew.LocName) IS NOT NULL
)
THEN
Comparing NULL with an empty string will not work.
If either side could be NULL, you could do something like:
WHEN MATCHED AND (
COALESCE(metroList.User, '') <> COALESCE(listnew.User, '')
or COALESCE(metroList.Email, '') <> COALESCE(listnew.Email, '')
or COALESCE(metroList.LocName, '') <> COALESCE(listnew.LocName, '')
) THEN
Of course, this assumes that you're fine with NULL meaning the same as an empty string (which may not be appropriate).
Take a look at this BOL article on NULL comparisons.
As I understand the question you are looking for an expression that emulates IS DISTINCT FROM.
The answer you have accepted is not correct then
WITH metroList([User])
AS (SELECT CAST(NULL AS VARCHAR(10))),
listnew([User])
AS (SELECT 'Foo')
SELECT *
FROM metroList
JOIN listnew
ON NULLIF(metroList.[User], listnew.[User]) IS NOT NULL
Returns zero rows. Despite the values under comparison being NULL and Foo.
I would use the technique from this article: Undocumented Query Plans: Equality Comparisons
WHEN MATCHED AND EXISTS (
SELECT metroList.[User], metroList.Email,metroList.LocName
EXCEPT
SELECT listnew.[User], listnew.Email,listnew.LocName
)