Check condition in WHERE clause - sql-server

I have below dynamic WHERE condition in XML mapping which is working fine:
WHERE
IncomingFlightId=#{flightId}
<if test="screenFunction == 'MAIL'.toString()">
and ContentCode = 'M'
</if>
<if test="screenFunction == 'CARGO'.toString()">
and ContentCode Not IN('M')
</if>
order by ContentCode ASC
I'm trying to run below query in a IDE but unfortunately its not working.
Can anybody please explain what i'm doing wrong?
WHERE
IncomingFlightId = 2568648
AND (IF 'MAIL' = 'MAIL'
BEGIN
SELECT 'and ContentCode = "M"'
END ELSE BEGIN
SELECT 'and ContentCode Not IN("M")'
END)
order by ContentCode ASC

You can't use IF in straight up SQL statement, use CASE WHEN test THEN returniftrue ELSE valueiffalse END instead (if you have to use conditional logic)
That said, it's probably avoidable if you do something like this:
WHERE
(somecolumn = 'MAIL' AND ContentCode = 'M') OR
(somecolumn <> 'MAIL' and ContentCode <> 'M')
Example of conditional logic in a straight SQL:
SELECT * FROM table
WHERE
CASE WHEN col > 0 THEN 1 ELSE 0 END = 1
Case when runs a test and returns a value. You always have to compare the return value to something else. You can't do something that doesn't return a value.
It's kinda dumb here though, because anything you can express in the truth of a case when, can be more simply and readably expressed in the truth of a where clause directly..
SELECT * FROM table
WHERE
CASE WHEN type = 'x'
THEN (SELECT count(*) FROM x)
ELSE (SELECT count(*) FROM y)
END = 1
Versus
SELECT * FROM table
WHERE
(type = 'x' AND (SELECT count(*) FROM x) = 1) OR
type <> 'x' AND (SELECT count(*) FROM y) = 1)
It's useful for things like this though:
SELECT
bustourname,
SUM(CASE WHEN age > 60 THEN 1 ELSE 0 END) as count_of_old_people
FROM table
GROUP BY bustourname
If you're looking to write a stored procedure that conditionally builds an SQL, then sure, you can do that...
DECLARE #sql VARCAHR(max) = 'SELECT * FROM TABLE WHERE';
IF blah SET #sql = CONCAT(#sql, 'somecolumn = 1')
IF otherblah SET #sql = CONCAT(#sql, 'othercolumn = 1')
EXEC #sql...
But this is only in a stored procedure or procedure-like sql script where it builds a string that looks like an SQL, and then executes it dynamically. You cannot use IF in a plain SELECT statement

You are running the query which (beside it is syntactically incorrect SQL) has nothing to do with query generated and used by mybatis.
You need to understand how if in mybatis mapper works.
if element evaluates before the query is executed at the stage of generation of the SQL query text. If the value of the test is true the content of if element is included into the resulting query. In your case depending on the screenFunction parameter passed to mybatis mapper method one of three conditions are generated.
If value of screenFunction is MAIL then:
WHERE
IncomingFlightId=#{flightId}
and ContentCode = 'M'
order by ContentCode ASC
If value of screenFunction is CARGO then:
WHERE
IncomingFlightId=#{flightId}
and ContentCode Not IN('M')
order by ContentCode ASC
Otherwise (if value of screenFunction is not MAIL and is not CARGO):
WHERE
IncomingFlightId=#{flightId}
order by ContentCode ASC
Only after the query text is generated it is executed via JDBC against the database.
So if you want to run the query manually you need to try one of these queries.
One thing that you may want to do to make it easier is to enable logging of SQL queries and parameters passed to them so you can more easily rerun them.

Related

Which is faster (ISNULL(#SKU, '') = '') or (#SKU IS NULL OR #SKU = '')

There is one if case where I am using IF #SKU IS NULL OR #SKU = '', but my friend says it will take more time as compare to IF ISNULL(#SKU, '') = ''. So you should use IF ISNULL(#SKU, '') = ''. But I think I'm using correct. So please suggest me which one is run faster.
This is my stored procedure:
CREATE PROCEDURE USP_GetExistingRefunds
(
#OrderNo VARCHAR(50),
#SKU VARCHAR(255),
#ProfileID INT
)
AS
BEGIN
--IF ISNULL(#SKU, '') = '' --this work faster or
IF #SKU IS NULL OR #SKU = '' --this work faster
BEGIN
SELECT OrderNo, SKU, ISNULL(Quantity, 0) Quantity, ISNULL(Amount, 0) Amount
FROM StoreRefundOrder SRO
INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID
WHERE SRO.OrderNo = #OrderNo
AND ProfileID = #ProfileID
END
ELSE
BEGIN
SELECT OrderNo, SKU, ISNULL(SUM(Quantity), 0) Quantity, ISNULL(SUM(Amount), 0) Amount
FROM StoreRefundOrder SRO
INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID
WHERE SRO.OrderNo = #OrderNo
AND SROI.SKU = #SKU
AND ProfileID = #ProfileID
GROUP BY OrderNo, SKU
END
END
In the context of an IF/ELSE Procedural batch
It doesn't make any difference whatsoever. It literally takes 0.00 MS to determine if a value is blank or unknown, it takes the 0.00MS to determine if ISNULL(#SKU, '') = ''. If there is a difference it would likely be measured in nanoseconds IMO. Again, this in the context of a procedural batch because the statement is only being evaluated once.
In the context of an FILTER (e.g. ON, WHERE or HAVING CLAUSE)
Here the difference is actually enormous, it cannot be understated. This is tricky to explain with parameters and variables involved, so, for brevity, I show you an example with this sample data:
IF OBJECT_ID('tempdb..#things','U') IS NOT NULL DROP TABLE #things;
SELECT TOP (10000) Txt = SUBSTRING(LEFT(NEWID(),36),1,ABS(CHECKSUM(NEWID())%x.N))
INTO #things
FROM (VALUES(1),(30),(40),(NULL)) AS x(N)
CROSS JOIN sys. all_columns;
UPDATE #things SET Txt = NEWID() WHERE txt = '0';
CREATE NONCLUSTERED INDEX nc_things1 ON #things(Txt);
The following queries will find rows that either do or do not contain blanks or nulls
-- Finding things that are blank or NULL
SELECT t.Txt
FROM #things AS t
WHERE t.Txt IS NULL OR t.Txt = '';
-- Finding things that are NOT blank or NULL
SELECT t.Txt
FROM #things AS t
WHERE NOT(t.Txt IS NULL OR t.Txt = '');
SELECT t.Txt
FROM #things AS t
WHERE t.Txt > '';
-- Finding things that are blank or NULL
SELECT t.Txt
FROM #things AS t
WHERE ISNULL(t.Txt,'') = '';
-- Finding things that are NOT blank or NULL
SELECT t.Txt
FROM #things AS t
WHERE ISNULL(t.Txt,'') <> '';
The first three queries are SARGable, the last two are not because of ISNULL. Even though there's an index to help me, the ISNULL renders it useless here. It's the difference between asking someone to look in a phone book for everyone whose name begins with "A" and finding everyone who's name ends with "A".
SARGable predicates allow a query to seek a portion of an index where non-SARGable predicates force a query to scan the entire table REGARDLESS of the how many matching rows exist (if any). When you are dealing with millions/billions of rows joined to many other tables the difference can be a query that runs in seconds to one that, in some cases, may run for hours or even weeks (I've seen a few).
EXECUTION PLANS:
Note that this last one WHERE t.Txt > '' will work too. Any non-null text value is > '' and if t.Txt was NULL then it will also evaluate to false. I include this because this expression works for filtered indexes. The only catch is you can't use it on a text field where Implicit conversion can transform this into the number 0 or less. Note these queries:
IF '' = 0 PRINT 'True' ELSE PRINT 'False'; -- Returns True
IF '' = '0' PRINT 'True' ELSE PRINT 'False'; -- Returns False
IF '' > -1 PRINT 'True' ELSE PRINT 'False'; -- Returns True
IF '' > '-1' PRINT 'True' ELSE PRINT 'False'; -- Returns False
IF #SKU IS NULL OR #SKU ='' is checking null and blank both. In second case if Isnull(#sku,'') you are checking null and assigning '' for null value. Both are different cases.
Which is faster (ISNULL(#SKU, '') = '') or (#SKU IS NULL OR #SKU =
'')
It really doesn't matter in this case. If you were comparing against a column then (SKU IS NULL OR SKU = '') would be preferable as it can use an index but any difference for a single comparison against a variable will be in the order of microseconds and dwarfed by the execution times of the SELECT statements.
To simplify the IF statement I'd probably invert it anyway as below
IF #SKU <> '' --Not null or empty string
BEGIN
SELECT OrderNo, SKU, ISNULL(SUM(Quantity), 0) Quantity, ISNULL(SUM(Amount), 0) Amount
FROM StoreRefundOrder SRO
INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID
WHERE SRO.OrderNo = #OrderNo
AND SROI.SKU = #SKU
AND ProfileID = #ProfileID
GROUP BY OrderNo, SKU
END
ELSE
BEGIN
SELECT OrderNo, SKU, ISNULL(Quantity, 0) Quantity, ISNULL(Amount, 0) Amount
FROM StoreRefundOrder SRO
INNER JOIN StoreRefundOrderItem SROI ON SRO.ID = SROI.RefundOrderID
WHERE SRO.OrderNo = #OrderNo
AND ProfileID = #ProfileID
END
A little long for a comment.
As has been noted by many already, in this case your two options don't really make any appreciable difference. But in the future, when you think of a couple of different ways to code something, there are standard practices you can implement quickly and easily to test for yourself.
At the top of your code block, add this command:
SET STATISTICS TIME, IO ON;
You can use either TIME or IO, but I almost always want to see both, so I always turn them both on at the same time.
The output from this addition will show up in your Messages window after your query or queries run and will give you tangible information about which method is faster, or causes less stress on the SQL Server engine.
You'll want to run a few tests with each option. Warm cache / cold cache especially, but a few iterations is best one way or the other to get an average or eliminate outlier results.
I'm weird, so I always close my code block with:
SET STATISTICS TIME, IO OFF;
But strictly speaking that's unnecessary. I just have a thing about resetting anything I change, just to avoid any possibility of forgetting to reset something that will matter.
Kendra Little has an informative blog post on using STATISTICS.

Stored procedure with conditional Where clause in SQL Server

I am creating a SQL Server stored procedure. It's a simple SELECT query that I am building. One of the parameters is to look for a flag parameter. If that parameter is left blank, then the SELECT should default to NOT having the WHERE clause at all.
CREATE PROCEDURE sprocA
#flagInd int
AS
BEGIN
SELECT intID, strQuestion, strAnswer, intCategory, intOrder
FROM tblA
-- Right here is where I am looking for the logic of the #flagInd to be evaluated.....
-- if #flagInd = 1 then 'WHERE flgInd=1'
-- else if #flagInd=0 then 'WHERE flgInd=0'
-- else if #flagInd IS NULL then ''
It's just a simple query and a simple thought I had, not sure if it can be done without nesting and rewriting the whole SELECT statement as part of of the IF statement.
This can be done like:
SELECT intID, strQuestion, strAnswer, intCategory, intOrder
FROM tblA
WHERE flgInd = #flgInd OR #flgInd IS NULL;
You can also use a CASE expression:
SELECT intID, strQuestion, strAnswer, intCategory, intOrder
FROM tblA
WHERE CASE
WHEN flgInd = #flgInd THEN 1
WHEN #flgInd IS NULL THEN 1
ELSE 0
END = 1;
There appears to just be a one to one mapping (from the parameter to the column) so why not use a simple where clause?
CREATE PROCEDURE sprocA
#flagInd int
AS
BEGIN
SELECT intID, strQuestion, strAnswer, intCategory, intOrder
FROM tblA WHERE flgInd = #flagInd;

SQL CASE Statement Change Column name in THEN clause

Is there anyway that I can change column name through THEN clause in CASE STATEMENT. This is how my code looks but it is not working.
SELECT
CASE
WHEN #IDWeeklySpecial = '107'
THEN
CASE
WHEN ISNULL(v.blnLeftHandDrive, 0) = 1
THEN [HANDEDNESS] = 'LHD'
ELSE [HANDEDNESS] = 'RHD'
ELSE
CASE
WHEN ISNULL(v.blnLeftHandDrive, 0) = 1
THEN STEERING = 'LHD'
ELSE STEERING = 'RHD'
END
END
I want this result
My Ideal Result
Is this achievable? If so how?
No, it's not possible... if you are asking about dynamically modifying the column name in the resultset.
The CASE expression returns a value. To a specify the column name in the resultset, assign an alias.
SELECT CASE ... END AS mycolname
, ...
FROM ...
The column name is determined when the statement is parsed and prepared. The execution of the statement cannot modify the column name.
(It's possible I didn't understand the question. It's not clear what OP is tying to achieve.)
case is an expression. You can name the column before or after an expression, but not in the middle.
Also, a SQL statement needs to return a fixed set of columns, including the column name.
So, you cannot do what you want with a simple SQL statement. You could use dynamic SQL:
declare #sql nvarchar(max);
set #sql = '
SELECT (CASE WHEN COALESCE(v.blnLeftHandDrive, 0) = 1
THEN 'LHD'
ELSE 'RHD'
END) as [columnname]
. . . ';
set #sql = replace(#sql, '[columnname]',
(case when #IDWeeklySpecial = '107' then 'HANDEDNESS' else 'STEERING' end)
);
exec sp_executesql #sql;
Note that the . . . is for the rest of your SQL query.
I can't digest your select statement, but assume its inline case
SELECT item_name = CASE WHEN item_name = 'sometext' THEN item_id ELSE item_name END AS MyColumnName
It is not possible to assign differing column names in your query, because all vertical data values in the result set belong to the same column.
If you are just trying to alias a column name while using the CASE expression, ensure that the AS is used after the END keyword. Others have mentioned this, but it was the problem in my case. Changed it to something like this and it worked:
CASE
WHEN table1.Name LIKE '%John%' THEN 'Some John Person'
WHEN table1.Name <> 'NULL' THEN table1.Name
ELSE 'Nobody'
END AS NewAliasColumnName

Exists vs select count

In SQL Server, performance wise, it is better to use IF EXISTS (select * ...) than IF (select count(1)...) > 0...
However, it looks like Oracle does not allow EXISTS inside the IF statement, what would be an alternative to do that because using IF select count(1) into... is very inefficient performance wise?
Example of code:
IF (select count(1) from _TABLE where FIELD IS NULL) > 0 THEN
UPDATE TABLE _TABLE
SET FIELD = VAR
WHERE FIELD IS NULL;
END IF;
the best way to write your code snippet is
UPDATE TABLE _TABLE
SET FIELD = VAR
WHERE FIELD IS NULL;
i.e. just do the update. it will either process rows or not. if you needed to check if it did process rows then add afterwards
if (sql%rowcount > 0)
then
...
generally in cases where you have logic like
declare
v_cnt number;
begin
select count(*)
into v_cnt
from TABLE
where ...;
if (v_cnt > 0) then..
its best to use ROWNUM = 1 because you DON'T CARE if there are 40 million rows..just have Oracle stop after finding 1 row.
declare
v_cnt number;
begin
select count(*)
into v_cnt
from TABLE
where rownum = 1
and ...;
if (v_cnt > 0) then..
or
select count(*)
into v_cnt
from dual
where exists (select null
from TABLE
where ...);
whichever syntax you prefer.
As Per:
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:3069487275935
You could try:
for x in ( select count(*) cnt
from dual
where exists ( select NULL from foo where bar ) )
loop
if ( x.cnt = 1 )
then
found do something
else
not found
end if;
end loop;
is one way (very fast, only runs the subquery as long as it "needs" to, where exists
stops the subquery after hitting the first row)
That loop always executes at least once and at most once since a count(*) on a table
without a group by clause ALWAYS returns at LEAST one row and at MOST one row (even of
the table itself is empty!)

SQL Server 2008: Why table scaning when another logical condition is satisfied first?

Consider following piece of code:
declare #var bit = 0
select * from tableA as A
where
1=
(case when #var = 0 then 1
when exists(select null from tableB as B where A.id=B.id)
then 1
else 0
end)
Since variable #var is set to 0, then the result of evaluating searched case operator is 1. In the documentation of case it is written that it is evaluated until first WHEN is TRUE. But when I look at execution plan, I see that tableB is scanned as well.
Does anybody know why this happens? Probably there are ways how one can avoid second table scan when another logical condition is evaluated to TRUE?
Because the plan that is compiled and cached needs to work for all possible values of #var
You would need to use something like
if (#var = 0)
select * from tableA
else
select * from tableA as A
where exists(select * from tableB as B where A.id=B.id)
Even OPTION RECOMPILE doesn't look like it would help actually. It still doesn't give you the plan you would have got with a literal 0=0
declare #var bit = 0
select * from
master.dbo.spt_values as A
where
1=
(case when 0 = #var then 1
when exists(select null from master.dbo.spt_values as B where A.number=B.number)
then 1
else 0
end)
option(recompile)
Plan http://img189.imageshack.us/img189/3977/executionplan.jpg
select * from
master.dbo.spt_values as A
where
1=
(case when 0 = 0 then 1
when exists(select null from master.dbo.spt_values as B where A.number=B.number)
then 1
else 0
end)
Plan http://img193.imageshack.us/img193/3977/executionplan.jpg
RE: Question in comments. Try the following with the "Include Actual Execution Plan" option enabled.
declare #var bit = datepart(second,GETDATE())%2
print #var
if (#var = 0)
select * from
master.dbo.spt_values --8BA71BA5-3025-4967-A0C8-38B9FBEF8BAD
else
select * from
master.dbo.spt_values as A --8BA71BA5-3025-4967-A0C8-38B9FBEF8BAD
where exists(select null from master.dbo.spt_values as B where A.number=B.number)
Then try
SELECT usecounts, cacheobjtype, objtype, text, query_plan
FROM sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
CROSS APPLY sys.dm_exec_query_plan(plan_handle)
where text like '%8BA71BA5-3025-4967-A0C8-38B9FBEF8BAD%'
The Compiled Plan will look like
Plan http://img178.imageshack.us/img178/3977/executionplan.jpg
The Actual Execution Plan will show only one path was actually executed though.
if tableB has few rows, a table scan is the fastest way to go.
best source for dynamic search conditions:
Dynamic Search Conditions in T-SQL by Erland Sommarskog
there are a lot of subtle implications on how you do this as to if an index can be used or not. If you are on the proper release of SQL Server 2008 you can just add OPTION (RECOMPILE) to the query and the local variable's value at run time is used for the optimizations.
Consider this, OPTION (RECOMPILE) will take this code (where no index can be used with this mess of ORs):
WHERE
(#search1 IS NULL or Column1=#Search1)
AND (#search2 IS NULL or Column2=#Search2)
AND (#search3 IS NULL or Column3=#Search3)
and optimize it at run time to be (provided that only #Search2 was passed in with a value):
WHERE
Column2=#Search2
and an index can be used (if you have one defined on Column2)

Resources