3

Я переношу очередь на диск в память SQL Server 2016 для реализации очереди.UPDATE из таблицы в SQL Собственная хранимая процедура (Hekaton)

Это мой формат очереди:

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 

Но я получаю эту ошибку:

Subqueries (queries nested inside another query) is only supported in SELECT statements with natively compiled modules.

Так что я попробовал другой подход, используя переменную для хранения результата.

Сначала я создал тип таблицы:

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 

Я получаю эту ошибку:

Must declare the scalar variable "@result".

(только при использовании @result на WHERE @result.MsgId = dbo.SimpleQueue.MsgId)

Вот старый Dequeue процесс с использованием таблиц диска 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 

Как я могу сделать, что UPDATE и выводить обновленные значения (с высокой производительностью, так как это очень важно)?

ответ

0

В вашей старой рутине вы используете 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 .. или возможно CROSS ОТНОСИТЬСЯ ,

EDIT: Еще один подход с КТР:

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 
+0

Относительно ORDER BY I опущен, что пункт о цели. В соответствии с MSDN «Строки можно извлекать в соответствии с порядком, указанным при создании индекса. Если порядок сортировки индекса соответствует порядку сортировки, требуемому для конкретного запроса, например, если индексный ключ соответствует предложению ORDER BY, нет необходимости для сортировки строк как части выполнения запроса ». Источник: https://msdn.microsoft.com/en-us/library/dn133166.aspx –

+0

Я пытался использовать UPDATE dbo.SimpleQueue \t SET IsDeleted = 1 \t WHERE dbo.SimpleQueue.MsgId IN (@result), но я все равно получаю эту ошибку: должен объявить скалярную переменную «@result». Пожалуйста, не забывайте, что вы не можете использовать подзапросы в ** собственных хранимых процедурах ** –

+0

@ JoãoAntunes, ну, у меня нет опыта работы с таблицами в памяти (обязательно посмотрите!), Но это больше похоже на объяснение как работает оптимизатор, поэтому нет необходимости в двойном ORDER BY, если сортировка уже подходит. В общем, нет абсолютно никакой гарантии, в каком порядке вы получите свой результат - если вы не укажете ORDER BY в самом внешнем запросе (то же самое с подзапросами ...) – Shnugo