2013-09-30 72 views
1

我有很多存儲過程。但是我只是有時候只爲這個SP收到Request Timeout?有時只從一個SP獲取請求超時?

ALTER PROCEDURE [dbo].[Insertorupdatedevicecatalog] 
       (@OS    NVARCHAR(50) 
       ,@UniqueID   VARCHAR(500) 
       ,@Longitude   FLOAT 
       ,@Latitude   FLOAT 
       ,@Culture   VARCHAR(10) 
       ,@Other    NVARCHAR(200) 
       ,@IPAddress   VARCHAR(50) 
       ,@NativeDeviceID VARCHAR(50)) 
AS 
BEGIN 
    SET NOCOUNT ON; 
    DECLARE @TranCount INT; 
    SET @TranCount = @@TRANCOUNT; 

    DECLARE @OldUniqueID VARCHAR(500) = ''-1''; 
    SELECT @OldUniqueID = [UniqueID] FROM DeviceCatalog WHERE (@NativeDeviceID != '''' AND [NativeDeviceID] = @NativeDeviceID); 

    BEGIN TRY 
     IF @TranCount = 0 
      BEGIN TRANSACTION 
     ELSE 
      SAVE TRANSACTION Insertorupdatedevicecatalog; 

     DECLARE @Geo GEOGRAPHY = geography::STGeomFromText(''POINT('' + CONVERT(VARCHAR(100), @Longitude) + '' '' + CONVERT(VARCHAR(100), @Latitude) + '')'', 4326); 

     IF EXISTS(SELECT 1 FROM DeviceCatalog WHERE [UniqueID] = @UniqueID) 
     BEGIN 
      DECLARE @OldGeo  GEOGRAPHY 
        ,@OldCity  NVARCHAR(100) 
        ,@OldCountry NVARCHAR(100) 
        ,@OldAddress NVARCHAR(100); 

      SELECT @OldGeo = [LastUpdatedLocationFromJob] 
        ,@OldCity = [City] 
        ,@OldCountry = [Country] 
        ,@OldAddress = [Address] 
      FROM DeviceCatalog 
      WHERE [UniqueID] = @UniqueID; 

      UPDATE DeviceCatalog 
       SET [OS] = @OS 
        ,[Location] = @Geo 
        ,[Culture] = @Culture 
        ,[Other] = @Other 
        ,[IPAddress] = @IPAddress 
      WHERE [UniqueID] = @UniqueID; 

          IF (@OldGeo IS NULL OR @OldAddress IS NULL OR @OldCity IS NULL OR @OldCountry IS NULL OR ISNULL(@Geo.STDistance(@OldGeo)/1000,0) > 50) 
      BEGIN 
       UPDATE DeviceCatalog 
        SET [Lastmodifieddate] = Getdate() 
       WHERE [UniqueID] = @UniqueID; 
      END 

     END 
     ELSE 
     BEGIN 
      INSERT INTO DeviceCatalog 
         ([OS] 
         ,[UniqueID] 
         ,[Location] 
         ,[Culture] 
         ,[Other] 
         ,[IPAddress] 
         ,[NativeDeviceID]) 
       VALUES (@OS 
         ,@UniqueID 
         ,@Geo 
         ,@Culture 
         ,@Other 
         ,@IPAddress 
         ,@NativeDeviceID); 
       IF(@OldUniqueID != ''-1'' AND @OldUniqueID != @UniqueID) 
       BEGIN 
        EXEC DeleteOldAndroidDeviceID @OldUniqueID, @UniqueID; 
       END 
     END 
LBEXIT: 
     IF @TranCount = 0 
      COMMIT; 


    END TRY 
    BEGIN CATCH 
     DECLARE @Error INT, @Message VARCHAR(4000), @XState INT; 
     SELECT @Error = ERROR_NUMBER() ,@Message = ERROR_MESSAGE() ,@XState = XACT_STATE(); 

     IF @XState = -1 
      ROLLBACK; 
     IF @XState = 1 AND @TranCount = 0 
      rollback 
     IF @XState = 1 AND @TranCount > 0 
      ROLLBACK TRANSACTION Insertorupdatedevicecatalog; 

     RAISERROR (''Insertorupdatedevicecatalog: %d: %s'', 16, 1, @error, @message) ; 
    END CATCH 
END 
+0

您能舉出一個實際問題的具體例子嗎?如果用戶開始第二次執行,會出現什麼問題?通常應該可以編寫完全安全的代碼,並行運行多次。 –

+0

@Damien_The_Unbeliever,我有一些選擇語句來在插入/更新操作之前獲取變量中的表值。我想確保在這些select語句之後,只有在同一個SP中的插入/更新操作應該運行,否則我的變量可能會有錯誤的值,因爲那時可能是另一個請求中的SP運行完成。 – user960567

+0

看看事務隔離級別和共享鎖使用情況 – LittleSweetSeas

回答

1

由於對同一個事務中的同一表進行兩次更新,發生超時。你可以用case語句來避免它。整個IF ELSE也可以用合併來替換。

MERGE INTO DeviceCatalog DC 
USING (SELECT @UniqueID AS UniqueID) T ON (DC.UniqueID = T.UniqueID) 
WHEN MATCHED THEN 
    UPDATE SET [OS] = @OS 
       ,[Location] = @Geo 
       ,[Culture] = @Culture 
       ,[Other] = @Other 
       ,[IPAddress] = @IPAddress 
       ,[Lastmodifieddate] = (CASE 
             WHEN (LastUpdatedLocationFromJob IS NULL OR [Address] IS NULL OR [City] IS NULL OR [Country] IS NULL OR ISNULL(@Geo.STDistance(LastUpdatedLocationFromJob)/1000,0) > 50) 
             THEN Getdate() 
             ELSE [Lastmodifieddate] 
             END) 
WHEN NOT MATCHED THEN 
    INSERT INTO DeviceCatalog 
         ([OS] 
         ,[UniqueID] 
         ,[Location] 
         ,[Culture] 
         ,[Other] 
         ,[IPAddress] 
         ,[NativeDeviceID]) 
       VALUES (@OS 
         ,@UniqueID 
         ,@Geo 
         ,@Culture 
         ,@Other 
         ,@IPAddress 
         ,@NativeDeviceID) 
WHEN NOT MATCHED BY SOURCE AND @OldUniqueID != ''-1'' AND @OldUniqueID != @UniqueID THEN 
DELETE; 

試試看,並檢查這是否是您的預期。

1

已經討論here

您可以用TSQL實現sp_getapplock它。

但是,你需要一個包裝storedproc或批處理這個。檢查下面的例子,它會幫助你設計你的包裝sp/batch語句。

示例代碼段

Create table MyTable 
(
      RowId int identity(1,1), 
      HitStartedAt datetime, 
      HitTimestamp datetime, 
      UserName varchar(100) 
) 


Go 

Create proc LegacyProc (@user varchar(100), @CalledTime datetime) 
as 
Begin 
      Insert Into MyTable 
      Values(@CalledTime, getdate(), @user); 
      --To wait for 10 sec : not required for your procedures, producing the latency to check the concurrent users action 
      WAITFOR DELAY '000:00:10' 
End 

Go 

Create Proc MyProc 
(
      @user varchar(100) 
) 
as 
Begin 
      Declare @PorcName as NVarchar(1000), @CalledTime datetime 
      Begin Tran 
      --To get the Current SP Name, it should be unique for each SP/each batch 
      SET @PorcName = object_name(@@ProcID) 
      SET @CalledTime = Getdate() 

      --Lock the Current Proc 
      Exec sp_getapplock @Resource = @PorcName, @LockMode = 'Exclusive' 

      --Execute Your Legacy Procedures 
      Exec LegacyProc @user, @CalledTime 

      --Release the lock 
      Exec sp_releaseapplock @Resource = @PorcName 
      Commit Tran 
End 
1

您在DeviceCatalog表,其中[UniqueID的在同一個事務= @UniqueID做兩個單獨的更新。

我敢打賭,你的鎖定/請求超時問題發生時:

IF(@OldGeo爲空或@OldAddress爲空或@OldCity爲空或@OldCountry爲空或ISNULL(@ Geo.STDistance(@ OldGeo)/ 1000,0)> 50)是真實的。

嘗試用這種方法代替這兩個更新。

顯然先在dev中測試。

在else子句中,如果when爲false,您希望它插入一些內容。這裏我只是在更新字段內容之前插入當前字段。

 UPDATE DeviceCatalog 
      SET [OS] = @OS 
       ,[Location] = @Geo 
       ,[Culture] = @Culture 
       ,[Other] = @Other 
       ,[IPAddress] = @IPAddress 
       ,[Lastmodifieddate] = 
          case when (
              @OldGeo is NULL 
              OR 
              @OldAddress is NULL 
              OR 
              @OldCity is NULL 
              OR 
              @OldCountry is NULL 
              OR 
              ISNULL(@Geo.STDistance(@OldGeo)/1000,0) > 50 
            ) then Getdate() 
           else [Lastmodifieddate] 
          end 
      WHERE [UniqueID] = @UniqueID