Return result of select query only if not empty - sql

Working with Sql Server. Writing a stored procedure. Here is the pseudocode for what I want to achieve:
IF EXISTS ( SELECT field1
FROM t1
WHERE field1 = ... AND field2 = ...)
BEGIN
SELECT field1
FROM t1
WHERE field1 = ... AND field2 = ...
END
any better way of doing this? Any help appreciated.
Chirayu
Update: The problem is that the same query is executed twice. I cannot also just the run query once and return null (if the result is null i would like to return an alternative result).

I have done this before using a CTE and table variable, it requires more lines of code but the query is only written once, therefore your logic exists in a single place.
DECLARE #Results TABLE (Result INT);
WITH ResultsCTE AS
(
--Your query goes here
SELECT 1 as Result
WHERE 1 = 1
)
INSERT INTO #Results
SELECT Result
FROM ResultsCTE
IF (SELECT COUNT(*) FROM #Results) > 0
BEGIN
SELECT * FROM #Results
END
ELSE BEGIN
SELECT 'Do Something Else or Do Nothing!'
END

You could check ##ROWCOUNT after running the query once to determine whether or not to return the value:
http://msdn.microsoft.com/en-us/library/ms187316.aspx

If the select doesn't yield any results, no results will be returned. I don't see any reason to use a condition here, unless I'm missing something...

A stored procedure that sometimes returns a result while sometimes it doesn't would be a nightmare to use from any API. The client side API has different entry points depending on whether you return a result set (SqlCommand.ExecuteReader) or it does not return a result set (SqlCommand.ExecuteNonQuery). It would be impossible for the application to know ahead of time which API to use! Modeling tools use the SET FMTONLY option to analyze the metadata of returned result sets and the modeling tools are very confused when your returned result set start changing shape at random. In other words, you are down the wrong path, stop and turn around.
Just run the query, it no rows match your criteria it will simply return an empty result set. Which is exactly what every client API and modeling tool expects from your procedure.

Related

Same query, different result after removing USE DatabaseName GO

My function GetProductDesc (when called) returns a different result after commenting out USE DatabaseName GO. I don't even know where to start debugging this. The pictures tell the story. I had to blur out a lot but you can see that the results are clearly different. Keep in mind that the pictures are not the function code, they are calling the function GetProductDesc
So strange. Any suggestions? I have an expert helping me later today but I had to share.
EDIT:
The function uses another lookup table in the same database. There is no Top or Order By clause. It calculates the product description based on the input components (numbers). It will return a different result if the input numbers are different, but here the input numbers are the same!
The function has been in place and working for over 5 years. I believe the problem started at about the time the version of SQL Server was updated recently.
EDIT 2 with partial answer:
The problem is caused by ##RowCount. It appears to be a breaking change caused by our recent migration to SQL Server 2019 although I haven't found the problem documented. The function returns a different product description based on ##RowCount following a Select statement. Internally the function does something like this:
SELECT Fields FROM Table WHERE Field = #Variable
IF ##Rowcount = 1
Return ProdDesc1
ELSE
Return ProdDesc2
After the SQL Server migration ##RowCount here was different depending on whether
USE DatabaseName
GO
was present.
The solution was to replace ##Rowcount with a variable #RowCount. This new code works:
DECLARE #RowCount INT = 0
SELECT Fields, #RowCount = #RowCount + 1
FROM Table WHERE Field = #Variable
IF #RowCount = 1
Return ProdDesc1
ELSE
Return ProdDesc2
If you have SQL Server 2019 installed try this to recreate the problem:
USE Master
GO
Select ##ROWCOUNT
The result here is ##ROWCOUNT = 0
Now comment out the two top lines:
--USE Master
--GO
Select ##ROWCOUNT
The result is now ##ROWCOUNT = 1
Anybody know why?
There is a SQL Server 2019 cumulative update from Microsoft that fixes this problem.

Re-use Query Parts in T-SQL

I have a very complicated stored procedure that repeats a very complicated query with different where clauses based on certain values passed in. The stored procedure takes up over 500 lines of code, with the common part of the query taking up just over 100 lines. That common part is repeated 3 times.
I originally thought to use CTE (Common Table Expressions) except in T-SQL you can't define the common part, do your IF statement and then apply the WHERE clause. That's essentially what I need.
As a workaround I created a view for the common code, but it's only used in one stored procedure.
Is there any way to do this without creating a full view or temp tables?
Ideally I would like to do something like this:
WITH SummaryCTE (col1, col2, col3...)
AS
(
SELECT concat("Pending Attachments - ", ifnull(countCol1, 0)) col1
-- all the rest of the stuff
FROM x as y
LEFT JOIN attachments z on z.attachmentId = x.attachmentId
-- and more complicated stuff
)
IF (#originatorId = #userId)
BEGIN
SELECT * FROM SummaryCTE
WHERE
-- handle this security case
END
ELSE IF (#anotherCondition = 1)
BEGIN
SELECT * FROM SummaryCTE
WHERE
-- a different where clause
END
ELSE
BEGIN
SELECT * FROM SummaryCTE
WHERE
-- the generic case
END
Hopefully the pseudo code gives you an idea of what I would like. Right now my workaround is to create a view for the contents of what I defined SummaryCTE as, and then handle the IF/ELSE IF/ELSE clause. Executing this structure will throw an error at the first IF statement because the next command is supposed to be a SELECT instead. At least in T-SQL.
Maybe this doesn't exist in any other way, but I wanted to know for sure.
Well, aside from the temp tables and views that you've identified, you could go with dynamic SQL to build the code then execute it. This keeps you from having to repeat code, but makes it a bit hard to just deal with. Like this:
declare #sql varchar(max) = 'with myCTE (col1, col2) as ( select * from myTable) select * from myCTE'
if (#myVar = 1)
begin
#sql = #sql + ' where col1 = 2'
end
else if (#myVar = 2)
begin
#sql = #sql + ' where col2 = 4'
end
-- ...
exec #sql
Another option would be to incorporate your different where clauses into the original query.
WITH SummaryCTE (col1, col2, col3...)
AS
(
SELECT concat("Pending Attachments - ", ifnull(countCol1, 0)) col1
-- all the rest of the stuff
FROM x as y
LEFT JOIN attachments z on z.attachmentId = x.attachmentId
-- and more complicated stuff
)
select *
from SummaryCTE
where
(
-- this was your first if
#originatorId = #userId
and ( whatever you do for your security case )
)
or
(
-- this was your second branch
#anotherCondition = 1
and ( handle another case here )
)
or
-- etc. etc.
This eliminates the if/else chain but makes the query more complicated. It also can cause some bad cached query plans because of parameter sniffing, but that may not matter much depending on your data. Test that before making a decision. (You can also add optimizer hints to not cache the query plan. You won't get a bad one, but you also take a hit on every execution to create the query plan again. Test to find out, don't guess. Also, a solution with a view and the if/else chain will suffer from the same parameter sniffing/cached query plan problem.)

SQL Set variable to select result

I was wondering if it is possible to set a declared variable to a return value from a select result? Something like:
#WatchedSeconds
SET #WatchedSeconds = 200
DECLARE #SelectedVideo int
SET #SelectedVideo = (SELECT TOP 1 * FROM Video v WHERE v.VideoID = 12)
IF #SelectedVideo IS NOT NULL
BEGIN
IF #SelectedVideo.VideoLength = #WatchedSeconds
BEGIN
--DO SOMETHING
END
IF #SelectedVideo.SomeOtherColumn = #SomethingElse
BEGIN
END
END
It's for using some information from the SELECT result multiple places in a Stored Procedure.
I know that I can set a variable to e.g, a integer, and set it to the selected result, if it returns a integer, e.g:
DECLARE #VideoSeconds int
SET #VideoSeconds = (SELECT v.Length FROM Video v WHERE v.VideoID = #VideoID)
This way I have to make multiple variables, and multiple SELECT calls if I need to use more values from the Video result. And that's what I want to avoid.
You can do this simply by running:
SELECT #videoSeconds = v.Length FROM Video v WHERE v.VideoID = #VideoID
so as to not add the SET part.
Also, you must make sure that only 1 row is being returned by the query, otherwise it will generate an error.
You can try something like
(declare variables first...)
SELECT TOP 1 #var1=col1, #var2=col2, #var3=col3, [...] FROM YourTable WHERE YourFilter
EDIT: All together this seems not to be the best approach... With SQL you should not think in values and single rows but rather in result sets (set based programming). Your thinking leads to many tiny selects, while loops, cursors and all this stuff one should avoid.
You can store the results in a temporary table or table variable:
SELECT TOP 1 *
INTO #SelectedVideo
FROM Video v
WHERE v.VideoID = 12;
Then you can assign values from the table later in your code. Something like:
IF ( (SELECT VideoLength FROM #SelectedVideo) = #WatchedSeconds)
However, for your particular example, if you have an index on video(VideoId), then there is little to be gained performance-wise from using a temporary table.
If what you're trying to get is similar to returning a dataset in a procedural language (so you can type something like Result.Field1 = 'Test') then I don't think this is possible. You'll just need to declare multiple variables and make the SELECT call as
SELECT TOP 1 #var1=col1, #var2=col2, #var3=col3, [...] FROM YourTable WHERE YourFilter
as #Shnugo suggests
The 'dataset' equivalent structure in SQL is cursors, but they require variables to be set up as well, so there's no benefit there.

How to use If/Else or Case statement for a checkbox with in a Select Query in SQL

I have a check box on front end.
If the check box is checked: data with only checked chk1 should appear on front end.
If the check box is not checked: full data should appear on front end.
Please suggest how should I proceed with the same in SQL Using If else / Case statement.
I am using:
SELECT *
FROM table1
where (some conditions) AND
CASE #a.Id_Oem_Irm
WHEN 0 THEN (a.id_oem_irm in(0,1))
WHEN 1 THEN (a.id_oem_irm in (1))
END
PS: a.Id_Oem_Irm: a is the table name, Id_oem_irm is the column name for check box.
I would recommend writing this as:
where (some conditions) AND
((#a.Id_Oem_Irm = 0 and a.id_oem_irm in(0, 1)) OR
(#a.Id_Oem_Irm = 1 and a.id_oem_irm in (1) )
)
I am not sure what #a.Id_Oem_Irm is supposed to be. I suspect you want a variable there.
Or you could tune it a bit like this:
SELECT *
FROM table1
where (some conditions) AND
(a.id_oem_irm = 1 OR
#a.Id_Oem_Irm = 0)
This is only valid if #a.Id_Oem_Irm is always 0 or 1. Otherwise, you obviously should add a #a.Id_Oem_Irm IN (0,1) or <= 1 (if it can't be negative) condition.
What is a? The alias for another table you didn't include here?
The simpler way to do this is create a storedprocedure which will take the input "#a.Id_Oem_Irm"
Example:
CREATE PROCEDURE p_get_a_data(
IN Id INT(1)
)
BEGIN
IF (Id = 0) BEGIN
--YOur query to retrieve partiular row/ column
END;
IF(Id = 1) BEGIN
--YOur query to retrieve all data
SELECT * FROM table1
where (some conditions)
END;
END
The usage of stored procedure will give you selected data and the query is precompiled in the database so it faster.
Note: I didn't quite understand what your query is returning so replace mine with yours

How to use IF Else in store procedure?

I am trying to execute Select query with ID parameter, now if ID is empty then I want All Rows, otherwise only row which contains this ID,
So far, I have created this Store Procedure,
IF(#CustId = null)
Begin
Select * from tblCustomer
End
Else
Begin
Select * from tblCustomer where custID=#custID
End
When I am executing this query with ID, I am getting result but when I am passing, I am not getting any result. What's correct way to do this?
Null is handled differently than other values, you need to use
IF (#CustId IS NULL)
Your current code of IF(#CustId = null) will always evaluate to False, so the else case will be executed regardless of what you pass for #CustId. This article goes into more detail about how to handle NULL values: