Use NULL as a valid value in a stored procedure - sql

I have a stored procedure with one parameter, #ID, which is an integer that might be zero. When it is zero, I want to use it as if it is null. So here is how I have written my query:
If #ID = 0
SELECT * FROM MyTable WHERE ID IS NULL
ELSE
SELECT * FROM MyTable WHERE ID = #ID;
This is quite inelegant. Surely there is a way to write the WHERE clause in such a way that makes duplicating the SELECT statement unnecessary.

You can phrase this more simply using:
SELECT *
FROM MyTable
WHERE COALESCE(ID, 0) = #id;
Next, you probably do not want to do this. It will prevent SQL Server from using an index. Similarly, OR is likely to prevent optimization as well.
Probably your best bet is your current code, or UNION ALL:
SELECT *
FROM MyTable
WHERE ID IS NULL AND #id = 0
UNION ALL
SELECT *
FROM MyTable
WHERE ID = #ID; -- not sure if `#id <> 0` is needed here
With this or your approach, you probably need OPTION (RECOMPILE) to ensure that an index is always used.

Just combine them using AND/OR logic:
SELECT *
FROM MyTable
WHERE (#Id != 0 AND ID = #ID)
OR (#Id = 0 AND ID IS NULL);

Related

How to use case statement inside an SQL select Query

I need to get the output of a selected query depending on certain conditions
Means if(id=uid)
then I need the below query
select * from table1 where id=5;
else
I need the below one
select * from table1 where id=10
I know i can use if condition for this. But my query is very long one so when I use if else then it would look like
if(#id=#uid)
begin
select * from table1 where id=5;// query 1
end
else
select * from table1 where id=10;//query 2
but here I need to replace the entire query once again for a single check. I hope I can do something like this:
declare #id int=4;
declare #uid=10;
select * from table1 where
case
when #id=#uid
then
id=5
else
id=10;
end
Updation
I need one more condition too
in this case id=5 and uid=10
then if(id=uid)
then
select * from table1 where id=5
and
if(id!=uid)
then
select * from table1
something like this
You can use the case expression to return the value id should be equal to:
SELECT *
FROM table1
WHERE id = CASE WHEN #id = #uid THEN 5 ELSE 10 END;
EDIT:
The updated requirement in the question is to return all rows when #id != #uid. This can be done by comparing id to id:
SELECT *
FROM table1
WHERE id = CASE WHEN #id = #uid THEN 5 ELSE id END;
Alternatively, with this updated requirement, a simple or expression might be simpler to use:
SELECT *
FROM table1
WHERE #id = #uid OR id = 5;
SELECT * FROM table1
WHERE (id=5 AND #id=#uid) OR (id=10 AND #id<>#uid)
SELECT
*
FROM
table1
WHERE
(
#id = #uid
AND
id =5
)
OR
(
not #id = #uid
AND
id=10
)

Conditional filter in WHERE clause

How can I set a conditional filter in a SQL WHERE clause? For example, I have a parameter #ID with the following procedure
SELECT * FROM myTable WHERE Column1 = 'test' AND Column2 = #ID
However, If #ID = -1 I don't want the last part of the SQL (AND Column2 = #ID) included
I realize I can make an if statement with 2 separate queries, however this is a large script and has this same issue multiple times, so I was hoping there was a better way than nearly duplicating several queries
This is ok for T-SQL:
SELECT * FROM myTable WHERE Column1 = 'test' AND (#ID = -1 OR Column2 = #ID)
Just include the condition in your SQL as an OR, note the brackets
SELECT * FROM myTable WHERE Column1 = 'test' AND (#ID = -1 OR Column2 = #ID)
One alternative:
SELECT * FROM myTable WHERE Column1 = 'test' AND #ID in (-1,Column2)

Use top based on condition

I have a parameter in my stored procedure that specifies number of rows to select. (Possible values: 0-100. 0 Means Select All rows)
For example #Rows = 5;
Then I can do this:
Insert into #MyTableVar
Select Top(#Rows) *
from myTable
Now, as I said before if 0 is supplied I need to return all rows.
This is a pseudo-code of what I need:
if (#Rows=0) then select * else select top(#Rows) *
I found out that there's SET ROWCOUNT that accepts 0 to return ALL rows, but I need to do an insert into a table variable which is not supported by ROWCOUNT.
Is it possible to achieve this without dynamic sql?
(I understand that I can write a simple if else statement and duplicate query, but I have pretty complex queries and there are lots fo them, I just want to avoid code duplication)
One way is to just put a big number in:
set #Rows = 5;
declare #RowsToUse = (case when #Rows = 0 then 1000000000 else #Rows end);
select top(#RowsToUse) * from myTable
First of all, you are missing the ORDER BY clause, since you are using TOP. You could do this:
SET #Rows = 5;
WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER(ORDER BY Id) --put the right order here
FROM myTable
)
INSERT INTO #MyTableVar
SELECT YourColumns
FROM CTE
WHERE RN <= #Rows OR #Rows = 0

IF condition in view in SQL Server

Is it possible to have a if condition in VIEWS
eg
CREATE VIEW
as
DECLARE #Count int
SET #Count=-1
select #Count=EmpID from EmployeeDetails where ID=200
IF #Count=-1
BEGIN
SELECT * FROM TEAM1
END
ELSE
BEGIN
SELECT * FROM TEAM1
END
You could try something sneaky with a UNION :
SELECT {fieldlist}
FROM Table1
WHERE EXISTS(SELECT EmpID FROM EmployeeDetails WHERE ID = 200)
UNION ALL
SELECT {fieldlist}
FROM Table2
WHERE NOT EXISTS(SELECT EmpID FROM EmployeeDetails WHERE ID = 200)
This method would require both SELECT statements to return the same set of fields, although their sources might be different.
Views only allow select statements as stated in here
if you need to do if on column values you can use a
SELECT
CASE WHEN COLUMN1 = 1 THEN COLUMNX ELSE COLUMNY END
FROM TABLE1
if your need exceeds this you should create a select from a table valued function instead of a view.
What you need is a simple Procedure
CREATE PROCEDURE DOSOMETHING
(
#ID INT
)
AS
BEGIN
IF #ID > 100
SELECT 1 AS ID,'ME' AS NAME, GETDATE() AS VARIABLEDATECOL, NEWID() AS VARIABLEGUID
ELSE
SELECT 2 AS ID, 'YOU' AS NAME
END
No I don't believe this is possible.
You could use a stored procedure instead to achieve this functionality.
simply use a udf (User defined Function)
Here you can use IF, ELSE, WHILE etc.
But when you are manipulating data (INSERT, UPDATE, DELETE) then you have to use Stored Procedures because udf's aren't able to do that

What's the best way to lock a record while it is being updated?

If I need to SELECT a value from a table column (happens to be the primary key column) based on a relatively complex WHERE clause in the stored procedure, and I then want to update that record without any other concurrent stored procedures SELECTing the same record, is it as simple as just using a transaction? Or do I also need to up the isolation to Repeatable Read?
It looks like this:
Alter Procedure Blah
As
Declare #targetval int
update table1 set field9 = 1, #targetval = field1 where field1 = (
SELECT TOP 1 field1
FROM table1 t
WHERE
(t.field2 = 'this') AND (t.field3 = 'that') AND (t.field4 = 'yep') AND (t.field9 <> 1))
return
I then get my targetval in my program so that I can do work on it, and meanwhile I don't have to worry about other worker threads grabbing the same targetval.
I'm talking SQL 2000, SQL 2005, and SQL 2008 here.
Adding ROWLOCK,UPDLOCK to the sub query should do it.
ALTER PROCEDURE Blah
AS
DECLARE #targetval INT
UPDATE table1
SET field9 = 1,
#targetval = field1
WHERE field1 = (SELECT TOP 1 field1
FROM table1 t WITH (rowlock, updlock)
WHERE ( t.field2 = 'this' )
AND ( t.field3 = 'that' )
AND ( t.field4 = 'yep' )
AND ( t.field9 <> 1 ))
RETURN
Updated
The currently accepted answer to this question does not use updlock. I'm not at all convinced that this will work. As far as I can see from testing in this type of query with a sub query SQL Server will only take S locks for the sub query. Sometimes however the sub query will get optimised out so this approach might appear to work as in Query 2.
Test Script - Setup
CREATE TABLE test_table
(
id int identity(1,1) primary key,
col char(40)
)
INSERT INTO test_table
SELECT NEWID() FROM sys.objects
Query 1
update test_table
set col=NEWID()
where id=(SELECT top (1) id from test_table )
Query 2
update test_table
set col=NEWID()
where id=(SELECT max(id) from test_table)