Giải Pháp Tương Tác Giữa SQL Server và Oracle Trong Môi Trường Đa Luồng

Cần thiết: Di chuyển dữ liệu từ các cột COMPANY, OGB04, OGA01, OGA03 của bảng tạm thời sqlservertemp_table trong SQL Server sang một phiên bản Oracle được kết nối với tên ds nhằm giảm việc truy vấn không cần thiết khi lấy dữ liệu từ SQL Server.

Dưới đây là một kịch bản triển khai đầy đủ bao gồm:

  • Tất cả các đối tượng cơ sở dữ liệu (bảng tạm thời, bảng trung gian, bảng nhật ký)
  • Các thủ tục lưu trữ (điểm nhập chính, xử lý nội bộ, gọi bất đồng bộ)
  • Mechanism xóa bỏ (xóa tự động dữ liệu cũ)
  • Ví dụ về công việc SQL Agent
  • Hỗ trợ gọi API Web (.NET Core ví dụ đơn giản)

Kịch Bản Triển Khai Đầy Đủ

1. Tạo Bảng Trung Gian và Bảng Nhật Ký

-- Bảng trung gian: Lưu trữ dữ liệu từ #Params để xử lý sau này
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[BảngTrungGian_Temp]') AND type in (N'U'))
CREATE TABLE dbo.BảngTrungGian_Temp (
    MaSession VARCHAR(50),
    CongTy VARCHAR(50),
    MaOGB04 VARCHAR(100),
    MaOGA01 VARCHAR(100),
    MaOGA03 VARCHAR(100)
);
GO

-- Bảng nhật ký: Ghi lại thông tin thực thi đồng bộ hóa
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[NhậtKýĐồngBộ]') AND type in (N'U'))
CREATE TABLE dbo.NhậtKýĐồngBộ (
    MaNhậtKý INT IDENTITY PRIMARY KEY,
    MaSession VARCHAR(50),
    ThờiGianBắtĐầu DATETIME,
    ThờiGianKếtThúc DATETIME,
    TrạngThai VARCHAR(20), -- 'ĐãBắtĐầu', 'ĐãHoànTất', 'ThấtBại'
    SốDòng INT,
    ThôngDiễnLỗi NVARCHAR(MAX)
);
GO

2. Thủ Tục Lưu Trữ Xử Lý Nội Bộ `ĐồngBộHoaDữLiệuNộiBộ`

-- Thủ tục: ĐồngBộHoaDữLiệuNộiBộ
-- Chức năng: Chèn dữ liệu từ bảng trung gian vào bảng tạm thời Oracle (ánh xạ trường)
IF OBJECT_ID('dbo.ĐồngBộHoaDữLiệuNộiBộ') IS NOT NULL
    DROP PROCEDURE dbo.ĐồngBộHoaDữLiệuNộiBộ;
GO

CREATE PROCEDURE dbo.ĐồngBộHoaDữLiệuNộiBộ
    @MaSession VARCHAR(50),
    @KíchThướcMônhChia INT = 500  -- Giới hạn số lượng bản ghi chèn mỗi lần, ngăn chặn khóa bảng
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @ThờiGianBắtĐầu DATETIME = GETDATE();
    DECLARE @SốDòng INT;
    DECLARE @TênBảngOracle NVARCHAR(128) = 'TMP_' + REPLACE(CAST(NEWID() AS VARCHAR(36)), '-', '_');
    DECLARE @DanhSáchCột NVARCHAR(MAX);
    DECLARE @CâuSQL NVARCHAR(MAX);

    BEGIN TRY

        -- Tạo bảng tạm thời cấp phiên (mô phỏng tải từ bảng trung gian)
        IF OBJECT_ID('tempdb..#BảngTemp') IS NOT NULL
            DROP TABLE #BảngTemp;

        SELECT *
        INTO #BảngTemp
        FROM dbo.BảngTrungGian_Temp
        WHERE MaSession = @MaSession;

        IF NOT EXISTS (SELECT 1 FROM tempdb.sys.tables WHERE name LIKE '#BảngTemp%')
        BEGIN
            THROW 50001, 'Không tìm thấy dữ liệu tương ứng với MaSession.', 1;
            RETURN;
        END

        -- Lấy danh sách cột
        SELECT @DanhSáchCột = STRING_AGG(QUOTENAME(name), ', ')
        FROM tempdb.sys.columns
        WHERE object_id = OBJECT_ID('tempdb..#BảngTemp');

        -- Tạo bảng Oracle
        IF NOT EXISTS (
            SELECT 1
            FROM OPENQUERY(DS, 'SELECT table_name FROM all_tables WHERE table_name = ''' + UPPER(@TênBảngOracle) + '''' )
        )
        BEGIN
            SET @CâuSQL = 'CREATE TABLE ' + @TênBảngOracle + ' (ma_session VARCHAR2(50), ' +
                       STUFF((SELECT ',' + name + ' VARCHAR2(4000)'
                              FROM tempdb.sys.columns
                              WHERE object_id = OBJECT_ID('tempdb..#BảngTemp')
                              ORDER BY column_id
                              FOR XML PATH('')), 1, 1, '') + ')';
            EXEC (@CâuSQL) AT DS;
        END

        -- Xóa dữ liệu hiện tại của session
        EXEC ('DELETE FROM ' + @TênBảngOracle + ' WHERE ma_session = ''' + @MaSession + '''') AT DS;

        -- Chèn dữ liệu
        WHILE EXISTS(SELECT 1 FROM #BảngTemp)
        BEGIN
            SET @CâuSQL = '
                INSERT INTO OPENQUERY(DS, ''SELECT ma_session, ' + @DanhSáchCột + ' FROM ' + @TênBảngOracle + ''')
                SELECT ''' + @MaSession + ''', ' + @DanhSáchCột + '
                FROM (
                    SELECT TOP (' + CAST(@KíchThướcMônhChia AS NVARCHAR) + ') *
                    FROM #BảngTemp
                ) AS tam';

            EXEC sp_executesql @CâuSQL;

            DELETE TOP (@KíchThướcMônhChia) FROM #BảngTemp;
        END

        SET @SốDòng = (SELECT COUNT(*) FROM BảngTrungGian_Temp WHERE MaSession = @MaSession);

        INSERT INTO dbo.NhậtKýĐồngBộ (MaSession, ThờiGianBắtĐầu, ThờiGianKếtThúc, TrạngThai, SốDòng)
        VALUES (@MaSession, @ThờiGianBắtĐầu, GETDATE(), 'ĐãHoànTất', @SốDòng);

    END TRY
    BEGIN CATCH

        INSERT INTO dbo.NhậtKýĐồngBộ (MaSession, ThờiGianBắtĐầu, ThờiGianKếtThúc, TrạngThai, SốDòng, ThôngDiễnLỗi)
        VALUES (@MaSession, @ThờiGianBắtĐầu, GETDATE(), 'ThấtBại', NULL, ERROR_MESSAGE());

    END CATCH

END
GO

3. Thủ Tục Lưu Trữ Điểm Nhập Chính `ĐồngBộHoaDữLiệuBấtĐồngBộ`

-- Thủ tục: ĐồngBộHoaDữLiệuBấtĐồngBộ
-- Chức năng: Khởi động tác vụ chèn bất đồng bộ
IF OBJECT_ID('dbo.ĐồngBộHoaDữLiệuBấtĐồngBộ') IS NOT NULL
    DROP PROCEDURE dbo.ĐồngBộHoaDữLiệuBấtĐồngBộ;
GO

CREATE PROCEDURE dbo.ĐồngBộHoaDữLiệuBấtĐồngBộ
    @MaSession VARCHAR(50) = NULL OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

    IF OBJECT_ID('tempdb..#ThôngSố') IS NULL
    BEGIN
        THROW 50001, '#ThôngSố bảng tạm thời không tồn tại, vui lòng tạo trước khi gọi thủ tục này.', 1;
        RETURN;
    END

    IF @MaSession IS NULL
        SET @MaSession = REPLACE(CAST(NEWID() AS VARCHAR(36)), '-', '_');

    -- Sao chép #ThôngSố vào bảng trung gian toàn cục
    IF OBJECT_ID('tempdb..#ThôngSốToànCục') IS NOT NULL
        DROP TABLE #ThôngSốToànCục;

    SELECT @MaSession AS MaSession, * INTO #ThôngSốToànCục FROM #ThôngSố;

    INSERT INTO dbo.BảngTrungGian_Temp
    SELECT * FROM #ThôngSốToànCục;

    -- Khởi động công việc SQL Agent (ví dụ cách thay thế tham số)
    EXEC msdb.dbo.sp_start_job @job_name = 'ĐồngBộHoaDữLiệuCôngViec',
                               @step_name = 'Chạy ThủTụcNộiBộ',
                               @input_parameters = N'@MaSession=''' + @MaSession + '''';

    SELECT @MaSession AS MaSession;

END
GO

4. Công Việc Dọn Dẹp Định Thời (SQL Server Agent Job hoặc Script Công Việc)

Tạo Thủ Tục Dọn Dẹp

-- Xóa dữ liệu session quá hạn (ví dụ như sau 1 giờ)
IF OBJECT_ID('dbo.DọnDẹpSessionCu') IS NOT NULL
    DROP PROCEDURE dbo.DọnDẹpSessionCu;
GO

CREATE PROCEDURE dbo.DọnDẹpSessionCu
AS
BEGIN
    SET NOCOUNT ON;

    -- Xóa dữ liệu nhật ký sau 1 giờ
    DELETE FROM dbo.NhậtKýĐồngBộ
    WHERE ThờiGianBắtĐầu < DATEADD(HOUR, -1, GETDATE());

    -- Xóa dữ liệu từ bảng trung gian quá hạn
    DELETE btg
    FROM dbo.BảngTrungGian_Temp btg
    LEFT JOIN dbo.NhậtKýĐồngBộ nk ON btg.MaSession = nk.MaSession
    WHERE nk.MaSession IS NULL
      AND btg.MaSession IN (
          SELECT MaSession
          FROM dbo.NhậtKýĐồngBộ
          WHERE ThờiGianBắtĐầu < DATEADD(HOUR, -1, GETDATE())
      );
END
GO

Hỗ Trợ Gọi API Web (Ví dụ .NET Core)

Giả sử bạn sử dụng .NET Core để tương tác với SQL Server, dưới đây là một ví dụ đơn giản về controller:

[ApiController]
[Route("api/[controller]")]
public class ĐồngBộController : ControllerBase
{
    private readonly string _chuỗiKếtNối;

    public ĐồngBộController(IConfiguration cấuHình)
    {
        _chuỗiKếtNối = cấuHình.GetConnectionString("MặcĐịnh");
    }

    [HttpPost("bắtđầu")]
    public async Task<IActionResult> BắtĐầuBấtĐồngBộ([FromBody] YêuCầuThôngSố yêuCầu)
    {
        var maSession = Guid.NewGuid().ToString();

        using (var kếtNối = new SqlConnection(_chuỗiKếtNối))
        {
            await kếtNối.MởAsync();

            // Tạo bảng #ThôngSố và chèn dữ liệu
            using (var lệnh = new SqlCommand())
            {
                lệnh.KếtNối = kếtNối;
                lệnh.CâuTruyVấn = $@"
                    CREATE TABLE #ThôngSố (CongTy VARCHAR(50), MaOGB04 VARCHAR(100), MaOGA01 VARCHAR(100), MaOGA03 VARCHAR(100));
                    
                    INSERT INTO #ThôngSố (CongTy, MaOGB04, MaOGA01, MaOGA03)
                    VALUES {string.Join(",", yêuCầu.BảnGhi.Select(r => $"'{r.CôngTy}','{r.MaOGB04}','{r.MaOGA01}','{r.MaOGA03}'"))};

                    EXEC dbo.ĐồngBộHoaDữLiệuBấtĐồngBộ @MaSession OUTPUT;
                ";
                lệnh.ThamSố.Add(new SqlParameter("@MaSession", SqlDbType.VarChar, 50) { Hướng = ParameterDirection.Output });

                await lệnh.ThựcThiNonQueryAsync();

                var maSessionĐầuRa = lệnh.ThamSố["@MaSession"].GiáTri?.ToString() ?? maSession;

                return Ok(new { MaSession = maSessionĐầuRa });
            }
        }
    }

    [HttpGet("trạngthái/{maSession}")]
    public async Task<IActionResult> LấyTrạngThái(string maSession)
    {
        using (var kếtNối = new SqlConnection(_chuỗiKếtNối))
        {
            var nhậtKý = await kếtNối.TruyVấnFirstOrDefaultAsync(
                "SELECT * FROM dbo.NhậtKýĐồngBộ WHERE MaSession = @MaSession",
                new { MaSession = maSession });

            return Ok(nhậtKý);
        }
    }
}

public class YêuCầuThôngSố
{
    public List BảnGhi { get; set; }
}

public class BảnGhiThôngSố
{
    public string CôngTy { get; set; }
    public string MaOGB04 { get; set; }
    public string MaOGA01 { get; set; }
    public string MaOGA03 { get; set; }
}

public class NhậtKýĐồngBộ
{
    public int MaNhậtKý { get; set; }
    public string MaSession { get; set; }
    public DateTime ThờiGianBắtĐầu { get; set; }
    public DateTime? ThờiGianKếtThúc { get; set; }
    public string TrạngThai { get; set; }
    public int? SốDòng { get; set; }
    public string ThôngDiễnLỗi { get; set; }
}

Thẻ: SQL Server Oracle Cross-Database High-Concurrency Stored Procedures

Đăng vào ngày 3 tháng 7 lúc 16:38