ОБНОВЛЕНИЕ из таблицы в SQL Собственная хранимая процедура (Hekaton)
Я перемещаю очередь на диске в память SQL Server 2016, чтобы реализовать очередь.
Это мой формат очереди:
CREATE TABLE dbo.SimpleQueue
(
MsgId BIGINT NOT NULL PRIMARY KEY NONCLUSTERED IDENTITY(1, 1),
Payload VARCHAR(7500) NOT NULL,
IsDeleted BIT NOT NULL
) WITH (MEMORY_OPTIMIZED=ON)
GO
Это мое Enqueue
Собственная хранимая процедура SQL Server:
CREATE PROCEDURE dbo.Enqueue(@Payload VARCHAR(7500), @IsDeleted BIT)
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH
(TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = 'english')
INSERT INTO dbo.SimpleQueue (Payload, IsDeleted) VALUES (@Payload, @IsDeleted);
END
GO
Я пытаюсь записать Dequeue
встроенная хранимая процедура SQL Server, но у меня возникают трудности с реализацией UPDATE
используя результаты SELECT или таблицы переменных.
Пока что попробовал:
CREATE PROCEDURE dbo.Dequeue(@BatchSize INT = 1)
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH
( TRANSACTION ISOLATION LEVEL = SNAPSHOT,LANGUAGE = 'english' )
UPDATE dbo.SimpleQueue
SET IsDeleted=1
WHERE MsgId = (
SELECT TOP(@BatchSize) MsgId, Payload
FROM dbo.SimpleQueue
WHERE IsDeleted = 0)
END
GO
Но я получаю эту ошибку:
Подзапросы (запросы, вложенные в другой запрос) поддерживаются только в инструкциях SELECT с собственно скомпилированными модулями.
Поэтому я попробовал другой подход, используя переменную для хранения результата.
Сначала я создал тип таблицы:
CREATE TYPE dbo.SimpleDequeue
AS TABLE
(
MsgId BIGINT NOT NULL PRIMARY KEY NONCLUSTERED,
Payload INT NOT NULL
)
WITH (MEMORY_OPTIMIZED=ON)
GO
Пока все хорошо, тогда я попытался использовать это:
CREATE PROCEDURE dbo.Dequeue(@BatchSize INT = 1)
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER
AS BEGIN ATOMIC WITH
( TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = 'english')
DECLARE @result dbo.SimpleDequeue;
INSERT @result
SELECT TOP(@BatchSize) MsgId, Payload FROM dbo.SimpleQueue
WHERE IsDeleted = 0
UPDATE dbo.SimpleQueue
SET IsDeleted = 1
WHERE
@result.MsgId = dbo.SimpleQueue.MsgId
SELECT MsgId, Payload FROM @result
END
GO
Я получаю эту ошибку:
Необходимо объявить скалярную переменную "@result".
(только когда используете @result
на WHERE @result.MsgId = dbo.SimpleQueue.MsgId
)
Вот старый процесс удаления из очереди, использующий в дисковых таблицах SQL Server:
CREATE PROCEDURE dbo.DequeueInDisk
@BatchSize INT = 1
AS
BEGIN
SET NOCOUNT ON;
WITH
cte AS (
SELECT TOP(@BatchSize) Payload
FROM dbo.SimpleQueue WITH (ROWLOCK, READPAST)
ORDER BY MsgId
)
DELETE FROM cte OUTPUT deleted.Payload;
END
Как я могу сделать это ОБНОВЛЕНИЕ и ВЫХОД обновленных значений (с высокой производительностью, так как это важно)?
2 ответа
Я думаю, что ваш подход имеет смысл с точки зрения разработки SQL - вы должны думать в наборах, а не в построчном подходе. Но похоже, что Microsoft считает, что вам нужен другой подход для нативных скомпилированных процедур, более обязательный и действительно построчный (см. " Реализация UPDATE с FROM или подзапросами" или " Реализация функциональности MERGE в нативно скомпилированной хранимой процедуре"). Таким образом, ваша процедура может выглядеть следующим образом этот:
create or alter procedure [dbo].[Dequeue](@BatchSize int = 1)
with native_compilation, schemabinding, execute as owner
AS
BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = 'english')
declare
@result dbo.SimpleDequeue;
declare
@MsgId int,
@Payload varchar(7500),
@i int = 0;
while @i < @BatchSize
begin
select top (1)
@MsgId = s.MsgId,
@Payload = s.Payload
from dbo.SimpleQueue as s
where
s.IsDeleted = 0
order by
s.MsgId;
if @@rowcount = 0
begin
set @i = @BatchSize;
end
else
begin
update dbo.SimpleQueue set IsDeleted = 1 where MsgId = @MsgId;
insert into @result (MsgId, Payload)
select @MsgId, @Payload;
set @i += 1;
end;
end;
select MsgId, Payload from @result;
END
Я не проверял, насколько быстро он будет работать, но я обязательно протестирую его с некоторыми действительными числами, потому что у нас реализована пара этих очередей таблиц, и мне интересно, сможем ли мы повысить производительность с Hekaton.
В своей старой рутине вы используете TOP(@BatchSize)
с ORDER BY MsgId
, Новый подход, кажется, не имеет этого ORDER BY
... Вы получите случайный результат...
Ваш
WHERE MsgId = (
SELECT TOP(@BatchSize) MsgId, Payload
FROM dbo.SimpleQueue
WHERE IsDeleted = 0
/*added this!*/ ORDER BY MsgId )
вернусь с двумя столбцами и - возможно - несколькими рядами. Вы не можете сравнить это с "=".
То, что вы можете попробовать:
WHERE MsgId IN (
SELECT TOP(@BatchSize) MsgId
FROM dbo.SimpleQueue
WHERE IsDeleted = 0
ORDER BY MsgId)
Или вы можете попробовать использовать INNER JOIN, что-то вроде этого:
UPDATE dbo.SimpleQueue
SET IsDeleted=1
FROM dbo.SimpleQeueu
INNER JOIN dbo.SimpleQueue AS sq ON dbo.SimpleQeueu.MsgId=sq.MsgId
AND sq.IsDeleted=0
--this is missing the TOP-clause
Что еще: вы можете попробовать INNER JOIN (SELECT TOP ... ) AS InnerSimpleQueue ON ..
или, может быть, ПРИМЕНЕНИЕ КРЕСТА.
РЕДАКТИРОВАТЬ: еще один подход с CTE:
WITH myCTE AS
(
SELECT TOP(@BatchSize) MsgId
FROM dbo.SimpleQueue
WHERE IsDeleted = 0
ORDER BY MsgId
)
UPDATE dbo.SimpleQueue
SET IsDeleted=1
FROM dbo.SimpleQeueu
INNER JOIN myCTE ON myCTE.MsgId=dbo.SimpleQueue.MsgId