AX调用数据库储存过程

在AX开发的中,如果迂到要从两千万级的数据表中提取记录并加以运行生成报表,常规的 while select 代码执行效率极低,用户通常都等不到结果就失去信心了。此时,可以考虑调用数据库的储存过程来实现,储存过程全程在数据库里运行,只提取符合条件的记录给AX,大大减轻网络和AOS的负担,效率提升不止100位。

下面是生成物料库存周转率的代码实例,目的:根據用戶選定的倉庫,開始日期,截止日期,生成庫存物料周轉率報表,報表關鍵欄位:物料編號,物料名稱,單位,成本單價,盤點基數,期初庫存數量,期初庫存貨值,期間出庫數量,期間出庫貨值,期末庫存數量,期末庫存貨值,周轉率。

欄位說明:

盤點基數:距離起始日期之前最近的盤點日期及盤點數量,作為盤點基數。

期初庫存數量:盤點基數 加減 從盤點日到起始日的出入倉數量

期間出庫數量:起始日期到截止日期之間的出倉數量累計

期末庫存數量:期初庫存數量 加減 從起始日期到截止日期的出入倉數量

庫存貨值 = 成本單價 * 數量

周轉率% = 期間出庫貨值*100 / (期初庫存貨值+期末庫存貨值)/2

整个项目分两部份:一、数据库中的存储过程;二、AX代码。

数据库存储过程如下:

USE PROD
GO

IF EXISTS (SELECT * FROM sysobjects WHERE id = OBJECT_ID(N'[dbo].[SPL_CalcInventoryTurnover]') AND OBJECTPROPERTY(id, N'IsProcedure') = 1)
    DROP PROCEDURE [dbo].[SPL_CalcInventoryTurnover]
GO

CREATE PROCEDURE [dbo].[SPL_CalcInventoryTurnover]
    @CountingDate CHAR(8),
    @DateFrom CHAR(8),
    @DateTo CHAR(8),
    @InventDimId VARCHAR(20),
    @DataAreaId CHAR(3)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @CountDate DATETIME, @FromDate DATETIME, @ToDate DATETIME;
    SET @CountDate = CONVERT(DATETIME, @CountingDate, 112);
    SET @FromDate  = CONVERT(DATETIME, @DateFrom, 112);
    SET @ToDate    = CONVERT(DATETIME, @DateTo, 112);

    -- 主結果表
    CREATE TABLE #Turnover (
        ItemId VARCHAR(20) PRIMARY KEY,
        CountingQty DECIMAL(28,12) DEFAULT 0,
        OpeningQty DECIMAL(28,12) DEFAULT 0,
        ClosingQty DECIMAL(28,12) DEFAULT 0,
        IssueQty DECIMAL(28,12) DEFAULT 0
    );

    -- 預定交易聚合表
    CREATE TABLE #TransSum (
        ItemId VARCHAR(20) PRIMARY KEY,
        OpeningChange DECIMAL(28,12) DEFAULT 0,
        ClosingChange DECIMAL(28,12) DEFAULT 0,
        IssueQty DECIMAL(28,12) DEFAULT 0
    );

    -- 1. 盤點基準庫存
    INSERT INTO #Turnover (ItemId, CountingQty)
    SELECT ItemId, SUM(Counted)
    FROM InventJournalTrans WITH (NOLOCK)
    WHERE Voucher > ''
      AND Counted > 0
      AND InventDimId = @InventDimId
      AND JournalType = 4
      AND TransDate = @CountDate
      AND DataAreaId = @DataAreaId
    GROUP BY ItemId;

    -- 2. 補充新物料
    INSERT INTO #Turnover (ItemId, CountingQty)
        SELECT ItemId, 0
        FROM InventTrans WITH (NOLOCK)
        WHERE InventDimId = @InventDimId
          AND DatePhysical > @CountDate
          AND DatePhysical <= @ToDate
          AND DataAreaId = @DataAreaId
          AND TransType <= 10
          AND NOT EXISTS (
              SELECT 1 FROM #Turnover t WHERE t.ItemId = InventTrans.ItemId)
        GROUP BY ItemId;  

    -- 3. 預聚合交易數據
    INSERT INTO #TransSum (ItemId, OpeningChange, ClosingChange, IssueQty)
    SELECT 
        ItemId,
        SUM(CASE WHEN DatePhysical > @CountDate AND DatePhysical <= @FromDate THEN Qty ELSE 0 END),
        SUM(CASE WHEN DatePhysical > @FromDate AND DatePhysical <= @ToDate THEN Qty ELSE 0 END),
        SUM(CASE WHEN DatePhysical >= @FromDate AND DatePhysical <= @ToDate AND Qty < 0 THEN -Qty ELSE 0 END)
    FROM InventTrans WITH (NOLOCK)
    WHERE InventDimId = @InventDimId
      AND DataAreaId = @DataAreaId
      AND TransType <= 10
      AND DatePhysical > @CountDate
      AND DatePhysical <= @ToDate
    GROUP BY ItemId;

    -- 4. 合併到主表
    UPDATE t
    SET 
        OpeningQty = t.CountingQty + ISNULL(s.OpeningChange, 0),
        ClosingQty = t.CountingQty + ISNULL(s.OpeningChange, 0) + ISNULL(s.ClosingChange, 0),
        IssueQty = ISNULL(s.IssueQty, 0)
    FROM #Turnover t
    LEFT JOIN #TransSum s ON t.ItemId = s.ItemId;

    -- 5. 回傳結果
    SELECT ItemId, CountingQty, OpeningQty, ClosingQty, IssueQty
    FROM #Turnover
    ORDER BY ItemId;

    DROP TABLE #TransSum;
    DROP TABLE #Turnover;
END
GO

GRANT EXECUTE ON [dbo].[SPL_CalcInventoryTurnover] TO axView
GO

AX中调用存储过程并在FORM的GRID中展示结果的代码

//sandal 全部物料指定期间的庫存周轉率
//COM改成command-解决防執行超時问题
void SPL_EnquireNoItem()
{
    InventTable         _inventTable;
    InventTableModule   _inventTableModule;
    str     _strCountDate, _strFromDate, _strToDate;
    COM     adoConnection;
    COM     adoCommand; //New Component
    COM     adoRecordset;
    COM     adoFields;
    COM     adoField;
    COMVariant  _tempVar;
    str     sqlCmd;
    str     _itemId, _tempStr;
    real    _countingQty, _openingQty, _closingQty, _issueQty, _avgAmt;
    int     _recordCount;
    str     _connStr = "Provider=SQLOLEDB.1;Data Source=192.168.0.186;Initial Catalog=PROD;User ID=axView;Password=11111111111;";
    ;
    _strCountDate   = date2str(g_ckDate,321,2,0,2,0,4); //轉字符YYYYMMDD
    _strFromDate    = date2str(g_FromDate,321,2,0,2,0,4);
    _strToDate      = date2str(g_ToDate,321,2,0,2,0,4);

    //臨時驗證參數代碼
    //info(strFmt("盤點日: %1, 期初: %2, 期末: %3", _strCountDate, _strFromDate, _strToDate));
    //info(strFmt("倉庫:'%1', 公司: %2", g_fullDimId, curext()));

    //建立鏈接
    adoConnection = new COM("ADODB.Connection");
    adoConnection.Open(_connStr);
    //建立Command對象並設置超時時間
    adoCommand = new COM("ADODB.Command");
    adoCommand.ActiveConnection(adoConnection);
    adoCommand.CommandTimeout(600);  // 超時10分鐘
    adoCommand.CommandType(1);       // adCmdText = 1
    //取過程命令
    sqlCmd = strFmt(
        "EXEC [dbo].[SPL_CalcInventoryTurnover] '%1', '%2', '%3', '%4', '%5'",
        _strCountDate, _strFromDate, _strToDate, g_fullDimId, curext() );
    adoCommand.CommandText(sqlCmd);
    adoRecordset = adoCommand.Execute();  // 通過 Command 執行
    //adoRecordset = adoConnection.Execute(sqlCmd); //超時失敗

    if (!adoRecordset || (adoRecordset.BOF() && adoRecordset.EOF()))
    {
        info('沒有取到數據');
        if (adoRecordset)
            adoRecordset.Close();
        adoConnection.Close();
        return;
    }

     //遍歷寫入臨時表
    _recordCount = 0;
    adoRecordset.MoveFirst();
    while (!adoRecordset.EOF())
    {
        _countingQty= 0;
        _openingQty = 0;
        _closingQty = 0;
        _issueQty   = 0;
        adoFields   = adoRecordset.Fields();
        adoField    = adoFields.Item(0);
        _tempVar    = adoField.Value();
        _itemId     = (_tempVar.variantType() == 0) ? "" : _tempVar.bstr();
        adoField    = adoFields.Item(1);
        _tempVar    = adoField.Value();
        _countingQty= (_tempVar.variantType() == 0) ? 0 : _tempVar.decimal();
        adoField = adoFields.Item(2);
        _tempVar    = adoField.Value();
        _openingQty = (_tempVar.variantType() == 0) ? 0 : _tempVar.decimal();
        adoField = adoFields.Item(3);
        _tempVar    = adoField.Value();
        _closingQty = (_tempVar.variantType() == 0) ? 0 : _tempVar.decimal();
        adoField = adoFields.Item(4);
        _tempVar    = adoField.Value();
        _issueQty   = (_tempVar.variantType() == 0) ? 0 : _tempVar.decimal();
        //_issueQty = str2num(_tempStr);    //無效的空值
        //_itemId     = adoRecordset.Fields("ItemId").Value();//Error
        //調試信息
        //info(strFmt("第 %1 條記錄", _recordCount + 1));
        //info(strFmt("ItemId: '%1'", _itemId));

        _inventTable = InventTable::find(_itemId);
        _inventTableModule = InventTableModule::find(_itemId, ModuleInventPurchSales::Invent);

        SPL_tmpEnqTable.clear();
        SPL_tmpEnqTable.ItemID    = _itemId;
        SPL_tmpEnqTable.ItemName  = _inventTable.ItemName;
        SPL_tmpEnqTable.BomUnitID = _inventTable.BOMUnitId;
        SPL_tmpEnqTable.Price     = _inventTableModule.Price;

        // 映射存儲過程結果 → Grid 欄位
        SPL_tmpEnqTable.Qty3 = _countingQty;   // 盤點數量
        SPL_tmpEnqTable.Qty4 = _openingQty;    // 期初數量
        SPL_tmpEnqTable.Qty5 = _issueQty;      // 出庫數量
        SPL_tmpEnqTable.Qty6 = _closingQty;    // 期末數量
        SPL_tmpEnqTable.Amt1 = decRound(_openingQty * SPL_tmpEnqTable.Price, 2);
        SPL_tmpEnqTable.Amt2 = decRound(_issueQty * SPL_tmpEnqTable.Price, 2);
        SPL_tmpEnqTable.Amt3 = decRound(_closingQty * SPL_tmpEnqTable.Price, 2);
        _avgAmt = (SPL_tmpEnqTable.Amt1 + SPL_tmpEnqTable.Amt3)/2;
        if (_avgAmt != 0)
            SPL_tmpEnqTable.Qty1 = decRound(SPL_tmpEnqTable.Amt2*100 / _avgAmt,0);
        else
            SPL_tmpEnqTable.Qty1 = 0;
        SPL_tmpEnqTable.insert();
        _recordCount++;

        adoRecordset.MoveNext();
    }

    //清理資源
    adoRecordset.Close();
    adoConnection.Close();
}

发表评论