SQL Server: stored procedure and send dbmail - sql-server

The reason of creating a stored procedure is to schedule a job to send a biweekly report to our staff (coordinators) using SQL Server db mail.
I'm having problems with getting it to work the right way. I don't usually work with cursors but couldn't find other choices.
Here's the issue. I tested the query by set criteria to send only to one Coordinator with one record "if #Coord_Email = 'lamez.sw1#gmail.com', where n.id = '43422546'". However the query been running over 5 minutes so i had to cancel it.
ALTER PROCEDURE [dbo].[sp_MZ_Coord_rpt_s9]
AS
BEGIN
DECLARE #Member_ID VARCHAR(20)
DECLARE Report_S9 CURSOR FOR
SELECT id
FROM name
WHERE status = 'a'
OPEN Report_S9
FETCH NEXT FROM Report_S9 INTO #member_ID
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #Coord_ID Varchar(20)
DECLARE #CO_ID Varchar(20)
DECLARE #Coord_Name Varchar(50)
DECLARE #Coord_Email Varchar(50)
SELECT #CO_ID = ID
FROM Relationship
WHERE id = #Member_ID
SELECT #Coord_ID = target_id
FROM Relationship
WHERE RELATION_TYPE = 'CO'
SELECT #Coord_Name = FULL_NAME
FROM Name
WHERE ID = #Coord_ID
SELECT #Coord_Email = email
FROM Name
WHERE id = #Coord_ID
IF #Coord_Email <> ''
BEGIN
SELECT
n.id, n.CO_ID, n.FULL_NAME, a.TRANSACTION_DATE, a.UF_1, r.TARGET_ID
FROM name n
INNER JOIN activity a ON n.id = a.id
INNER JOIN Tops_Profile tp ON a.id = tp.ID
INNER JOIN Relationship r ON n.CO_ID = r.ID
WHERE
n.id = #member
AND UF_1 <> ''
AND (DATEDIFF(dd, TRANSACTION_DATE, GETDATE()) < 2)
AND r.RELATION_TYPE = 'co'
ORDER BY
TRANSACTION_DATE
EXEC msdb..sp_send_dbmail
#profile_name = 'TOPS.ADMIN',
#recipients = #Coord_Email,
--#blind_copy_recipients = ,
#subject = 'S9 Report'
End
FETCH NEXT FROM Report_S9 INTO #member_ID
END
CLOSE Report_S9
DEALLOCAT Report_S9
End
Any help is greatly appreciated.

The FETCH NEXT should be outside of your check for null. You need to continue the loop, even when there is nothing to do.

Related

SQL Server - update multiple records using a stored procedure

Being a super novice at this, I would like some guidance on this, please.
I need to compare two sets of data and update one set with a value. This is what I have so far.
PROCEDURE [dbo].[update_personnel_rank]
AS
DECLARE #frsid VARCHAR
DECLARE #officerid VARCHAR
DECLARE #hrrank VARCHAR
DECLARE #personnelrank VARCHAR
DECLARE #farank VARCHAR
DECLARE #rank VARCHAR(150)
SET #rank = 'Admin Spec II'
BEGIN
SET NOCOUNT ON;
SELECT
#frsid = hr.FRSID,
#officerid = p.OfficerID,
#hrrank = hr.Rank,
#personnelrank = p.Rank,
#farank = r.FA_Rank
FROM
[FireApp_REPL_DW_Data].[dbo].[MCFRSCombinedPersonnelandPimsStaff] hr
INNER JOIN
[fh_reports].[dbo].[personnel_bk] p ON p.OfficerID = hr.FRSID
INNER JOIN
[fh_reports].[dbo].[Rank_Lookup_tbl] r ON r.FA_Rank = hr.Rank
WHERE
(p.rank <> hr.Rank OR p.rank = '')
AND p.Rank = #rank
UPDATE [fh_reports].[dbo].[personnel_bk]
SET Rank = #farank
WHERE OfficerID = #officerid
END
GO
The select query returns 3 records and this stored procedure runs without any error, but it does not update the records. Since the select query returns 3 records, I think I need to change the parameter setting accordingly, but not sure how...
To #Sami's point, if you are not returning those variables, you do not need to set them and can instead just run the update:
USE [YourDatabase]
GO
SET NOCOUNT ON
GO
ALTER PROCEDURE [dbo].[update_personnel_rank]
#rank VARCHAR(150) --= 'Admin Spec II'
AS
BEGIN
IF #rank IS NULL OR #rank = ''
RAISERROR('Please enter a valid rank string.', 16, 1)
UPDATE hr
SET [Rank] = r.FA_Rank
FROM [FireApp_REPL_DW_Data].[dbo].[MCFRSCombinedPersonnelandPimsStaff] [hr]
INNER JOIN [fh_reports].[dbo].[personnel_bk] [p]
ON [p].[OfficerID] = [hr].[FRSID]
INNER JOIN [fh_reports].[dbo].[Rank_Lookup_tbl] [r]
ON [r].[FA_Rank] = [hr].[Rank]
WHERE [p].[rank] <> [hr].[Rank]
AND ([p].[Rank] = #rank OR p.[Rank] = '')
END ;
GO

Running tsqlt assert inside a cursor

I'm writing tsqlt against a proc that can be run against various parameter values. I initially built a proc that populates fake tables - and then 1 tsqlt test per possible value (ended up with 35 tests and they each worked).
What I would like to do is reduce these into 1 test (since they are all really testing the same functionality - just for different values). I thought I could do this with a cursor like so:
---- Declare Sproc Variables
DECLARE #ReviewId INT;
DECLARE #SourceId INT = 1;
CREATE TABLE #Present
(
SubmissionReviewId INT ,
username VARCHAR(50)
);
CREATE TABLE #Expected
(
SubmissionReviewId INT ,
username VARCHAR(50)
);
--Create Cursor to loop through each active value
DECLARE review_id CURSOR
FOR
SELECT ReviewId
FROM reftype.Rev
WHERE IsActive = 1;
OPEN review_id;
FETCH NEXT FROM review_id
INTO #ReviewId;
WHILE ##FETCH_STATUS = 0
BEGIN
--Setup Fake Data according to the specified test condition
EXEC ut_DataSetupProc #ReviewId = #ReviewId;
-- Run set cutover Sproc
EXEC Procbeing Tested #ReviewId = #ReviewId,
#SourceId = 1, #Username = 'blah';
-- Confirm appropriate review is present in Submission Review Active
DELETE FROM #Present;
DELETE FROM #Expected;
INSERT INTO #Present
SELECT SubmissionReviewId ,
LastModifiedBy
FROM review.SubmissionReviewActive
ORDER BY SubmissionReviewId ,
LastModifiedBy;
/**********************Create table holding expected values***************************/
INSERT INTO #Expected
--This confirms active reviews that belong to other sections/sources remain unaffected
SELECT SubmissionReviewId ,
LastModifiedBy
FROM review.SubmissionReviewActive
WHERE ( ReviewId != #ReviewId )
OR ( SourceId != #SourceId )
UNION
SELECT sra.SubmissionReviewId ,
sra.LastModifiedBy
FROM review.SubmissionReviewActive sra
JOIN review.SubmissionReviewFutureActive srfa ON srfa.IssuerId = sra.IssuerId
AND srfa.ReviewId = sra.ReviewId
AND srfa.Version < sra.Version
WHERE sra.ReviewId = #ReviewId
AND sra.SourceId = #SourceId
UNION
SELECT srfa.SubmissionReviewId ,
'jmarina' AS LastModifiedBy
FROM review.SubmissionReviewFutureActive srfa
JOIN review.SubmissionReviewActive sra ON srfa.IssuerId = sra.IssuerId
AND srfa.ReviewId = sra.ReviewId
AND srfa.Version > sra.Version
WHERE sra.ReviewId = #ReviewId
AND srfa.SourceId = #SourceId
UNION
SELECT srfa.SubmissionReviewId ,
'blah' AS LastModifiedBy
FROM review.SubmissionReviewFutureActive srfa
WHERE srfa.ReviewId = #ReviewId
AND srfa.SourceId = #SourceId
AND srfa.IssuerId NOT IN (
SELECT IssuerId
FROM review.SubmissionReviewActive
WHERE ReviewId = #ReviewId
AND SourceId = #SourceId )
UNION
SELECT sra.SubmissionReviewId ,
sra.LastModifiedBy
FROM review.SubmissionReviewActive sra
WHERE sra.ReviewId = #ReviewId
AND sra.SourceId = #SourceId
AND IssuerId NOT IN (
SELECT IssuerId
FROM review.SubmissionReviewFutureActive
WHERE ReviewId = #ReviewId
AND SourceId = #SourceId )
ORDER BY SubmissionReviewId ,
LastModifiedBy;
/*************************************************************/
EXEC tSQLt.AssertEqualsTable #Expected = '#Expected',
#Actual = '#Present', #Message = N'', -- nvarchar(max)
#FailMsg = N'Active Status is not a match'; -- nvarchar(max)
FETCH NEXT FROM review_id
INTO #ReviewId;
END;
CLOSE review_id;
DEALLOCATE review_id;
DROP TABLE #Expected;
DROP TABLE #Present;
END;
However, running this using
EXEC proc name #ReviewId = #ReviewId;
yields a message saying no tests were run. How can I sue a cursor to reduce my number of tests? Or is there another approach I should consider?
I'd suggest you write something called a parameterized test.
tSQLt does not (yet) have native support for that, but there is an easy workaround:
You start by writing one of your tests normally. But instead of hardcoding the pertinent values, you make them parameters of the procedure. (For data sets, you can use table parameters.)
You also name that procedure something that doesn't start with "test" (but lives in the same schema).
Then you write one real test per actual case, each one consisting of one line: the execution of your parameterized procedure.
That will lead to tests that are a lot easier to understand than your current approach. And additionally, if one of them fails, you immediately know which.
As a side note: You always want to hardcode your expected results. Your current code is way complex. You want to minimize things that can go wrong in the test itself. Really, your goal should be tests that can be understood with one glance.
In the end I achieved the end goal in a couple of steps:
1. Move the assert statement outside of the cursor
2. Created 'cased' temp table with pass/fail records
INSERT INTO #ActualAssert
SELECT p.SubmissionReviewId,e.SubmissionReviewId,
CASE WHEN ( e.SubmissionReviewId IS NULL
OR p.SubmissionReviewId IS NULL
) THEN 'Fail'
ELSE 'Pass'
END
FROM #Present p
LEFT JOIN #Expected e ON e.SubmissionReviewId = p.SubmissionReviewId
UNION
SELECT p.SubmissionReviewId,e.SubmissionReviewId ,
CASE WHEN ( e.SubmissionReviewId IS NULL
OR p.SubmissionReviewId IS NULL
) THEN 'Fail'
ELSE 'Pass'
END
FROM #Present p
RIGHT JOIN #Expected e ON e.SubmissionReviewId = p.SubmissionReviewId;
3. Outside of the cursor I set up a new parameter that takes any fails if they exist or 'pass' if they don't
SET #Result = ( SELECT DISTINCT TOP 1
TestStatus
FROM #ActualAssert
ORDER BY TestStatus ASC
);
4. Then I modified the assert to fail if #result is anything other than 'Pass'
EXEC tSQLt.AssertEqualsString #Expected = N'Pass', -- nvarchar(max)
#Actual = #Result, #Message = N''; -- nvarchar(max)
** A note I change previous present and expected temp tables into variable tables

Same query, same database, different server, different result

We have our main database on a server, there is this stored procedure; when we run it against the database, it returns wrong values.
But when I take a back up of this database and restore it on another server and run the exact same query, it returns the correct answer.
What can I do?
Is it possible that the configuration of SQL Server affects how a query returns results?
If yes where can I start looking for problem ?
Here is the stored procedure, the exact same procedure runs on both databases and both databases are identical.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[S_GheymatGozaryFIFOFroosh]
#AYear SMALLINT,
#LDate CHAR(8),
#OdCd VARCHAR(17),
#FromFirst BIT,
#SCd TINYINT
AS
DECLARE #LHId Int, #LHRadif SmallInt,
#LHFact_Date CHAR(8), #LHFact_No INT,
#LHStock_Cd TinyInt, #LQnt_Resid DECIMAL(18,4),
#LPrc_Resid DECIMAL(30,8)
DECLARE #LRId INT, #LRRadif SmallInt,
#LRFact_Date CHAR(8), #LRFact_No INT,
#LRStock_Cd TinyInt
DECLARE #Kind_Cd TINYINT, #StartDate CHAR(8)
DECLARE #Cnt INT
SET #Cnt = 0
IF #ldate IS NOT NULL AND #FromFirst = 1
BEGIN
DELETE FROM S_Fifo_Gheymat
WHERE (Acc_Year = #Ayear)
AND (#SCd = 0 OR H_Stock_Cd = #SCd)
AND (Od_Cd = #OdCd)
END
IF #SCd = 0
SET #Kind_Cd = 2
ELSE
SET #Kind_Cd = 1
SET #StartDate = Right(CAST(#AYear AS VARCHAR(4)), 2) + '/01/01'
SELECT
#LHId = H_Id,
#LHRadif = H_Radif,
#LHFact_Date = H_Fact_Date,
#LHFact_No = H_Fact_No,
#LHStock_Cd = H_Stock_Cd,
#LQnt_Resid = Qnt_Resid,
#LPrc_Resid = Prc_Resid,
#LRId = R_Id,
#LRRadif = R_Radif,
#LRFact_Date = R_Fact_Date,
#LRFact_No = R_Fact_No,
#LRStock_Cd = R_Stock_Cd
FROM
S_Fifo_Gheymat
WHERE
Acc_Year = #AYear
AND Od_Cd = #OdCd
AND (#SCd = 0 OR H_Stock_Cd = #SCd)
AND EXISTS (SELECT Id
FROM S_Dtl_Fct
WHERE Id = H_Id
AND Radif = H_Radif
AND Stock_Cd = H_Stock_Cd
AND Od_Cd = S_Fifo_Gheymat.Od_Cd)
AND EXISTS (SELECT Id
FROM S_Dtl_Fct
WHERE Id = R_Id
AND Radif = R_Radif
AND Stock_Cd = R_Stock_Cd
AND Od_Cd = S_Fifo_Gheymat.Od_Cd)
SELECT #LHId=ISNULL(#LHId,0),#LHRadif=IsNull(#LHRadif,0),#LHFact_Date=IsNull
(#LHFact_Date,#StartDate),#LHFact_No=IsNull(#LHFact_No,0),#LHStock_Cd=ISNULL
(#LHStock_Cd,0)
,#LQnt_Resid=ISNULL(#LQnt_Resid,0),#LPrc_Resid=ISNULL(#LPrc_Resid,0)
,#LRId=ISNULL(#LRId,0),#LRRadif=IsNull(#LRRadif,0),#LRFact_Date=IsNull
(#LRFact_Date,#StartDate),#LRFact_No=IsNull(#LRFact_No,0),#LRStock_Cd=ISNULL
(#LRStock_Cd,0)
---------------------------------------
IF #LDate IS NULL BEGIN
SELECT TOP 1 #LDate=Fact_Date
FROM S_Dtl_Fct D
LEFT OUTER JOIN S_Hed_Fct H ON D.Id=H.Id
LEFT OUTER JOIN dbo.S_STOCKS S ON D.Stock_Cd=S.Stock_Cd
LEFT OUTER JOIN U_Log U ON H.Id_Log=U.Id_Log AND U.Action_Cd=5
WHERE (H.Acc_Year=#AYear) AND (H.Flag=6) AND (D.Od_Cd=#OdCd) AND
(H.Tamam=0) AND (#SCd<>0 OR S.Estesna_Gp=0)
AND (
(H.Fact_Date>#LHFact_Date)
OR (H.Fact_Date=#LHFact_Date AND
H.Fact_No>#LHFact_No)
OR (H.Fact_Date=#LHFact_Date AND
H.Fact_No=#LHFact_No AND D.Radif>#LHRadif)
OR (H.Fact_Date=#LHFact_Date AND
H.Fact_No=#LHFact_No AND D.Radif=#LHRadif AND D.Stock_Cd>#LHStock_Cd)
)
AND (#SCd=0 OR D.Stock_Cd=#SCd) AND (H.VAZEIAT<>2) AND
(U.Id_Log IS NOT NULL)
ORDER BY H.Fact_Date
End
DECLARE #H TABLE ( H_Id INT,H_Radif SMALLINT,H_Fact_Date CHAR
(8),H_Fact_No INT,H_Stock_Cd TINYINT,Quantity Decimal(18,4),Un_Prc
MONEY,HTamam Bit
,R_Id INT,R_Radif SMALLINT,R_Fact_Date
CHAR(8),R_Fact_No INT,R_Stock_Cd TINYINT,Qnt_Resid Decimal(18,2),Prc_Resid
Decimal(30,8))
INSERT INTO #H
(H_Id,H_Radif,H_Fact_Date,H_Fact_No,H_Stock_Cd,Quantity,HTamam)
SELECT D.Id,D.Radif,H.Fact_Date,H.Fact_No,D.Stock_Cd,D.Quantity,H.Tamam
FROM S_Dtl_Fct D
LEFT OUTER JOIN S_Hed_Fct H ON D.Id=H.Id
LEFT OUTER JOIN dbo.S_STOCKS S ON D.Stock_Cd=S.Stock_Cd
WHERE (H.Acc_Year=#AYear) AND (H.Flag=6) AND (D.Od_Cd=#OdCd) AND
(H.Fact_Date<=#LDate) AND (#SCd<>0 OR S.Estesna_Gp=0)
AND (
(H.Fact_Date>#LHFact_Date)
OR (H.Fact_Date=#LHFact_Date AND H.Fact_No>#LHFact_No)
OR (H.Fact_Date=#LHFact_Date AND H.Fact_No=#LHFact_No
AND D.Radif>#LHRadif)
OR (H.Fact_Date=#LHFact_Date AND H.Fact_No=#LHFact_No
AND D.Radif=#LHRadif AND D.Stock_Cd>#LHStock_Cd)
)
AND (#SCd=0 OR D.Stock_Cd=#SCd) AND (H.VAZEIAT<>2)
ORDER BY H.Fact_Date,H.Fact_No,D.Radif,D.Stock_Cd
Delete S_Related_RH FROM #H H LEFT OUTER JOIN S_Related_RH R ON
H.H_Id=R.H_Id AND H.H_Radif=R.H_Radif
------------------------------------------
DECLARE #HQnt DECIMAL(18,4),#HDate CHAR(8),#SumQ DECIMAL(18,4),#SumG
MONEY,#HQntWithPrc DECIMAL(18,4)
SET #SumG=#LQnt_Resid*#LPrc_Resid
SET #SumQ=#LQnt_Resid
--
DECLARE Cr CURSOR FOR SELECT Quantity,H_Fact_Date,H_Id,H_Radif FROM #H FOR
UPDATE OF Un_Prc
Open Cr
Fetch Next From Cr InTo #HQnt,#HDate,#LHId,#LHRadif
While (##Fetch_Status=0) AND (#LRId IS NOT NULL)
Begin
IF #HQnt<=#LQnt_Resid BEGIN
SET #LQnt_Resid=#LQnt_Resid-#HQnt
UPDATE #H SET
Un_Prc=#SumG/#SumQ,R_Id=#LRId,R_Radif=#LRRadif,R_Fact_Date=#LRFact_Date,
R_Fact_No=#LRFact_No,R_Stock_Cd=#LRStock_Cd
,Qnt_Resid=#LQnt_Resid,Prc_Resid=#LPrc_Resid
WHERE CURRENT OF Cr
IF #HQnt>0 BEGIN
INSERT INTO dbo.S_Related_RH
(H_Id,H_Radif,R_Id,R_Radif,Quantity)
VALUES (#LHId,#LHRadif,#LRId,#LRRadif,#HQnt)
END
SET #SumG=#LQnt_Resid*#LPrc_Resid
SET #SumQ=#LQnt_Resid
Fetch Next From Cr InTo #HQnt,#HDate,#LHId,#LHRadif
END ELSE BEGIN
IF #LQnt_Resid>0 BEGIN
INSERT INTO dbo.S_Related_RH
(H_Id,H_Radif,R_Id,R_Radif,Quantity)
VALUES (#LHId,#LHRadif,#LRId,#LRRadif,#LQnt_Resid)
END
SET #HQnt=#HQnt-#LQnt_Resid --مقدار باقیمانده حواله
SELECT TOP 1
#LRId=D.Id,#LRRadif=D.Radif,#LRFact_Date=H.Fact_Date,#LRFact_No=H.Fact_No,
#LRStock_Cd=D.Stock_Cd,#LQnt_Resid=D.QUANTITY
,#LPrc_Resid=CASE D.QUANTITY WHEN 0
THEN 0 ELSE ( (Un_Prc*D.QUANTITY)+ISNULL(Qnt_1,0) )/ D.QUANTITY END
FROM S_Dtl_Fct D
LEFT OUTER JOIN S_Hed_Fct H ON D.Id=H.Id
LEFT OUTER JOIN dbo.S_STOCKS S ON D.Stock_Cd=S.Stock_Cd
WHERE (H.Acc_Year=#AYear) AND (H.Flag=5) AND (D.Od_Cd=#OdCd)
AND (H.Fact_Date<=#HDate) AND (H.Tamam=1) AND (#SCd<>0 OR S.Estesna_Gp=0)
AND (
(H.Fact_Date>#LRFact_Date)
OR (H.Fact_Date=#LRFact_Date AND
H.Fact_No>#LRFact_No)
OR (H.Fact_Date=#LRFact_Date AND
H.Fact_No=#LRFact_No AND D.Radif>#LRRadif)
OR (H.Fact_Date=#LRFact_Date AND
H.Fact_No=#LRFact_No AND D.Radif=#LRRadif AND D.Stock_Cd>#LRStock_Cd)
)
AND (#SCd=0 OR D.Stock_Cd=#SCd) AND (H.VAZEIAT<>2)
ORDER BY H.Fact_Date,H.Fact_No,D.Radif,D.Stock_Cd
--
IF #LRId IS NOT NULL BEGIN
IF #HQnt<=#LQnt_Resid SET #HQntWithPrc=#HQnt ELSE SET
#HQntWithPrc=#LQnt_Resid
SET #SumG=#SumG+(#HQntWithPrc*#LPrc_Resid)
SET #SumQ=#SumQ+#HQntWithPrc
End
IF ISNULL(#LQnt_Resid,0)=0 Break
End
END
Close Cr
Deallocate Cr
DECLARE #E Int
SET #E=0
BEGIN TRAN
UPDATE D SET Un_Prc=G.Un_Prc
FROM S_Dtl_Fct D
INNER JOIN #H G ON D.Id=G.H_Id AND D.Radif=G.H_Radif
WHERE (G.HTamam=0) And (G.R_Id IS NOT NULL)
SET #Cnt=##ROWCOUNT
Set #E=#E+##Error
DELETE F FROM S_Fifo_Gheymat F
WHERE (Acc_Year=#Ayear) AND (#SCd=0 OR H_Stock_Cd=#SCd) AND
(Od_Cd=#OdCd)
And EXISTS (SELECT TOP 1 Od_Cd
FROM #H
WHERE (H_Stock_Cd=F.H_Stock_Cd) AND
(Od_Cd=#OdCd) AND (R_Id IS NOT NULL)
ORDER BY H_Fact_Date DESC ,H_Fact_No
DESC ,H_Radif DESC ,H_Stock_Cd DESC)
Set #E=#E+##Error
INSERT INTO S_Fifo_Gheymat
(Acc_Year,H_Stock_Cd,OD_CD,R_Stock_Cd,H_Id,H_Fact_Date,H_Fact_No,
H_Radif,R_Id,R_Fact_Date,R_Fact_No,R_Radif,Qnt_Resid,Prc_Resid)
SELECT TOP 1
#AYear,H_Stock_Cd,#OdCd,R_Stock_Cd,H_Id,H_Fact_Date,H_Fact_No,H_Radif,
R_Id,R_Fact_Date,R_Fact_No,R_Radif,Qnt_Resid,Prc_Resid
FROM #H
WHERE R_Id IS NOT Null
ORDER BY H_Fact_Date DESC ,H_Fact_No DESC ,H_Radif DESC ,H_Stock_Cd Desc
Set #E=#E+##Error
IF #E=0 COMMIT TRAN ELSE ROLLBACK TRAN
SELECT #Cnt Cnt,#LHFact_No LHFactNo,#LHFact_Date LHFactDate,#LHStock_Cd
LHStock_Cd,#LRFact_No LRFactNo,#LRFact_Date LRFactDate,#LRStock_Cd
LRStock_Cd
Without a copy of your db (not a request) it's not possible to answer this.
Is it possible that the configuration of SQL Server affects how a
query returns results?
Yes this is a possibility. For example, if your ANSI NULL settings are different between the two servers then NULL will be equal to NULL on the server has ANSI_NULL set to on OFF but not on the server where ANSI_NULL is ON. Collation is another. If one server has a case sensitive collation the "A" and "a" are not equal whereas the opposite is true using the SQL Server default. These are just a couple examples.
That said, they way to isolate the problem is to break the stored proc up into parts and try to identify where the differences are starting. In the first steps, where you assign variables, add a step to dump them into a temp table that you can summarize and compare across both servers. If there's no differences keep moving down the proc running each part until you find a difference. Often I'll comment everything out then uncomment code from top-to-bottom until I isolate the problem.
Lastly, you are using couple cursors here and don't need to. You can simplify your code by making it more set-based and therefore less verbose. It will be much easier to troubleshoot and will perform much, much better.

Alternative to for loop in SQL Server

I'm trying to migrate stored procedure in Oracle to Sql server 2014. It has two "for loops" written in it. I thought of using cursors but I would appreciate if someone has better alternative to cursors or it is appropriate to use it ?
The procedure takes each deleted programs from a table present in db_1 schema and deletes the associated values from the table present in second DB_2
CREATE OR REPLACE PROCEDURE PD_DELETE
AS
v_PID VARCHAR2(40 BYTE);
BEGIN
-- Finding all the deleted subparts in DB_1
FOR i IN
(SELECT ad.ID AS ID
, ad.D_FAM AS D_FAM
, SUBSTR( ad.ID, 0, 18 ) AS A_fac_id
, SUBSTR (ad.ID, 20, 7) AS A_CODE
, SUBSTR( ad.ID, 20 ) AS A_SubCode
FROM DB_1.IC_DELETE ad
WHERE ad.IC_Status_ID <> 'P'
AND ad.D_FAM = 'A_Program_Subpart'
)
LOOP
Database DB_2
SELECT A_ID INTO v_PID FROM DB_2.IC_Program
WHERE A_ID = i.a_fac_id
and A_CODE = i.A_CODE;
DELETE
FROM DB_2.AP_SUBPART t
WHERE t.A_ID = v_PID
AND t.SUBPARTCODE = i.A_SubCode;
--- Reset the status in DB_1
UPDATE DB_1.IC_DELETE ad
SET ad.IC_Status_ID = 'P',
ad.IC_DATE = SYSDATE
WHERE ad.ID = i.ID
AND ad.D_FAM = i.D_FAM;
-- Commit data for a single record
COMMIT;
END LOOP;
========================================
SQL SERVER using cursor
AS
BEGIN
Declare #V_ID varchar(40),
#id varchar(10), #fam varchar(max), #code varchar(max)
Declare DB_cursor CURSOR LOCAL FORWARD_ONLY FOR
Select id, fam, code from IC
where ind <> 'P'
OPEN DB_Cursor
BEGIN
FETCH DB_Cursor into #id, #fam, #code
select #V_id = id from AP
...............
DELETE
FROM
..............
UPDATE
.............
END
Close DB_Cursor
Deallocate DB_Cursor
Good grief your column and table names are so cryptic!!!
It is a shot in the dark here are it is not 100% clear what you are trying to do but I think this should be pretty much it.
This would replace all the looping here.
update ad
SET ind = 'P'
, date = GETDATE()
from ad
join IC on IC.id = a.ID
where IC.ind <> 'P'
delete IC
from IC
join AP on AP.afid = IC.afacid
AND AP.acode = IC.code

Already an object named in the database issue

I am trying to execute an email that contains the results of a stored procedure. I was looking at other posts in stackoverflow but cannot seem to get past an error that states "Msg 2714, Level 16, State 1, Procedure CompareConfirm_FraudRules, Line 38
There is already an object named '##returnInactiveRules' in the database." I've looked in the DB and there is no object that exists with this name already. Any suggestions on how to fix this issue would be appreciated. Thanks.
Here is the my SP:
BEGIN
CREATE TABLE ##returnInactiveRules (
ProductName varchar(100),
ChannelName varchar(100),
StrategyCode varchar(100),
StrategyName varchar(100),
RuleCode varchar(100),
RuleName varchar(100),
On_Off varchar(5)
);
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
-- SELECT R.RuleCode, R.Name as RuleName, S.StrategyCode, S.Name as StrategyName, R.IsActive
SELECT DISTINCT
CASE
WHEN PC.ProductIdentifier='2000' THEN 'GP'
WHEN PC.ProductIdentifier='1000' THEN 'MB'
END as ProductName, C.Name as ChannelName, S.StrategyCode, S.Name as StrategyName, R.RuleCode, R.Name as RuleName,
CASE
WHEN R.IsActive = 1 THEN 'On'
WHEN R.IsActive = 0 THEN 'Off'
END as On_Off
INTO ##returnInactiveRules
FROM dbo.[Rule] R
INNER JOIN dbo.Strategy S
on S.KnockoutRuleSet = R.KnockoutRuleSet
INNER JOIN dbo.RFAI P
on R.RFAIId = P.RFAIId
INNER JOIN dbo.DecisionStatus D
on D. StatusId = R. StatusId
LEFT OUTER JOIN dbo.NOAA N
on N.NOAAId = R.NOAAId
INNER JOIN dbo.RuleQuestionsXRef Q
ON Q.RuleId = R.RuleId
INNER JOIN ProductChannelStrategyRuleXref X
ON X.RuleId = R.RuleId
INNER JOIN ProductChannelStrategy CS
ON CS.ProductChannelStrategyId = X.ProductChannelStrategyId
INNER JOIN ProductChannel PC
ON PC.ProductChannelId = CS.ProductChannelId
INNER JOIN dbo.Channel C
ON C.ChannelId = PC.ChannelId
WHERE R.IsActive = 0
AND R.RuleCode IN ('F06',
'F07',
'F11',
'F12',
'F14',
'F15',
'F16',
'F17',
'F19',
'F23',
'F25',
'F26',
'F10'
)
-- ORDER BY R.RuleCode, R.Name;
ORDER BY ProductName, C.Name, S.StrategyCode, S.Name, R.RuleCode, R.Name;
-- SELECT * FROM #returnValue;
-- Email the results in the #returnValue table --
EXEC msdb.dbo.sp_send_dbmail
#execute_query_database='Prod-XXX',
#recipients=N'msavoy#xxx.com',
#body='Attached please find a file with the results.',
#subject ='Compare Fraud Rule Results',
#profile_name ='Reports',
#query ='EXEC CompareConfirm_Rules',
#attach_query_result_as_file = 1,
#query_attachment_filename ='CompareRuleResults.txt'
END
DROP TABLE ##returnInactiveRules;
GO
If exists Drop it, then create the table
IF Object_id('tempdb..##returnInactiveRules') IS NOT NULL
DROP TABLE ##returnInactiveRules
CREATE TABLE ##returnInactiveRules
(
ProductName VARCHAR(100),
ChannelName VARCHAR(100),
StrategyCode VARCHAR(100),
StrategyName VARCHAR(100),
RuleCode VARCHAR(100),
RuleName VARCHAR(100),
On_Off VARCHAR(5)
);
Temp tables which start with ## are global temp tables, so if you have 2 connection opened, and one of them created a table called ##Temp, you would not be able to create the same table from connection 2, until it is dropped by any of the 2 connections.
Best thing would be to use #returnInactiveRules and check for existence before creating it.
IF OBJECT_ID('tempdb..#returnInactiveRules') IS NOT NULL
DROP TABLE #returnInactiveRules
GO
Have the drop statement at the beginning and if it's a global temp table ensure that only one process is creating it at any one time as two cannot exist simultaneously..
Also, unless you require other sessions to access the data, consider using a temp table rather than a global temp table by replacing the ## with #

Resources