stored procedure with if else sql server 2008 - sql

I have 3 tables, I have to check if grandparent table id has records in grandchildren table. If yes, return yes, else return no. Here is my stored procedure. I got an incorrect syntax error. I am new to stored procedure. Please help me.
CREATE PROCEDURE P_Check
#PKG_ID INT,
#S_ID INT,
#FLAG VCHAR(10) OUT
DECLARE IDS CURSOR LOCAL FOR SELECT S_ID FROM T1 WHERE P_ID = #PKG_ID
OPEN IDS
FETCH NEXT FROM IDS into #S_ID
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT * FROM T2 WHERE S_ID = #S_ID
IF ##ROWCOUNT<>0
#FLAG = 'YES'
RETURN
ELSE
#FLAG = 'NO'
FETCH NEXT FROM IDS into #S_ID
END
CLOSE IDS
DEALLOCATE IDS

A few things to check:
I don't think there is a vchar data type in SQL Server unless that is your custom type. So change it to varchar
You forgot the AS
You might want to enclose your logic inside if in between begin and end
Your code that can be compiled:
CREATE PROCEDURE P_Check
#PKG_ID INT,
#S_ID INT,
#FLAG VARCHAR(10) OUT
AS
DECLARE IDS CURSOR LOCAL FOR SELECT S_ID FROM T1 WHERE P_ID = #PKG_ID
OPEN IDS
FETCH NEXT FROM IDS into #S_ID
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT * FROM T2 WHERE S_ID = #S_ID
IF ##ROWCOUNT<>0
BEGIN
SET #FLAG = 'YES'
RETURN
END
ELSE
BEGIN
SET #FLAG = 'NO'
FETCH NEXT FROM IDS into #S_ID
END
END
CLOSE IDS
DEALLOCATE IDS
However, I think your cursor will not be closed as you are returning here IF ##ROWCOUNT<>0. I think what you should do is change this:
IF ##ROWCOUNT<>0
BEGIN
SET #FLAG = 'YES'
RETURN
END
to this:
IF ##ROWCOUNT<>0
BEGIN
SET #FLAG = 'YES'
GOTO ON_EXIT
END
then end your procedure like this:
ON_EXIT:
CLOSE IDS
DEALLOCATE IDS
Then to answer your question in the comment, you are "returning" it already in a sense. You can call and test your procedure like this:
declare #result varchar(10)
exec P_Check 1, 1, #result out
print #result

This is just way too complicated and using a cursor here is totally not needed and totally unnecessary.
Simplify your procedure to be:
CREATE PROCEDURE P_Check
#PKG_ID INT,
#S_ID INT,
#FLAG CHAR(1) OUT
AS BEGIN
IF EXISTS (SELECT * FROM T2
INNER JOIN T1 ON T2.S_ID = T1.S_ID WHERE P_ID = #PKG_ID)
SET #FLAG = 'Y'
ELSE
SET #FLAG = 'N'
END
When working seriously with SQL Server, you need to get away from the procedural row-by-agonizing-row thinking using cursors and loops, and you need to start thinking in sets to be efficient and productive.

Related

How to use results from an SQL Query as a list to delete (WSUS) updates

My problem is that I want to use the results from a SELECT query as the input values for a Stored Procedure. The issue is that the SP will only accept Scalar values, and I do not know SQL and so have been struggling to find a workaround or solution.
I want to modify the following Proc to accept multiple values to be used within the query:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[spDeleteUpdateByUpdateID]
#updateID UNIQUEIDENTIFIER
AS
SET NOCOUNT ON
DECLARE #localUpdateID INT
SET #localUpdateID = NULL
SELECT #localUpdateID = LocalUpdateID FROM dbo.tbUpdate WHERE UpdateID = #updateID
IF #localUpdateID IS NULL
BEGIN
RAISERROR('The update could not be found.', 16, 40)
RETURN(1)
END
IF EXISTS (SELECT r.RevisionID FROM dbo.tbRevision r
WHERE r.LocalUpdateID = #localUpdateID
AND (EXISTS (SELECT * FROM dbo.tbBundleDependency WHERE BundledRevisionID = r.RevisionID)
OR EXISTS (SELECT * FROM dbo.tbPrerequisiteDependency WHERE PrerequisiteRevisionID = r.RevisionID)))
BEGIN
RAISERROR('The update cannot be deleted as it is still referenced by other update(s).', 16, 45)
RETURN(1)
END
DECLARE #retcode INT
EXEC #retcode = dbo.spDeleteUpdate #localUpdateID
IF ##ERROR <> 0 OR #retcode <> 0
BEGIN
RAISERROR('spDeleteUpdateByUpdateID got error from spDeleteUpdate', 16, -1)
RETURN(1)
END
RETURN (0)
TLDR: if anyone knows a quick way for me to use the results from SELECT UpdateID FROM tbUpdate WHERE UpdateTypeID = 'D2CB599A-FA9F-4AE9-B346-94AD54EE0629' to run exec spDeleteUpdateByUpdateID #updateID= i'd be extremely grateful.
There are some examples online of people using cursors to clean up WSUS. It will be slow but you are presumably only running it once. As mentioned there are other strategies for WSUS cleanup that should probably be investigated first.
DECLARE #var1 INT
DECLARE #msg nvarchar(100)
-- Get obsolete updates into temporary table
-- insert your own ID's here if you wish
CREATE TABLE #results (Col1 INT)
INSERT INTO #results(Col1) EXEC spGetObsoleteUpdatesToCleanup
DECLARE WC Cursor
FOR SELECT Col1 FROM #results
OPEN WC
FETCH NEXT FROM WC INTO #var1
WHILE (##FETCH_STATUS > -1)
BEGIN
SET #msg = 'Deleting' + CONVERT(varchar(10), #var1)
RAISERROR(#msg,0,1) WITH NOWAIT EXEC spDeleteUpdateByUpdateId #var1
FETCH NEXT FROM WC INTO #var1
END
CLOSE WC
DEALLOCATE WC
DROP TABLE #results

What's better to optimize a database: use a bunch of stored procedures with different filters or use a single one with if conditions?

I'm trying to optimize my database so, I decided to change a stored procedure which has a lot of "IF CONDITIONS" to filter with different parameters.
So, I was thinking on change it and make a bunch of stored procedures with different filters and stop using the one with a lot of "IF CONDITIONS"
And just validate on my backend which stored procedure use, instead of do it in SQL Server.
This is the best way to do it or there are better ways to do this?
Example of my actual stored procedure:
CREATE PROCEDURE [dbo].[sp_GetFilters]
#Id INT,
#Name VARCHAR(50),
#LastName VARCHAR(50),
#Age INT,
#AND_MORE_PARAMETERS VARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
IF #ID IS NOT NULL AND
#NAME IS NULL AND
#LASTNAME IS NULL AND
#AGE IS NULL AND
#AND_MORE_PARAMETERS IS NULL
BEGIN
SELECT *
FROM EXAMPLE_TABLE
WHERE ID = #ID
END
ELSE IF #ID IS NULL AND
#NAME IS NOT NULL AND
#LASTNAME IS NULL AND
#AGE IS NULL AND
#AND_MORE_PARAMETERS IS NULL
BEGIN
SELECT *
FROM EXAMPLE_TABLE WHERE
NAME = #NAME
END
...
and so on..
I want to change that one to a bunch of different stored procedures, each one of them with a unique filter
It is a good idea? There are better ways to optimize this stored procedure?
Thanks, for the help!
You could try one query, with parameter evaluation in the WHERE clause. I've used this approach widely with great success.
CREATE PROCEDURE dbo.sp_GetFilters
#Id INT = NULL
,#Name VARCHAR(50) = NULL
,#LastName VARCHAR(50) = NULL
,#Age INT = NULL
,#AND_MORE_PARAMETERS VARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
SELECT *
FROM EXAMPLE_TABLE
WHERE 1 = 1
AND CASE WHEN #Id IS NULL THEN 1 ELSE ID END = CASE WHEN #Id IS NULL THEN 1 ELSE #Id END
AND CASE WHEN #Name IS NULL THEN '1' ELSE NAME END = CASE WHEN #Name IS NULL THEN '1' ELSE #Name END
AND CASE WHEN #Age IS NULL THEN 1 ELSE AGE END = CASE WHEN #Age IS NULL THEN 1 ELSE #Age END;
/* and so on....*/
END;

Creating an Instead of insert Trigger SQL

I am a DBA with my company. I am trying to create trigger that will check any insert statement for duplicates first then if none allow the original insert. Not even sure this can be done. The insert statements may be written by various users so the statements will never be the same. All I have found so far is the check for duplicates but the insert statement is then hard coded in the trigger. My plan is also to check update as well, but it is not important right now.
Here is my current code.
ALTER TRIGGER [dbo].[BlockDuplicatesOnTable]
ON [dbo].[blockduplicates]
Instead of INSERT, Update
AS
BEGIN
SET NOCOUNT ON;
Declare #ProviderAcctNumber nvarchar(50)
Declare #Referredby int
Declare #Action as char(1)
Declare #Count as int
Set #Action = 'I'
Select #Count = Count(*) from DELETED
IF #Count > 0
Begin
Set #Action = 'D'
Select #Count = count(*) from INSERTED
IF #Count > 0
Set #Action = 'U'
IF #Action = 'I'
Begin
IF not exists (Select 1 from inserted as i
inner join dbo.blockduplicates as b
on i.ProviderAcctNumber = b.ProviderAcctNumber
and i.Referredby = b.Referredby)
Begin
--execute original insert
End
Else
Begin
Print 'Duplicate insert'
Raiserror ('Duplicate Entry for Insert',16,1);
Return
End
End
Else IF #Action = 'U'
Begin
Select #ProviderAcctNumber = ProviderAcctNumber, #Referredby = Referredby from inserted
IF Not exists (Select 1 from deleted where ProviderAcctNumber = #ProviderAcctNumber and Referredby = #Referredby)
Begin
Print 'Update Statement is True';
End
Else
Begin
Print 'duplicate'
Raiserror ('Duplicate Entry for Update',16,1);
Return
End
End
End
End;

Loop in stored procedure in SQL server

I need help with writing stored procedure that calls another stored procedure and passes values to it. So far this was done in C#, now I want to move it to stored procedure and make an SQL agent job that calls it at specific time. Any ideas? This is the case.
Table A:
PK_TableA_ID
Table B:
PK_TableB_ID
Stored procedure SP1:
#TableA_ID
#TableB_ID
I need this but in T-SQL
foreach(var TableAId in TableA)
{
foreach(var TableBId in TableB)
{
//call stored procedure
SP1(TableAId, TableBId);
}
}
Here's an example of how you can use cursors to do loops:
-- set up some test data
declare #table_a table (PK_TableA_ID int)
declare #table_b table (PK_TableB_ID int)
insert #table_a values (1),(2),(3)
insert #table_b values (4),(5),(6)
-- do the actual processing
SET NOCOUNT ON
DECLARE #TableA_ID int, #TableB_ID int
DECLARE TableA_cursor CURSOR FOR SELECT PK_TableA_ID FROM #table_a
OPEN TableA_cursor
FETCH NEXT FROM TableA_cursor INTO #TableA_ID
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE TableB_cursor CURSOR FOR SELECT PK_TableB_ID FROM #table_b
OPEN TableB_cursor
FETCH NEXT FROM TableB_cursor INTO #TableB_ID
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT CAST(#TableA_ID AS CHAR(1)) + ':' + CAST(#TableB_ID AS CHAR(1))
-- execute your stored procedure here:
-- EXEC Your_stored_procedure (#TableA_ID, #TableB_ID)
FETCH NEXT FROM TableB_cursor INTO #TableB_ID
END
CLOSE TableB_cursor
DEALLOCATE TableB_cursor
FETCH NEXT FROM TableA_cursor INTO #TableA_ID
END
CLOSE TableA_cursor
DEALLOCATE TableA_cursor
The cursor above (with the test data in the temporary tables) will generate this output:
1:4
1:5
1:6
2:4
2:5
2:6
3:4
3:5
3:6
Using cursors might not be the best way to solve your problem though.
I have a clean and clear option without cursors for this case using the table ids.
DECLARE
#Counter1 INT,#MaxId1 INT,
#Counter2 INT, #MaxId2 INT
SELECT #Counter1 = min(PK_TableA_ID) , #MaxId1 = max(PK_TableA_ID)
FROM TableA
SELECT #Counter2 = min(PK_TableB_ID) , #MaxId2 = max(PK_TableB_ID)
FROM TableB
WHILE(#Counter1 IS NOT NULL AND #Counter1 <= #MaxId1)
BEGIN
WHILE(#Counter2 IS NOT NULL AND #Counter2 <= #MaxId2)
BEGIN
//call stored procedure
SP1(#Counter1, #Counter2);
SET #Counter2 = #Counter2 + 1
END;
SELECT #Counter2 = min(PK_TableB_ID) , #MaxId2 = max(PK_TableB_ID)
FROM TableB
SET #Counter1 = #Counter1 + 1
END;

Stored Procedure OutPut Parameters

i need to write a stored procedure which will return a string.logic is
when user try to insert a new record i need to check whether that record already exist.if exist need to return msg "Record exist" else return "Inserted"
following is what i have done for the moment and i'm stuck here.can some one help me to complete the procedure
CREATE PROCEDURE [dbo].[spInsetPurpose]
#Purpose VARCHAR(500),
#Type VARCHAR(6),
#Result VARCHAR(10)= NULL OUTPUT
AS
BEGIN
Declare #Position VARCHAR(20)
DECLARE #TempTable TABLE (Purpose VARCHAR(500))
INSERT INTO #TempTable
SELECT Purpose FROM tblPurpose WHERE Purpose=#Purpose
INSERT INTO tblPurpose(Purpose,[Type]) VALUES(#Purpose,#Type)
END
To check if the row already exists you can do
If Exists (Select Top 1 1 from tblPurpose where Purpose = #Purpose and [Type] = #Type)
Begin
Insert Into tblPurpose
(Purpose, [Type])
Select
#Purpose, #Type
SET #Result = 'Inserted'
End
Else
Begin
SET #Result = 'Record exists'
End