Execute Store Procedure For All Rows in SELECT without using Cursor
You only have a couple of choices. Either modify your procedure to receive a table valued function so you can deal with it set based. Or you are stuck doing some sort of looping, most likely a cursor.
Writing the stored procedure without cursor
You can get rid of the cursor with a while loop as illustrated below. If the data you are querying included a unique identifier you could do away with the temp table but for your example I used a temp table and included a recordId.
ALTER PROCEDURE [dbo].[AccreditationExpiryCheck]
AS
BEGIN
SET NOCOUNT ON;
declare @taskTypeId int = 19 -- Accreditations, automated
declare @firstActionTypeId int = 23 -- Accreditation expiring
declare @nextActionTypeId int = 3 -- Call company
declare @companyId int
declare @accreditationId int
declare @comment nvarchar(max) = N' accreditation for this company has expired.'
-- find all companies and accreditations expiring
select ROW_NUMBER() OVER(ORDER BY c.Company_Id, a.Accred_ID) as [RecordId], c.Company_Id as [Company_Id], a.Accred_ID as [Accred_ID]
into #COMPANIES
from COMPANY c
inner join MEMBERSHIP m on c.Company_ID = m.Company_ID
inner join ACCREDITATION a on c.Company_ID = a.Company_ID
where
-- Accreditation expired yesterday
cast(a.Accred_ExpDate as DATE) = cast(DATEADD(DAY, -1, GETDATE()) as DATE)
and m.IsMember_Ind = 1
and (c.HQ_ID IS NULL OR c.HQ_ID = c.Company_ID) -- FB4640: this isn't a 'team' co (with an HQ)
-- and there is no action of this type created within 1 day
-- of the expiry date
and not exists (
select * from TaskAction ta where
ta.FirstActionTypeId = @firstActionTypeId and
ta.TaskTypeId = @taskTypeId and
ta.TaskCreatedOn BETWEEN a.Accred_ExpDate AND DATEADD(DAY, 1, a.Accred_ExpDate) and
ta.EntityId = c.Company_ID and
ta.EntityTypeId = 1 )
declare @recordId int = 0;
declare @title nvarchar(max);
declare @comment2 nvarchar(max);
while(1=1)
begin
select top 1 @recordId = [RecordId]
,@companyId = [CompanyId]
,@accreditationId = [Accred_ID]
from #COMPANIES
where [RecordId] > @recordId
if @@ROWCOUNT = 0 break;
set @title =
(select AccredType_Name from ACCREDITATION_TYPE at
inner join ACCREDITATION a on at.AccredType_ID = a.AccredType_ID
where a.Accred_ID = @accreditationId)
set @comment2 = isnull(@title, '') + ' accreditation for this company has expired.'
exec CreateSystemTask
@taskTypeId,
@firstActionTypeId,
@nextActionTypeId,
@companyid,
@comment2,
@title
end
drop table #COMPANIES
END
SQL Server : call stored procedure with side effects for each row without using a cursor
cursors are inherently bad
No. Cursors are easily misused, and tend to be leapt upon by people new to SQL because they're from a procedural background and haven't even heard of "set-based". Cursors have their place, and if you've assessed the available approaches and concluded that cursors are a good fit, I'd say use one.
Using a WHILE
loop to hide the fact that what you're really doing is using a cursor is also something I'd recommend against.
One final note - you mentioned fast_forward
and read_only
- but one other recommendation would be local
- that way, if something goes wrong, at least the cursor is cleaned up when you exit whatever scope your cursor is running in - rather than persisting for the lifetime of the connection.
Call stored procedure for each XML element without cursors
alter PROCEDURE [dbo].[spSendLogLinesAsXML]
(
@device_id varchar(128),
@application_name VARCHAR(64),
@application_user_name VARCHAR(6),
@log_lines_xml XML
)
AS
BEGIN
declare @log_line_dt datetime,
@log_line_message varchar(1024)
declare @curse cursor
set @curse = cursor fast_forward for
select n.d.value('dt[1]', 'datetime') as log_line_dt, n.d.value('data[1]', 'varchar(1024)') as log_line_message
from @log_lines_xml.nodes('/lines/line') n(d)
open @curse
fetch next from @curse into @log_line_dt, @log_line_message
while (@@fetch_status = 0)
begin
EXEC spSendLogLine
@device_id = @device_id,
@application_name = @application_name,
@application_user_name = @application_user_name,
@log_line_dt = @log_line_dt,
@log_line_message = @log_line_message
fetch next from @curse into @log_line_dt, @log_line_message
end
close @curse;
return -100
END
GO
Related Topics
Renaming a Column in Ms SQL Server 2005
SQL Recursion Without Recursion
Select Average from MySQL Table with Limit
Enable Full-Text Search on View with Inner Join
Rails, Ransack: How to Search Habtm Relationship for "All" Matches Instead of "Any"
Detect Duplicate Items in Recursive Cte
Many-To-Many Relations in Rdbms Databases
SQL Indexes and Performance Improvement
SQL Developer "Disconnected from the Rest of the Join Graph"
Make H2 Treat Quoted Name and Unquoted Name as the Same
Using Bcp Utility to Export SQL Queries to a Text File
Oracle Trigger Ora-04098: Trigger Is Invalid and Failed Re-Validation
#1222 - the Used Select Statements Have a Different Number of Columns
Sql: Sort by Priority, But Put 0 Last