Тупик при обновлении той же таблицы - SQL - PullRequest
0 голосов
/ 25 сентября 2019

Существует хранимая процедура, которая обновляет pre_plan и pre_type для всех сотрудников.И это тупик.

DDL таблиц посещений и ассоциаций:

CREATE TABLE [dbo].[Associate](
    [pre_plan_id] [smallint] NULL,
    [pre_type_id] [smallint] NULL,
    [associate_id] [smallint] NOT NULL,
    [deleted] [bit] NOT NULL
)  

INSERT INTO Associate 
VALUES 
(NULL,  NULL,   -32768, 0),
(NULL,  NULL,   2,  1),
(NULL,  NULL,   3,  0),
(NULL,  NULL,   6,  1),
(NULL,  NULL,   3097,   1),
(NULL,  NULL,   3109,   0),
(NULL,  NULL,   3265,   1),
(NULL,  NULL,   3313,   0),
(NULL,  NULL,   3318,   1),
(NULL,  NULL,   3329,   0)


CREATE TABLE [dbo].[Visit](
    [type_id] [smallint] NOT NULL,
    [plan_id] [smallint] NOT NULL,
    [associate_id] [smallint] NOT NULL,
    [time_in] [smalldatetime] NOT NULL
) 
INSERT INTO Visit 
VALUES
(390,   31, 3109,   '2009-09-02'),
(304,   32, 3109,   '2010-02-05'),
(388,   31, 3109,   '2010-09-24'),
(388,   31, 3109,   '2010-09-27'),
(388,   31, 3109,   '2010-09-27'),
(388,   31, 3109,   '2010-09-28'),
(388,   31, 3109,   '2010-10-01'),
(333,   28, 3109,   '2011-01-11'),
(338,   30, 3109,   '2011-01-18'),
(388,   31, 3109,   '2011-01-27')

Хранимая процедура

CREATE PROCEDURE [dbo].[update_pre__] 

AS

UPDATE Associate SET pre_plan_id = 
      (SELECT TOP 1 plan_id 
         FROM Visit  
        WHERE associate_id = Associate.associate_id 
          AND time_in > 90
        GROUP BY plan_id 
        ORDER BY Count(*) DESC)
 WHERE deleted = 0

UPDATE Associate SET pre_type_id = 
      (SELECT TOP 1 [type_id] 
         FROM Visit 
        WHERE associate_id = Associate.associate_id 
          AND time_in > 90
        GROUP BY [type_id] 
        ORDER BY Count(*) DESC)
 WHERE deleted = 0 

Я думал об отделении транзакций, добавляя BEGIN TRANSACTION и COMMIT TRANSACTIONна обоих из обновлений.Поможет ли это избежать тупика?Может ли кто-нибудь помочь мне предложить наиболее эффективный способ избежать тупика?

Ответы [ 2 ]

0 голосов
/ 26 сентября 2019

Эта процедура не создает тупиковую ситуацию, поскольку два оператора обновления будут выполняться последовательно.Когда вы создаете, а затем запускаете следующую процедуру (update_pre__test), которая добавляет блок BEGIN TRAN / COMMIT TRAN .Вы можете проверить следующий запрос DMV, который показывает открытые транзакции в другом окне запроса.Запрос DMV показывает только одну открытую транзакцию, которая связана с этой хранимой процедурой.

Однако мы должны обратить внимание на одну мысль о взаимоблокировках, нам нужны две открытые транзакции и между ними должна быть конкуренция за ресурсы.

Сессия-1:

CREATE  [dbo].[update_pre__test]
AS BEGIN TRAN;
       UPDATE Associate
         SET pre_plan_id = (SELECT TOP 1 plan_id
                            FROM Visit
                            WHERE associate_id = Associate.associate_id
                                  AND time_in > 90
                            GROUP BY plan_id
                            ORDER BY COUNT(*) DESC)
       WHERE deleted = 0;
       UPDATE Associate
         SET pre_type_id = (SELECT TOP 1 [type_id]
                            FROM Visit
                            WHERE associate_id = Associate.associate_id
                                  AND time_in > 90
                            GROUP BY [type_id]
                            ORDER BY COUNT(*) DESC)
       WHERE deleted = 0;
       WAITFOR DELAY '00:01:02';
       COMMIT TRAN

Сессия-2:

    SELECT *
     FROM sys.dm_tran_active_transactions tat
     INNER JOIN sys.dm_exec_requests  er ON tat.transaction_id = er.transaction_id
    CROSS APPLY sys.dm_exec_sql_text(er.sql_handle) 
0 голосов
/ 26 сентября 2019

Мейб Это идея использовать WITH (NOLOCK) при использовании операции SELECT CRUD

ее значение, с использованием READ UNCOMMITED в качестве уровня изоляции транзакции в запросе SELECT.

With (NOLOCK) относится к текущему состоянию таблицы и игнорирует другие грубые операции в текущей операции. важно: вы можете пропустить незафиксированные операции из разных транзакций в выбранной таблице ( грязное чтение )

(SELECT TOP 1 [type_id] 
   FROM Visit With (NOLOCK)
  WHERE associate_id = Associate.associate_id 
    AND time_in > 90
GROUP BY [type_id] 
ORDER BY Count(*) DESC)
...