SQL Server OR statement with AND - sql

I have a query like this
select *
from MyTable
where (#Param is null or (FieldCode = 'Whatever' and FieldValue like '%' + #Param + '%'))
As I'm adding more statements to my where clause, my query is slowing down. I think it's because it's evaluating the AND part before the OR (which contains a LIKE, which can be slow). I want it to skip the query with the AND if #Param is null, how should i do this?

I would highly suggest you check your indexes you are definitely missing one then.
Beside of that do you really need to use every column? Use (NOLOCK) for reading data.
Furthermore like is very bad for indexes look if you would really need the first % for your program or if you could search for the data on the clientside (Filter a larger subset of data)

You could always break it apart and just do a different select statement if the #param is null.
If #Param is not null
select *
from MyTable
where (FieldCode = 'Whatever' and FieldValue like '%' + #Param + '%')
Else
select *
from MyTable

Try this for better performance:
select *
from MyTable
where #Param is null or case when #Param is not null and FieldCode = 'Whatever'
then case when FieldValue like '%' + #Param + '%' then 1 end end = 1
This will prevent the heavy wildchar compare when it isn't necessary

Related

LIKE clause applied to a DB Type in Stored Procedure

I have created the following Type:
CREATE TYPE dbo.expressions
AS TABLE
(
expression varchar(255)
);
GO
How can I, in a Stored Procedure, use the n types that I receive and do, with each one, an select with a like clause?
In terms of "programming" (and pseudo-code), would be something like this:
for each expression in expressions
rows+=select * from table where table.field LIKE '%' + expression[i] + '%'
I can always call the SP multiple times from my API but I was wondering if this is possible, and even faster, to do in SQL side.
You simply SELECT from / JOIN with an instance of the type. Assuming you have something like:
CREATE TYPE expressions AS TABLE (expression varchar(255));
CREATE PROCEDURE mysp(#expressions AS expressions) ...
You can use the variable like you would use a table:
SELECT *
FROM #expressions AS expressions
INNER JOIN yourtable ON yourtable.field LIKE '%' + expressions.expression + '%'
The above will allow you to use expression in select clause. Otherwise you can use the following:
SELECT *
FROM yourtable
WHERE EXISTS (
SELECT 1
FROM #expressions AS expressions
WHERE yourtable.field LIKE '%' + expressions.expression + '%'
)

Query filter delphi firedac Firebird database

I´m migrating a database from SQLITE to Firebird, but now my query doesn't work.
What am I doing wrong? Is there a reason?
frmDados.Clientes.Close();
frmDados.Clientes.SQL.Text :=
'SELECT * FROM CLIENTES ' +
'WHERE (nomecliente like :d1) '+
'order by nomecliente asc';
frmDados.Clientes.Params.ParamByName('d1').AsString := '%' + Edit1.text + '%';
frmDados.Clientes.OpenOrExecute();
Firebird dose not support case insensitive queries.
Query_Case_Insensitive.html
Consider these options
select * from "abc_table" where "Some_Field" = 'Abc'
select * from "abc_table" where "Some_Field" like 'Abc'
select * from "abc_table" where "Some_Field" containing 'Abc'
select * from "abc_table" where upper("Some_Field") = 'ABC'
Equals (=) and *like * both perform case sensitive matches
*containing * is case insensitive, but will also match 'abcd'
upper() works, but will not use an index and, therefore, will read every record in the table
Equals (=) is the fastest because it uses an index (if available)

Conditional where clause?

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+'%'

SQL Search Query With Null and ''

I have a query that I'm building for an application. The database is setup in SQL Server 2008. I want to use a query similar to below, however, I will be using this 'Where' clause for about 4 other columns using the same requirements. Is this the appropriate way to test for null or '' in a column that is VarChar(255) and does allow nulls.
Ideally, if the variable #UutSerialNumber is null or empty (''), I want all the results, but if it is not, I want to use the 'LIKE' clause. Is this the proper way of doing this and will it work? It seems to work until I start adding more columns to the Where clause.
Also, how would I handle a "text" datatype using the same type of query?
SELECT DeviceName, UutStatus
FROM MyTable
WHERE (UutSerialNumber LIKE '%' + #UutSerialNumber + '%' OR UutSerialNumber LIKE '%%' AND (#UutSerialNumber = '' OR #UutSerialNumber IS NULL)) AND ...
Help is appreciated. Thanks everyone!
It amy seem like duplication of SQL but the best way to do this is in terms of performace is using IF ... ELSE
IF ISNULL(#UutSerialNumber, '') = ''
BEGIN
SELECT DeviceName, UutStatus
FROM MyTable
-- MORE EXPRESSIONS
END
ELSE
BEGIN
SELECT DeviceName, UutStatus
FROM MyTable
WHERE (UutSerialNumber LIKE '%' + #UutSerialNumber + '%'
-- MORE EXPRESSIONS
END
It can be done within the WHERE clause if you are doing it on multiple columns and the query you posted wasn't far off it was just missing additional parenthesis along with having a redundant clause.
SELECT DeviceName, UutStatus
FROM MyTable
WHERE (ISNULL(#UutSerialNumber, '') = '' OR UutSerialNumber LIKE '%' + #UutSerialNumber + '%')
AND (ISNULL(#AnotherParameter, '') = '' OR AnotherColumn LIKE '%' + #AnotherParameter + '%')
Convert the text type to VARCHAR(MAX).
as a footnote, I personally would use the CHARINDEX rather than concatenating strings in the like:
WHERE (ISNULL(#UutSerialNumber, '') = '' OR CHARINDEX(#UutSerialNumber, UutSerialNumber) > 0)
This is nothing more than a footnote however as I have done no performance testing, I just think it is easier on the eye!
SELECT DeviceName, UutStatus
FROM MyTable
WHERE ((#UutSerialNumber = '') OR (#UutSerialNumber is null)) OR (UutSerialNumber like #UutSerialNumber)
add '%' to the last #UutSerialNumber if you think you need

creating SQL command to return match or else everything else

i have three checkboxs in my application. If the user ticks a combination of the boxes i want to return matches for the boxes ticked and in the case where a box is not checked i just want to return everything . Can i do this with single SQL command?
I recommend doing the following in the WHERE clause;
...
AND (#OnlyNotApproved = 0 OR ApprovedDate IS NULL)
It is not one SQL command, but works very well for me. Basically the first part checks if the switch is set (checkbox selected). The second is the filter given the checkbox is selected. Here you can do whatever you would normally do.
You can build a SQL statement with a dynamic where clause:
string query = "SELECT * FROM TheTable WHERE 1=1 ";
if (checkBlackOnly.Checked)
query += "AND Color = 'Black' ";
if (checkWhiteOnly.Checked)
query += "AND Color = 'White' ";
Or you can create a stored procedure with variables to do this:
CREATE PROCEDURE dbo.GetList
#CheckBlackOnly bit
, #CheckWhiteOnly bit
AS
SELECT *
FROM TheTable
WHERE
(#CheckBlackOnly = 0 or (#CheckBlackOnly = 1 AND Color = 'Black'))
AND (#CheckWhiteOnly = 0 or (#CheckWhiteOnly = 1 AND Color = 'White'))
....
sure. example below assumes SQL Server but you get the gist.
You could do it pretty easily using some Dynamic SQL
Lets say you were passing your checkboxes to a sproc as bit values.
DECLARE bit #cb1
DECLARE bit #cb2
DECLARE bit #cb3
DECLARE nvarchar(max) #whereClause
IF(#cb1 = 1)
SET #whereClause = #whereClause + ' AND col1 = ' + #cb1
IF(#cb2 = 1)
SET #whereClause = #whereClause + ' AND col2 = ' + #cb2
IF(#cb3 = 1)
SET #whereClause = #whereClause + ' AND col3 = ' + #cb3
DECLARE nvarchar(max) #sql
SET #sql = 'SELECT * FROM Table WHERE 1 = 1' + #whereClause
exec (#sql)
Sure you can.
If you compose your SQL SELECT statement in the code, then you just have to generate:
in case nothing or all is selected (check it using your language), you just issue non-filter version:
SELECT ... FROM ...
in case some checkboxes are checked, you create add a WHERE clause to it:
SELECT ... FROM ... WHERE MyTypeID IN (3, 5, 7)
This is single SQL command, but it is different depending on the selection, of course.
Now, if you would like to use one stored procedure to do the job, then the implementation would depend on the database engine since what you need is to be able to pass multiple parameters. I would discourage using a procedure with just plain 3 parameters, because when you add another check-box, you will have to change the SQL procedure as well.
SELECT *
FROM table
WHERE value IN
(
SELECT option
FROM checked_options
UNION ALL
SELECT option
FROM all_options
WHERE NOT EXISTS (
SELECT 1
FROM checked_options
)
)
The inner subquery will return either the list of the checked options, or all possible options if the list is empty.
For MySQL, it will be better to use this:
SELECT *
FROM t_data
WHERE EXISTS (
SELECT 1
FROM t_checked
WHERE session = 2
)
AND opt IN
(
SELECT opt
FROM t_checked
WHERE session = 2
)
UNION ALL
SELECT *
FROM t_data
WHERE NOT EXISTS (
SELECT 1
FROM t_checked
WHERE session = 2
)
MySQL will notice IMPOSSIBLE WHERE on either of the SELECT's, and will execute only the appropriate one.
See this entry in my blog for performance detail:
Selecting options
If you pass a null into the appropriate values, then it will compare that specific column against itself. If you pass a value, it will compare the column against the value
CREATE PROCEDURE MyCommand
(
#Check1 BIT = NULL,
#Check2 BIT = NULL,
#Check3 BIT = NULL
)
AS
SELECT *
FROM Table
WHERE Column1 = ISNULL(#Check1, Column1)
AND Column2 = ISNULL(#Check2, Column2)
AND Column3 = ISNULL(#Check3, Column3)
The question did not specify a DB product or programming language. However it can be done with ANSI SQL in a cross-product manner.
Assuming a programming language that uses $var$ for variable insertion on strings.
On the server you get all selected values in a list, so if the first two boxes are selected you would have a GET/POST variable like
http://url?colors=black,white
so you build a query like this (pseudocode)
colors = POST['colors'];
colors_list = replace(colors, ',', "','"); // separate colors with single-quotes
sql = "WHERE ('$colors$' == '') OR (color IN ('$colors_list$'));";
and your DB will see:
WHERE ('black,white' == '') OR (color IN ('black','white')); -- some selections
WHERE ('' == '') OR (color IN ('')); -- nothing selected (matches all rows)
Which is a valid SQL query. The first condition matches any row when nothing is selected, otherwise the right side of the OR statement will match any row that is one of the colors. This query scales to an unlimited number of options without modification. The brackets around each clause are optional as well but I use them for clarity.
Naturally you will need to protect the string from SQL injection using parameters or escaping as you see fit. Otherwise a malicious value for colors will allow your DB to be attacked.