1. 程式人生 > >SQL Server 遊標的使用示例

SQL Server 遊標的使用示例

Ø  簡介

本文主要記錄 MSSQL 中的遊標使用示例,在有必要時方便借鑑查閱。遊標一般定義在某段功能性的 SQL 語句中,或者儲存過程中。之所以選擇用它,是因為有時候無法使用簡單的 SQL 語句滿足我們需求,比如需要對結果集中的每一條資料,根據不同條件進行不同操作(CRUD),這時我們就可以使用遊標來完成。

提示:來之 DBA 的傑作,哈哈~~

 

1.   示例1

1)   本示例,用於初始化某新表的資料。使用遊標遍歷查詢結果集,根據遍歷的資料再插入另外兩張表。

2)   注意:在遊標開啟後,如果在未關閉之前發生了異常,遊標不能正常關閉,會導致該遊標一直存在。所以,我們可以將遊標定義在

TRY 語句之前,在 CATCH 語句中進行關閉。SQL 程式碼如下:

IF(OBJECT_ID('SP_Init_CustomerNewOpening', 'P') IS NOT NULL)

    DROP PROCEDURE SP_Init_CustomerNewOpening;

GO

CREATE PROCEDURE SP_Init_CustomerNewOpening

AS

    INSERT INTO Sys_DbLog(LogTime, Type, Item, Message, Remark) VALUES

(GETDATE(), 'info', 'SP_Init_CustomerNewOpening', '儲存過程執行開始', NULL);

    BEGIN TRY

        BEGIN TRANSACTION;

 

        DECLARE @RewardsMoney decimal(18,2) = CAST(ISNULL((SELECT Value FROM Config WHERE Id=79),

'0') AS decimal(18,2));

        DECLARE @NewOpenMoney decimal(18,2) = 0;

        DECLARE @CustNewOpenId int;

        DECLARE @PayTime datetime, @Address nvarchar(500), @CustomerId bigint, @EmplCityId int, @EmployeeId int, @RealTotal decimal(18,2);

        DECLARE cursor_CustomerNewOpening CURSOR GLOBAL SCROLL DYNAMIC SCROLL_LOCKS

        FOR SELECT PayTime, Address, CustomerId, EmplCityId, EmployeeId, SUM(RealTotal) AS RealTotal FROM

        (

            SELECT T1.PayTime, T1.Address, T1.CustomerId, T1.EmplCityId, T1.EmployeeId, T2.RealTotal FROM

            (

                SELECT T2.PayTime, T2.Address, T1.Id AS CustomerId, T3.CityId AS EmplCityId, T3.EmployeeId FROM Customer AS T1

                INNER JOIN Orders AS T2 ON(T1.BdBonus = T2.Id)

                INNER JOIN Sys_EmployeeProfile AS T3 ON(T2.SalesUserId = T3.EmployeeId)

                WHERE T1.BdBonus > 0

            ) AS T1

            INNER JOIN Orders AS T2 ON(T1.PayTime = T2.PayTime AND T1.Address = T2.Address)

            WHERE 1=1

        ) AS T WHERE 1=1

        GROUP BY PayTime, Address, CustomerId, EmplCityId, EmployeeId;

        OPEN cursor_CustomerNewOpening;

        FETCH FIRST FROM cursor_CustomerNewOpening INTO @PayTime, @Address, @CustomerId, @EmplCityId, @EmployeeId, @RealTotal;

        WHILE(@@FETCH_STATUS = 0)

        BEGIN

            --插入客戶新開

            SET @NewOpenMoney = CASE WHEN @RealTotal > 999 THEN 999 WHEN @RealTotal > 699 THEN 699 ELSE @RealTotal END;

            INSERT INTO Crm_CustomerNewOpening(CustomerId, EmplCityId, EmployeeId, RewardsMoney, NewOpenMoney, RealTotal,

                RefundmentMoney, PayTime, NewOpenStatus, AuditorId, AuditorTime, AuditDesc, CreateTime, UpdateTime)

            VALUES(@CustomerId, @EmplCityId, @EmployeeId, @RewardsMoney, @NewOpenMoney, @RealTotal,

                0, @PayTime, 4/*歷史有效*/, NULL, NULL, NULL, GETDATE(), NULL);

            SET @CustNewOpenId = SCOPE_IDENTITY();

           

            --插入訂單新開

            INSERT INTO Crm_OrderNewOpening(CustNewOpenId, OrderId, OrderNum, RealTotal, RefundmentMoney, CreateTime, UpdateTime)

            SELECT 0, T1.Id, T1.OrderNum, T1.RealTotal, 0, GETDATE(), NULL FROM Orders AS T1

            WHERE 1=1

            AND T1.PayTime = @PayTime

            AND T1.Address = @Address;

 

            FETCH NEXT FROM cursor_CustomerNewOpening INTO @PayTime, @Address, @CustomerId, @EmplCityId, @EmployeeId, @RealTotal;

        END

        CLOSE cursor_CustomerNewOpening;

        DEALLOCATE cursor_CustomerNewOpening;

        COMMIT;

        INSERT INTO Sys_DbLog(LogTime, Type, Item, Message, Remark) VALUES(GETDATE(), 'info', 'SP_Init_CustomerNewOpening', '儲存過程執行結束', NULL);

    END TRY

    BEGIN CATCH

        ROLLBACK;

        DECLARE @Message varchar(8000) = '錯誤:行號[' + CAST(ERROR_LINE() AS nvarchar(20)) + ']' + ERROR_MESSAGE();

        INSERT INTO Sys_DbLog(LogTime, Type, Item, Message, Remark) VALUES(GETDATE(), 'error', 'SP_Init_CustomerNewOpening', @Message, '事物已回滾');

    END CATCH

GO

 

2.   示例2

1)   本示例,建立了一個儲存過程,建立了兩個遊標。

2)   第一個遊標,使用遊標完成更新遍歷的記錄,再使用遍歷的資料完成對另外兩張表的 Insert 操作。

3)   第二個遊標,完成對3張表的更新操作,在示例中使用了當前遊標記錄作為更新條件,例如:WHERE CURRENT OF cursor_Crm_Refundment

4)   該儲存過程用於定時任務執行,比在程式中去查詢資料,然後再遍歷寫入資料效率上還是比較有優勢的,而且便於維護。SQL 程式碼如下:

IF(OBJECT_ID('SP_Add_CustomerNewOpening', 'P') IS NOT NULL)

    DROP PROCEDURE SP_Add_CustomerNewOpening;

GO

CREATE PROCEDURE SP_Add_CustomerNewOpening

(

    @StartTime datetime,                --開始時間

    @EndTime datetime                   --結束時間

)

AS

    INSERT INTO Sys_DbLog(LogTime, Type, Item, Message, Remark) VALUES(GETDATE(), 'info', 'SP_Add_CustomerNewOpening', '開始儲存過程執行開始', NULL);

    DECLARE @RewardsMoney decimal(18,2) = CAST(ISNULL((SELECT Value FROM Config WHERE Id=79), '0') AS decimal(18,2));

    DECLARE @NewOpenMoney decimal(18,2) = CAST(ISNULL((SELECT Value FROM Config WHERE Id=80), '0') AS decimal(18,2));

    BEGIN TRY

        BEGIN TRANSACTION;

 

        --1. 新增新開客戶

        DECLARE @OrderNewOpenId int, @CustNewOpenId int;

        DECLARE @UserId bigint, @PayTime datetime, @Address nvarchar(500), @CustomerId bigint, @EmplCityId int, @EmployeeId int, @RealTotal decimal(18,2);

<