2
I’m facing one of those situations where I need to perform an action for each line resulting from a query.
This way I have two options, use a Cursor
or a Table Variable
, but the two seem very similar to me (semantically).
I wonder if using a Table Variable
I’ll get some performance improvement over Cursor
.
I believe the difference between the two is that the Table Variable
will perform only one query and go through the records in memory, while the Cursor
will hold a consultation (Fetch
) for each line, but I have no way to confirm (this my achism).
So which of the two is better and why?
EDIT
I decided to add a complete example and statistics.
TABLE
CREATE TABLE [dbo].[CursorTeste](
[CursorTesteID] [int] IDENTITY(1,1) NOT NULL,
[Coluna1] [uniqueidentifier] NOT NULL,
[Coluna2] [uniqueidentifier] NOT NULL,
[Coluna3] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_CursorTeste] PRIMARY KEY CLUSTERED
(
[CursorTesteID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT
DECLARE @count int;
SET @count = 0;
WHILE (@count < 10000)
BEGIN
INSERT INTO CursorTeste VALUES (NEWID(), NEWID(), NEWID());
SET @count = @count + 1;
END
CURSOR
DECLARE @coluna1 uniqueidentifier;
DECLARE @coluna2 uniqueidentifier;
DECLARE @coluna3 uniqueidentifier;
DECLARE @CURSOR_teste CURSOR;
SET @CURSOR_teste = CURSOR LOCAL FAST_FORWARD FOR
SELECT Coluna1, Coluna2, Coluna3 FROM CursorTeste
OPEN @CURSOR_teste
WHILE (1 = 1)
BEGIN
FETCH NEXT FROM @CURSOR_teste INTO @coluna1, @coluna2, @coluna3;
IF (@@FETCH_STATUS <> 0)
BEGIN
BREAK;
END
PRINT '{ ' + cast(@coluna1 as varchar(50)) + ' } - { ' + cast(@coluna2 as varchar(50)) + ' } - { ' + cast(@coluna3 as varchar(50)) + ' }';
END
CLOSE @CURSOR_teste
DEALLOCATE @CURSOR_teste
Table Variable
DECLARE @coluna1 uniqueidentifier;
DECLARE @coluna2 uniqueidentifier;
DECLARE @coluna3 uniqueidentifier;
DECLARE @indice int
DECLARE @count int
DECLARE @tabela table(
RowNumber int identity,
Coluna1 uniqueidentifier not null,
Coluna2 uniqueidentifier not null,
Coluna3 uniqueidentifier not null,
PRIMARY KEY (RowNumber)
);
INSERT INTO @tabela
SELECT Coluna1, Coluna2, Coluna3 FROM CursorTeste
SET @count = (SELECT COUNT(RowNumber) FROM @tabela)
SET @indice = 1;
WHILE (@indice <= @count)
BEGIN
SELECT
@indice = RowNumber + 1,
@coluna1 = Coluna1,
@coluna2 = Coluna2,
@coluna3 = Coluna3
FROM @tabela
WHERE RowNumber = @indice
PRINT '{ ' + cast(@coluna1 as varchar(50)) + ' } - { ' + cast(@coluna2 as varchar(50)) + ' } - { ' + cast(@coluna3 as varchar(50)) + ' }';
END
- Rating 1: CURSOR
- Rating 2: CURSOR FAST_FORWARD
- Rating 3: CURSOR LOCAL FAST_FORWARD
- Rating 4: WHILE LOOP WITH @TABLE
I understand the whole problem of cursors and that it should be avoided and rarely this is not possible. But the idea here is to compare these two approaches and see if it is possible to get some gain by using a table instead of a CURSOR.
– Tobias Mesquita