SQL if( exists()) query duplication - sql

I need to write a a query that gets a set of information from a table, but if there is no information for that specific client, then use the default set. I was wondering if there is a way to avoid query duplication in an if(exists()) statement.
For example:
IF( EXISTS( SELECT * FROM UserTable WHERE Name = #UserName))
BEGIN
SELECT * FROM UserTable WHERE Name = #UserName))
END
ELSE
BEGIN
SELECT * FROM UserTable WHERE Name = 'Jon Skeet'))
END
The first two selects (exists and the true part of the if) are the exact same. I want to avoid running the same query twice if the statement is true. I know the exists stops once the first true condition is met but that is still O(n) worst case.
Another option I know of, is put the information in a temp table and check if information is there, if not return the default information.
Both ways would work but what is the best way to do it? Are there other ways to do this? Is there some way to do this in the WHERE clause since that's the only thing that is different?
Edit: Fixed example to return a row not just a single item. So the answers of dumping the select to a single variable would be equal to a temp table I assume. Also, to not anger Jon Skeet, spelled his name right

You could do this:
SELECT TOP 1
UserID
FROM
UserTable
WHERE
Name IN (#UserName, 'John Skeet')
ORDER BY
CASE WHEN Name = 'John Skeet' THEN 2 ELSE 1 END
(or use LIMIT or whatever the method is for your RDBMS)
Or you could do this:
DECLARE #UserID INT
SELECT
#UserID = UserID
FROM
UserTable
WHERE
Name = #UserName
IF (#UserID IS NULL)
SELECT
#UserID = UserID
FROM
UserTable
WHERE
Name = 'John Skeet'
SELECT #UserID AS UserID
Or this:
SELECT
COALESCE(T2.UserID, T1.UserID)
FROM
UserTable T1
LEFT OUTER JOIN UserTable T2 ON
T2.Name = #UserName
WHERE
T1.Name = 'John Skeet'
Or this:
SELECT
UserID
FROM
UserTable
WHERE
Name = #UserName
IF (##ROWCOUNT = 0) -- MS SQL Server specific, your RDBMS method will vary
SELECT
UserID
FROM
UserTable
WHERE
Name = 'John Skeet'

How about:
DECLARE #UserID int;
SELECT #UserID = UserID FROM UserTable WHERE Name = #UserName
IF(#UserID IS NULL)
BEGIN
SELECT #UserID=UserID FROM UserTable WHERE Name = 'Jon Skeet'))
END
SELECT #UserID

You can select into a variable check the variable if it comes back null run the get default query
something like this:
Declare #uID int;
SELECT #uID = UserID FROM UserTable WHERE Name = #UserName
IF (#uID is null)
begin
'Select default here'
End
select #uID

If you are concerned about performance, I think you probably don't need to be - most DBMSes should optimize away the duplication. If you are still concerned about performance, run it both ways to see if your DBMS has any problem with it.
If you're concerned about code - maintainability of duplicate code sections, readability, and the like, then study the above answers with that in mind. You may find that the duplication is your least-worst option. It's a shame SQL isn't more malleable.

You don't say what DBMS you are using, but it clearly isn't Oracle (which is the one I know). However, I would expect your DBMS would allow you to do something like this pseudo-code:
SELECT UserID FROM UserTable WHERE Name = #UserName
IF <no data returned by that>
BEGIN
SELECT UserID FROM UserTable WHERE Name = 'John Skeet'
END
In Oracle this would be:
BEGIN
SELECT UserID INTO v_UserID FROM UserTable WHERE Name = :UserName;
EXCEPTION
WHEN NO_DATA_FOUND THEN
SELECT UserID INTO v_UserID FROM UserTable WHERE Name = 'John Skeet';
END

Unless you are going to define a set of variables for each field you are returning (and it's only one record) I think the most efficent way is to make the call twice. The temp table would probably cost more.

Related

SQL Stored procedure querying multiple tables syntax

I am currently trying to create a stored procedure where it first grabs the RUId(Registered User ID) from the first table.And then uses this variable to query another table
The easiest way to explain this is by showing the a pseudo code of this request as shown below
create procedure GetRUIdForUser
#Email nvarchar (160)
AS
SELECT RUId From RegisteredUsers
WHERE Email = #Email
Then
Select * From OtherTable where Ruid = #Ruid
What would the correct syntax for this be or would this need to be split up into two separate stored procedures?
Thanks!
Why not simply use JOIN ? instead of multiple variable & select statements :
select t.*
from RegisteredUsers ru inner join
table t
on t.Ruid = ru.RUId
where ru.Email = #Email;
However, your query variable will have only one ruid which will not help you more. So, you need a table variable to hold all ruids instead.
Try this:
SELECT *
FROM OtherTable
WHERE Ruid = ( SELECT RUId
FROM RegisteredUsers
WHERE Email = #Email ) ;
I recommend a join but you need to declare a variable to use what you are trying to do.
create procedure GetRUIdForUser #Email nvarchar (160) as
declare #ruid int
SELECT #ruid = RUId From RegisteredUsers
WHERE Email = #Email
Select * From OtherTable where Ruid = #Ruid
My guess is that problem isn't as simple as you summarized.

SQL server Where Clause variable may be null

I have the following query (SQL server):
DECLARE #UserId INT;
SET #UserId = //... set by dynamic variable
SELECT *
FROM Users
WHERE userId = #UserId
My issue is that if the #UserId is null the query will not evaluate correctly.
How can I write this query to evaluate correctly if the variable is null or not null?
EDIT:
There have been many suggestions to use the following:
WHERE (#UserId IS NULL OR userId = #UserId)
OR similar.
In this case, if there is a table of 3 entries, with userId of 1,2 and 3 the variable '#UserId' IS NULL, this query will return all 3 entries. What I actually need it to return is no entries, as none of them have a userId of NULL
You need to use an OR:
DECLARE #UserId INT;
SET #UserId = //... set by dynamic variable
SELECT *
FROM Users
WHERE (userId = #UserId OR #UserId IS NULL);
This, however, could well have (severe) performance issues if you're writing this in a Stored Procedure, reusing this code a lot or adding more NULLable parameters. If so, include OPTION (RECOMPILE) in your query so that the query plan is generated each time it's run. This will stop the Data Engine using query plans generated that had a different set of NULL parameters.
Edit: The OP wasn't clear on their question. They don't want to pass the value NULL for #UserID and return all rows, they want to pass NULL and get rows where UserID has a value of NULL. That would be:
SELECT *
FROM Users
WHERE UserID = #UserID
OR (UserID IS NULL AND #UserID IS NULL);
After reading the edit, i think you want your query like
SELECT *
FROM Users
WHERE COALESCE(userId ,0) = COALESCE(#UserId,0)
Edit:
As pointed by Gordon Linoff & Larnu that above query will not be good in terms of performance as the query is "non-SARGable", for the better performance same query can be written as
SELECT *
FROM Users
WHERE userId = #UserId OR( userId is null and #UserId is null)
use coalesce
SELECT *
FROM Users
WHERE userId = coalesce(#UserId,val)
You can simplify the Boolean logic instead :
WHERE (#UserId IS NULL OR userId = #UserId)
Try this
DECLARE #UserId INT;
SET #UserId = //... set by dynamic variable
SELECT *
FROM Users
WHERE userId= (case when #UserId is null then userId else #UserId end)
I haven't seen the use of INTERSECT suggested so far so putting this out there as an alternative that also results in easy to read SQL when your list of potentially NULL variables might be long.
SELECT *
FROM Users
WHERE EXISTS (
SELECT UserId
INTERSECT SELECT #UserId
)
This pattern is useful when you have other variables to check e.g.
SELECT *
FROM Users
WHERE EXISTS (
SELECT
UserId,
Username,
UserRole
INTERSECT SELECT
#UserId,
#Username,
#UserRole
)

query in sql server for retrieving rows

I have a table that contains the following 4 columns:
id
name
lastname
phone
I want to write a stored procedure that gets an id as parameter, and then gets the name of that id and then use that name to get all the rows that their name is equal to the names that i found in last step!
here it is my query, i know it's wrong but i'm new to sql commands:
ALTER PROCEDURE dbo.GetAllNames
#id int
AS
select name as Name from Users where id = #id
-- i don't how to retrieve the names that are equal to Name
select * from Users where name = Name
can you correct my query and help me? Thanks.
SELECT by_name.name FROM Users AS by_id
join Users AS by_name ON by_id.name = by_name.name
where id = #id
You can simply say
SELECT * FROM users WHERE name in (SELECT name from users where id = #id)
This would handle the case where there are multiple records with the ID. If ID is a primary key, then you can replace the IN operator with =.
Try this :
ALTER PROCEDURE dbo.GetAllNames
#Id INT
AS
BEGIN
SELECT * FROM Users WHERE [Name] IN (SELECT [Name] FROM Users WHERE Id = #Id)
END
You can declare a variable #name where you can store the name....
ALTER PROCEDURE dbo.GetAllNames
#id int
AS
DECLARE #name nvarchar(50);
select #name =name from Users where id = #id;
select * from Users where name = #name;
I have just provide above solution as per your question style but I strongly recommend you to use JOIN in given scenario in following way:
SELECT b.name FROM Users a
INNER JOIN Users b
ON a.name = b.name
WHERE a.id = #id

How to return default value from SQL query

Is there any easy way to return single scalar or default value if query doesn't return any row?
At this moment I have something like this code example:
IF (EXISTS (SELECT * FROM Users WHERE Id = #UserId))
SELECT Name FROM Users WHERE Id = #UserId
ELSE
--default value
SELECT 'John Doe'
How to do that in better way without using IF-ELSE?
Assuming the name is not nullable and that Id is unique so can match at most one row.
SELECT
ISNULL(MAX(Name),'John Doe')
FROM
Users
WHERE
Id = #UserId
Try ISNULL or COALESCE:
SELECT ISNULL((SELECT TOP 1 Name FROM Users WHERE Id = #UserId), 'John Doe')
The inner select will return nothing if no user exist with this id, the isnull will solve this case.
Try this
SELECT IFNULL(Name,'John Doe')
FROM Users
WHERE Id = #UserId)
You can drop the if statement using following construct but that doesn't necessarely mean it is better.
SELECT Name FROM Users WHERE Id = #UserId UNION ALL
SELECT 'John Doe' WHERE NOT EXISTS (SELECT Name FROM Users WHERE Id = #UserId)
Try isnull
SELECT IsNULL(Name, 'John Doe') FROM Users WHERE Id = #UserId
Edit:
drop table users
go
create table users
(id int,name varchar(20))
go
insert into users select 1,'1'
go
declare #userid int
set #userid = 1
select isnull(username.username, 'John Doe')
from (select #userid as userid) userid
outer apply (SELECT name as username FROM Users WHERE Id = userid.userid ) username
--outer apply (SELECT name as username FROM Users WHERE Id = #userid ) username
I suppose you could use ##ROWCOUNT to see if any will be returned.
SELECT Name FROM Users WHERE Id = #UserId
if(##ROWCOUNT = 0)
SELECT 'John Doe'
You could also use a variable if you're expecting one row.
declare #name varchar(100)
set #name = (select top 1 name from users where id = #userId)
if(#name is null) set #name = 'John Doe'
select #name
I would suggest that the best way to do is that first declare #name . Then set this value based on user id and then if #name is null show default name otherwise show name... That method would be as efficient as any other method and will be more readable.for other methods any other user to have think a lot to know what is going on unless there is nice comment.
declare #userid int
set #userid = 1
select isnull(
(select name from users where id = #userid),
'John Doe'
)
go
--My preffered would be this one..
declare #name varchar(20),#userid int
set #userid = 1
select #name = name from users where id = #userid
select isnull(#name,'John Doe')
If the query is supposed to return at most one row, use a union with the default value then a limit:
SELECT * FROM
(
SELECT Name FROM Users WHERE Id = #UserId`
UNION
SELECT 'John Doe' AS Name --Default value
) AS subquery
LIMIT 1
Both the query and default can have as many columns as you wish, and you do not need to guarantee they are not null.

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