38. Complex Business Logic -- Multi-Step Processes
Level: Advanced SQL Server for real projects
- Multi-step workflows in transactions
- State management (status tracking)
- Conditional branching logic
- Error propagation and recovery
- Notification patterns
- Audit trails for compliance
- Saga pattern (distributed workflows)
- Best practices for complex logic
Complex processes need careful orchestration. This article teaches multi-step logic.
Scenario 1: Semester Enrollment Workflow
Complete enrollment with dependencies:
CREATE PROCEDURE sp_CompleteSemesterEnrollment
@StudentId INT,
@NewClassName NVARCHAR(10),
@AcademicYear NVARCHAR(9),
@IsSuccess BIT OUTPUT,
@ErrorMessage NVARCHAR(500) OUTPUT
AS
BEGIN
SET @IsSuccess = 0;
BEGIN TRANSACTION;
BEGIN TRY
-- Step 1: Validate student exists and active
IF NOT EXISTS (SELECT 1 FROM Student WHERE StudentId = @StudentId AND Status = 'Active')
BEGIN
THROW 50001, 'Student not active or not found', 1;
END;
-- Step 2: Validate class exists
IF NOT EXISTS (SELECT 1 FROM Class WHERE ClassName = @NewClassName)
BEGIN
THROW 50002, 'Class not found', 1;
END;
-- Step 3: Update student class
UPDATE Student SET ClassName = @NewClassName WHERE StudentId = @StudentId;
-- Step 4: Create fee account for new academic year
INSERT INTO FeeAccount (StudentId, AcademicYear, TotalFees, Status)
SELECT @StudentId, @AcademicYear, c.FeesAmount, 'Pending'
FROM Class c WHERE ClassName = @NewClassName;
-- Step 5: Initialize attendance records (create for each subject)
INSERT INTO Attendance (StudentId, SubjectId, Date, IsPresent)
SELECT @StudentId, SubjectId, GETDATE(), 0
FROM Subject WHERE ClassName = @NewClassName;
-- Step 6: Log enrollment
INSERT INTO AuditLog (Action, EntityName, EntityId, NewValues, ChangedBy, ChangedAt)
VALUES (
'SemesterEnroll',
'Student',
@StudentId,
CONCAT('Class=', @NewClassName, ', Year=', @AcademicYear),
SYSTEM_USER,
GETDATE()
);
-- Step 7: Commit all changes
COMMIT TRANSACTION;
SET @IsSuccess = 1;
SET @ErrorMessage = 'Enrollment completed successfully';
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
SET @IsSuccess = 0;
SET @ErrorMessage = ERROR_MESSAGE();
END CATCH
END;
All-or-nothing: Class + Fees + Attendance + Audit.
Scenario 2: Fee Payment with Reconciliation
Multi-step payment processing:
CREATE PROCEDURE sp_ProcessFeePayment
@StudentId INT,
@Amount DECIMAL(10,2),
@PaymentMode NVARCHAR(20),
@TransactionId NVARCHAR(50),
@PaymentId INT OUTPUT,
@IsSuccess BIT OUTPUT,
@ErrorMessage NVARCHAR(500) OUTPUT
AS
BEGIN
SET @IsSuccess = 0;
BEGIN TRANSACTION;
BEGIN TRY
DECLARE @FeeAccountId INT;
DECLARE @DueAmount DECIMAL(10,2);
-- Step 1: Get current fee account
SELECT TOP 1 @FeeAccountId = FeeAccountId, @DueAmount = (TotalFees - PaidAmount)
FROM FeeAccount
WHERE StudentId = @StudentId AND Status != 'Paid'
ORDER BY AcademicYear DESC;
IF @FeeAccountId IS NULL
BEGIN
THROW 50001, 'No pending fee account found', 1;
END;
-- Step 2: Validate amount
IF @Amount > @DueAmount
BEGIN
THROW 50002, CONCAT('Amount exceeds due (', @DueAmount, ')'), 1;
END;
-- Step 3: Record payment
INSERT INTO FeePayment (FeeAccountId, Amount, PaidOn, PaymentMode, TransactionId, CollectedBy)
VALUES (@FeeAccountId, @Amount, GETDATE(), @PaymentMode, @TransactionId, SYSTEM_USER);
SET @PaymentId = SCOPE_IDENTITY();
-- Step 4: Update fee account
DECLARE @NewPaidAmount DECIMAL(10,2);
SELECT @NewPaidAmount = PaidAmount + @Amount FROM FeeAccount WHERE FeeAccountId = @FeeAccountId;
UPDATE FeeAccount
SET PaidAmount = @NewPaidAmount,
Status = CASE WHEN @NewPaidAmount >= TotalFees THEN 'Paid' ELSE 'Partial' END
WHERE FeeAccountId = @FeeAccountId;
-- Step 5: Log transaction
INSERT INTO AuditLog (Action, EntityName, EntityId, NewValues, ChangedBy, ChangedAt)
VALUES (
'FeePayment',
'FeePayment',
@PaymentId,
CONCAT('Amount=', @Amount, ', Mode=', @PaymentMode),
SYSTEM_USER,
GETDATE()
);
COMMIT TRANSACTION;
SET @IsSuccess = 1;
SET @ErrorMessage = 'Payment processed successfully';
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
SET @IsSuccess = 0;
SET @ErrorMessage = ERROR_MESSAGE();
END CATCH
END;
Payment: Validate -> Record -> Update account -> Log.
Scenario 3: State Management (Academic Workflow)
Track student academic status:
CREATE TABLE StudentAcademicStatus (
StatusId INT PRIMARY KEY IDENTITY(1,1),
StudentId INT,
Status NVARCHAR(20), -- Enrolled, InProgress, Completed, Failed, Withdrawn
ChangeReason NVARCHAR(200),
ChangedAt DATETIME DEFAULT GETDATE(),
ChangedBy NVARCHAR(100),
FOREIGN KEY (StudentId) REFERENCES Student(StudentId)
);
-- Procedure to change status with validation
CREATE PROCEDURE sp_UpdateAcademicStatus
@StudentId INT,
@NewStatus NVARCHAR(20),
@Reason NVARCHAR(200),
@IsSuccess BIT OUTPUT
AS
BEGIN
SET @IsSuccess = 0;
BEGIN TRY
DECLARE @CurrentStatus NVARCHAR(20);
-- Get current status
SELECT TOP 1 @CurrentStatus = Status
FROM StudentAcademicStatus
WHERE StudentId = @StudentId
ORDER BY StatusId DESC;
-- Validate state transition
IF @CurrentStatus = 'Completed' AND @NewStatus != 'Completed'
BEGIN
THROW 50001, 'Cannot change status from Completed', 1;
END;
IF @CurrentStatus = 'Withdrawn' AND @NewStatus != 'Withdrawn'
BEGIN
THROW 50002, 'Cannot change status from Withdrawn', 1;
END;
-- Update
INSERT INTO StudentAcademicStatus (StudentId, Status, ChangeReason, ChangedBy)
VALUES (@StudentId, @NewStatus, @Reason, SYSTEM_USER);
SET @IsSuccess = 1;
END TRY
BEGIN CATCH
SET @IsSuccess = 0;
THROW;
END CATCH
END;
State machine: Validate transitions before changing status.
Scenario 4: Report Generation Workflow
Multi-step report creation:
CREATE PROCEDURE sp_GenerateProgressReport
@StudentId INT,
@ReportDate DATE
AS
BEGIN
BEGIN TRY
-- Step 1: Validate student
DECLARE @StudentName NVARCHAR(100);
SELECT @StudentName = Name FROM Student WHERE StudentId = @StudentId;
IF @StudentName IS NULL
BEGIN
THROW 50001, 'Student not found', 1;
END;
-- Step 2: Create temp report table
CREATE TABLE #ProgressReport (
StudentName NVARCHAR(100),
ClassName NVARCHAR(10),
Subject NVARCHAR(100),
AvgMarks DECIMAL(5,2),
Grade NVARCHAR(2),
Attendance DECIMAL(5,2),
Status NVARCHAR(20)
);
-- Step 3: Populate with academic data
INSERT INTO #ProgressReport
SELECT
s.Name,
s.ClassName,
sub.Name,
AVG(r.MarksObtained),
CASE WHEN AVG(r.MarksObtained) >= 80 THEN 'A'
WHEN AVG(r.MarksObtained) >= 70 THEN 'B'
ELSE 'C' END,
ROUND(
SUM(CASE WHEN a.IsPresent = 1 THEN 1 ELSE 0 END) * 100.0 /
COUNT(a.AttendanceId), 2
),
CASE WHEN AVG(r.MarksObtained) < 60 THEN 'At Risk'
ELSE 'Good' END
FROM Student s
LEFT JOIN ExamResult r ON s.StudentId = r.StudentId
LEFT JOIN Exam e ON r.ExamId = e.ExamId
LEFT JOIN Subject sub ON e.SubjectId = sub.SubjectId
LEFT JOIN Attendance a ON s.StudentId = a.StudentId
WHERE s.StudentId = @StudentId
GROUP BY s.Name, s.ClassName, sub.Name;
-- Step 4: Return formatted report
SELECT * FROM #ProgressReport;
-- Step 5: Log report generation
INSERT INTO AuditLog (Action, EntityName, EntityId, ChangedBy, ChangedAt)
VALUES ('GenerateReport', 'Student', @StudentId, SYSTEM_USER, GETDATE());
-- Step 6: Cleanup
DROP TABLE #ProgressReport;
END TRY
BEGIN CATCH
DROP TABLE IF EXISTS #ProgressReport;
THROW;
END CATCH
END;
Report: Validate -> Gather -> Format -> Log -> Cleanup.
Use transaction + stored procedure:
BEGIN TRANSACTION;
BEGIN TRY
Step 1: Validate
Step 2: Update Table1
Step 3: Update Table2
Step 4: Log
COMMIT;
END TRY
BEGIN CATCH
ROLLBACK;
END CATCH
All steps succeed or all rollback.
Track status changes + validate transitions:
Current: Enrolled
Valid next: InProgress, Withdrawn
Invalid next: Completed (only from InProgress)
Validate before UPDATE.
State machine prevents invalid transitions.
Check: amount <= due, update account, record payment, log.
Atomicity: All or rollback.
Yes, for intermediate results:
CREATE TABLE #TempData (...);
INSERT INTO #TempData ... FROM ...;
SELECT * FROM #TempData;
DROP TABLE #TempData;
Cleanup: DROP TABLE in CATCH too.
Order matters:
- Validate all prerequisites
- Execute in dependency order
- Log after success
- Rollback on any failure
Transaction ensures atomicity.
Log summary + key data:
INSERT INTO AuditLog (Action, EntityId, NewValues)
VALUES ('ComplexOp', @Id, 'Step1=OK, Step2=OK, Result=Success');
Track progress for debugging.
Use ChatGPT, Claude, or Copilot to go deeper on Complex business logic and multi-step workflows. Try these prompts:
"Show semester enrollment with multiple steps (class, fees, attendance)""How do I process payment with reconciliation?""Explain state machine: validate transitions before update""How do I generate multi-step reports with temp tables?""Quiz me: 5 questions about workflows, state, atomicity"
💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.
Next Article
39. Troubleshooting & Debugging -- Finding and Fixing Issues ->