How do i check if something exist without using count(*) ... limit 1 - sql-server

My code is SELECT COUNT(*) FROM name_list WHERE [name]='a' LIMIT 1
It appears there is no limit clause in SQL Server. So how do i say tell me if 'a' exist in name_list.name?

IF EXISTS(SELECT * FROM name_list WHERE name = 'a')
BEGIN
-- such a record exists
END
ELSE
BEGIN
-- such a record does not exist
END
Points to note:
don't worry about the SELECT * - the database engine knows what you are asking
the IF is just for illustration - the EXISTS(SELECT ...) expression is what answers your question
the BEGIN and END are strictly speaking unnecessary if there is only one statement in the block

COUNT(*) returns a single row anyway, no need to limit.
The ANSI equivalent for LIMIT is TOP: SELECT TOP(1) ... FROM ... WHERE...
And finally, there is EXISTS: IF EXISTS (SELECT * FROM ... WHERE ...).

The TOP clause is the closest equivalent to LIMIT. The following will return all of the fields in the first row whose name field equals 'a' (altough if more than one row matches, the row that ets returned will be undefined unless you also provide an ORDER BY clause).
SELECT TOP 1 * FROM name_list WHERE [name]='a'
But there's no need to use it if you're doing a COUNT(*). The following will return a single row with a single field that is number of rows whose name field eqals 'a' in the whole table.
SELECT COUNT(*) FROM name_list WHERE [name]='a'

IF (EXISTS(SELECT [name] FROM name_list where [name] = 'a'))
begin
//do other work if exists
end
You can also do the opposite:
IF (NOT EXISTS(SELECT [name] FROM name_list where [name] = 'a'))
begin
//do other work if not exists
end

No nono that is wrong.
First there is top, so you have to say something like:
select top 1 1 from name_list where [name]='a'
You'll get a row with only a unnamed field with 1 out of the query if there is data, and no rows at all if there is no data.

This query returns exactly what you intended:
SELECT TOP 1 CASE WHEN EXISTS(SELECT * WHERE [name] = 'a') THEN 1 ELSE 0 END FROM name_list

Related

I am trying to use an IF statement using a select IN clause. What is he correct syntax for the following T-SQL statement?

Using SQL Server 2000; I am trying to determine the action if a value in a field of the INSERTED record matches one of several distinct values in a field in a table. Field y in tableB could be say 'one', 'two' or 'three'. The INSERTED record must be a single record, and therefore the field x must be a single value. Hence, given the code snippet below, what is the correct syntax? In particular where do the "()" go in the IF statement?
if select x from INSERTED in (select y from tableB)
and <another condition>
begin
<some code>
end
The correct syntax is
IF EXISTS (SELECT * FROM TABLE1 WHERE X IN (1,2))
Begin
-- code
End
You can store the individual flag in variables and use that in IF condition.
Declare #chkExist int
Select #chkExist = Count(*) from tableA x in (select y from tableB)
if ((#chkExist > 0) and (<another condition>))
begin
<some code>
end
Try a "where exists" instead of "IN"
if exists ( select null from tableA taAlias where (select null from tableB tbAlias where tbAlias.y = taAlias.x ) )
and 1=1
begin
Select 1 as 'YouNeedAtLeastOneLineOfCodeInThisBeginEnd'
end

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!)

Index seek with coalesce

I have a table [MyTable] with a column [MyColumn] NVarchar(50). I have a nonclustered index on this column, now while running the below two queries:
SELECT 1
FROM [MyTable] M
WHERE M.[MyColumn] = #MyColumn
SELECT 1
FROM [MyTable] M
WHERE M.[MyColumn] = COALESCE(#MyColumn, M.[MyColumn] )
I noticed the first query is using Index Seek (NonClustered) and the second one is using Index Scan (Non Clustered). May I know how will I make use of index seek with coalesce or isnull ?
May I know how will I make use of
index seek with coalesce or isnull ?
Perhaps not an answer to your question but you can have two different queries. One for the case where #MyColumn is null and one for the case where you want to use #MyColumn in the where clause.
IF #MyColumn IS NULL
BEGIN
SELECT 1
FROM [MyTable] M
END
ELSE
BEGIN
SELECT 1
FROM [MyTable] M
WHERE M.[MyColumn] = #MyColumn
END
This isn't easy, since as Alex pointed out using the functions forces a scan, since the optimizer knows it needs to check every row.
What you CAN do is created a Computed Column for the result of your function, and index that column.
There's not really a prettier way to get a seek.
EDIT:
In rereading your question, this may not be an option for you unless you rethink your logic. You are integrating a variable into the function, and there is absolutely no way to index that.
EDIT 2:
Instead of your current logic, try something like:
...
WHERE (M.[MyColumn] = #MyColumn
OR #MyColumn IS NULL)
Using functions like COALESCE or ISNULL in the where clause is asking the server to search for the results of those functions - which are unknown until they are executed for every row in the resulting set, so there is no way for it to make use of an index.
To take full advantage of the index don't use functions in the WHERE clause, modify it with standard conditions e.g. WHERE MyColumn = #MyColumn OR #MyColumn IS NULL
I guess you will use this query in a more complex one, possibly withEXISTS:
EXISTS
( SELECT 1
FROM [MyTable] M
WHERE M.[MyColumn] = COALESCE(#MyColumn, M.[MyColumn] )
)
Try this instead:
EXISTS
( SELECT 1
FROM [MyTable] M
WHERE M.[MyColumn] = #MyColumn
)
OR EXISTS
( SELECT 1
FROM [MyTable] M
WHERE #MyColumn IS NULL
)
Or this one:
CASE WHEN #MyColumn IS NULL
THEN 1
ELSE
( SELECT 1
FROM [MyTable] M
WHERE M.[MyColumn] = #MyColumn
)
END
In the query with the coalesce clause the optimizer knows "MyColumn" is a range of values so it will decide to use a scan on the index. The only method to use a seek when there is a non null variable passed in is to code two stored procs and call the appropiate one via logic testing of the variable.
If you have a situation as simple as your example and you wish to use an Index Seek when the variable is NOT NULL then you should code the query as :
If #MyColumn is NULL
Begin
EXEC MyStoredProcWithMyColumn=Mycolumn
END
ELSE
Begin
EXEC MyStoredProcWithMyColumn=Variable #MyColumn
END
after creating two stored procedures one which returns the data using the where clause with the variable and one with the where cluase for the column equal to itself.

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)

Saving a select count(*) value to an integer (SQL Server)

I'm having some trouble with this statement, owing no doubt to my ignorance of what is returned from this select statement:
declare #myInt as INT
set #myInt = (select COUNT(*) from myTable as count)
if(#myInt <> 0)
begin
print 'there's something in the table'
end
There are records in myTable, but when I run the code above the print statement is never run. Further checks show that myInt is in fact zero after the assignment above. I'm sure I'm missing something, but I assumed that a select count would return a scalar that I could use above?
If #myInt is zero it means no rows in the table: it would be NULL if never set at all.
COUNT will always return a row, even for no rows in a table.
Edit, Apr 2012: the rules for this are described in my answer here:Does COUNT(*) always return a result?
Your count/assign is correct but could be either way:
select #myInt = COUNT(*) from myTable
set #myInt = (select COUNT(*) from myTable)
However, if you are just looking for the existence of rows, (NOT) EXISTS is more efficient:
IF NOT EXISTS (SELECT * FROM myTable)
select #myInt = COUNT(*) from myTable
Declare #MyInt int
Set #MyInt = ( Select Count(*) From MyTable )
If #MyInt > 0
Begin
Print 'There''s something in the table'
End
I'm not sure if this is your issue, but you have to esacpe the single quote in the print statement with a second single quote. While you can use SELECT to populate the variable, using SET as you have done here is just fine and clearer IMO. In addition, you can be guaranteed that Count(*) will never return a negative value so you need only check whether it is greater than zero.
[update] -- Well, my own foolishness provides the answer to this one. As it turns out, I was deleting the records from myTable before running the select COUNT statement.
How did I do that and not notice? Glad you asked. I've been testing a sql unit testing platform (tsqlunit, if you're interested) and as part of one of the tests I ran a truncate table statement, then the above. After the unit test is over everything is rolled back, and records are back in myTable. That's why I got a record count outside of my tests.
Sorry everyone...thanks for your help.

Resources