重写了一个BOM查询的脚本

企业产品日益增加,采购人员复核成本时存在效率低下,工作强度大的问题。之前有写过一个工具来配合采购的工作,一直以来,效果还是不错的。近来,凸现了一些不满足于实际需求的障碍存在。为此,在MES系统成功落地之后,有了更多的时间,重写一个版本的BOM查询脚本。在逻辑上有较大的更新,主要是实现了纯SQL语句的形式,CTE的应用场景。通过实践,验证了一直在脑海中的思维,BOM的查询一直存在效率低下的问题,此次用CTE逻辑重写之后,有明显的改善。之前查询一个BOM耗时需要8秒,反应到界面上的耗时一般会有30-40秒!

-- ============================================================

-- BOM 展开递归查询(最终优化版 v4)

-- 优化内容:将 4 个标量函数全部改写为 CTE + LEFT JOIN

-- ============================================================

-- 1. 判断临时表 #BOM 是否存在,如果存在则删除

IF OBJECT_ID('tempdb..#BOM') IS NOT NULL

BEGIN

DROP TABLE #BOM;

END;

-- ============================================================

-- 2. 构建 BOM 基础数据临时表

-- ============================================================

SELECT a.ID,

RIGHT('000' + CAST((CAST(a.Sequence AS INT) / 10) AS VARCHAR(100)), 3) sequence,

b.ItemMaster p_itemmaster,

d.Code p_code,

d.Name p_name,

d.ItemFormAttribute p_ItemFormAttribute,

a.ItemMaster s_itemmaster,

c.Code s_code,

c.Name s_name,

c.SPECS s_specs,

c.PurchaseUOM s_uom,

e.Code s_uomcode,

c.MainItemCategory AS 主分类,

A42.Code AS 主分类_Code,

A43.Name AS 主分类_Name,

ISNULL(A16.Code, '-') AS 成本币种_Code,

ISNULL(A17.Name, '-') AS 成本币种_Name,

c.RefrenceCost AS 参考成本,

c.ItemFormAttribute s_ItemFormAttribute,

CASE c.ItemFormAttribute

WHEN 0 THEN

'模型'

WHEN 1 THEN

'按订单拣货'

WHEN 2 THEN

'按订单装配'

WHEN 3 THEN

'可选类'

WHEN 4 THEN

'委外加工件'

WHEN 5 THEN

'工序委外件'

WHEN 6 THEN

'虚拟'

WHEN 7 THEN

'计划'

WHEN 8 THEN

'系列'

WHEN 9 THEN

'采购件'

WHEN 10 THEN

'制造件'

WHEN 11 THEN

'最终装配件'

WHEN 12 THEN

'套件'

WHEN 13 THEN

'工艺'

WHEN 14 THEN

'低值易耗品'

WHEN 15 THEN

'商品'

WHEN 16 THEN

'杂项料品'

WHEN 17 THEN

'服务'

WHEN 18 THEN

'资产'

WHEN 19 THEN

'备件'

WHEN 20 THEN

'配方'

WHEN 21 THEN

'重复'

WHEN 22 THEN

'费用性料品'

ELSE

'Unknow'

END AS 料品形态属性,

ISNULL(f.Supplier, '-') AS s_SupplierID,

ISNULL(g.Name, '-') AS s_SupplierName,

a.IssueOrg orgid

INTO #BOM

FROM dbo.CBO_BOMComponent AS a

LEFT JOIN dbo.CBO_BOMMaster AS b

ON a.BOMMaster = b.ID

LEFT JOIN dbo.CBO_ItemMaster AS c

ON a.ItemMaster = c.ID

LEFT JOIN dbo.CBO_ItemMaster AS d

ON b.ItemMaster = d.ID

LEFT JOIN dbo.Base_UOM AS e

ON c.PurchaseUOM = e.ID

LEFT JOIN dbo.CBO_PurchaseInfo f

ON f.ItemMaster = c.ID

LEFT JOIN dbo.CBO_Supplier_Trl g

ON f.Supplier = g.ID

LEFT JOIN dbo.CBO_Category AS A42

ON (c.MainItemCategory = A42.ID)

LEFT JOIN dbo.CBO_Category_Trl AS A43

ON (A43.SysMLFlag = 'zh-CN')

AND (A42.ID = A43.ID)

LEFT JOIN Base_Currency AS A16

ON (c.CostCurrency = A16.ID)

LEFT JOIN Base_Currency_Trl AS A17

ON (A17.SysMLFlag = 'zh-CN')

AND (A16.ID = A17.ID)

WHERE a.IssueOrg = 1002204180083960;

-- 建索引,加速递归 JOIN

CREATE INDEX Index_BOM_SName ON #BOM (p_itemmaster, s_itemmaster);

-- ============================================================

-- 3. 用 CTE 替代 4 个标量函数

-- ============================================================

-- 3a. 最近一次采购的供应商名称 + 价格(合并原 FN_LYJ_GetItemLastSupplierName 和 FN_LYJ_GetItemLastPrice)

-- 原函数:逐行查询 PM_POLINE,按 CreatedOn DESC 取 TOP 1

-- 优化:一次扫描,ROW_NUMBER 分组取最新

WITH LatestSupplier

AS (SELECT ItemInfo_ItemCode,

Payer_ShortName,

FinallyPriceTC,

ROW_NUMBER() OVER (PARTITION BY ItemInfo_ItemCode ORDER BY CreatedOn DESC) AS rn

FROM dbo.PM_POLine

WHERE ItemInfo_ItemCode IS NOT NULL),

-- 3b. 最近一次有效价目表价格(替代 FN_LYJ_GetItemPurLnetPrice)

-- 原函数:逐行查询 PPR_PurPriceList/Line + CBO_ItemMaster,筛选有效记录按 FromDate DESC 取 TOP 1

-- 优化:一次扫描,ROW_NUMBER 分组取最新

LatestPrice

AS (SELECT b.ItemInfo_ItemCode,

ROUND(b.Price, 4) AS PurLnetPrice,

ROW_NUMBER() OVER (PARTITION BY b.ItemInfo_ItemCode ORDER BY b.FromDate DESC) AS rn

FROM dbo.PPR_PurPriceList a

INNER JOIN dbo.PPR_PurPriceLine b

ON a.ID = b.PurPriceList

INNER JOIN dbo.CBO_ItemMaster c

ON b.ItemInfo_ItemCode = c.Code

WHERE b.ToDate > GETDATE()

AND a.Cancel_Canceled = 0

AND a.Status = 2

AND c.Org = 1002204180083960),

-- 3c. 料品最终成本(替代 FN_LYJ_GetItemFinalCostPrice)

-- 原函数:逐行查询 IC_ItemCost + CBO_ItemMaster,取三项成本之和

-- 优化:一次扫描,ROW_NUMBER 分组

ItemCost

AS (SELECT b.Code AS ItemCode,

a.Org AS OrgID,

(ISNULL(a.ElementTypeCost_MaterialCurrentCost, 0) + ISNULL(a.ElementTypeCost_SubcontractCurrentCost, 0)

  • ISNULL(a.ElementTypeCost_MaterialPriorCost, 0)

) AS FinalCostPrice,

ROW_NUMBER() OVER (PARTITION BY b.Code, a.Org ORDER BY (SELECT NULL)) AS rn

FROM dbo.IC_ItemCost a

INNER JOIN dbo.CBO_ItemMaster b

ON a.ItemMaster = b.ID

WHERE a.Org = 1002204180083960),

-- ============================================================

-- 4. 递归 CTE 展开 BOM

-- ============================================================

cte

AS (

-- 4a. 定位点成员(根节点)

SELECT TOP 1

1 AS lv,

CAST('000' AS VARCHAR(100)) AS px,

sequence,

ID,

p_itemmaster,

p_code,

p_name,

p_ItemFormAttribute,

p_itemmaster AS s_itemmaster,

s_code,

p_name AS s_name,

s_specs,

参考成本,

s_uom,

s_uomcode,

主分类_Name,

料品形态属性,

s_SupplierID,

s_SupplierName,

orgid

FROM #BOM

WHERE p_itemmaster = '1002205160016301' -- 母件的 ItemMaster

AND orgid = 1002204180083960

UNION ALL

-- 4b. 递归成员

SELECT lv = e.lv + 1,

CAST(e.px + '-' + CAST(a.sequence AS VARCHAR(100)) AS VARCHAR(100)) AS px,

a.sequence,

a.ID,

a.p_itemmaster,

a.p_code,

a.p_name,

a.p_ItemFormAttribute,

a.s_itemmaster,

a.s_code,

a.s_name,

a.s_specs,

a.参考成本,

a.s_uom,

a.s_uomcode,

a.主分类_Name,

a.料品形态属性,

a.s_SupplierID,

a.s_SupplierName,

a.orgid

FROM #BOM AS a

INNER JOIN cte AS e

ON a.p_itemmaster = e.s_itemmaster -- 父项 = 子项,向下展开

)

-- ============================================================

-- 5. 最终查询:LEFT JOIN 三个 CTE,替代标量函数调用

-- ============================================================

SELECT cte.lv,

cte.px,

cte.p_code '上阶料号',

cte.p_name '上阶料品名称',

cte.s_code '子件料号',

cte.s_name '子件名称',

cte.s_specs '子件规格',

cte.参考成本,

cte.s_uomcode '单位',

cte.主分类_Name,

cte.料品形态属性,

cte.s_SupplierName AS 主供应商,

ISNULL(ls.Payer_ShortName, '-') AS 最近一次订单厂商,

ISNULL(ls.FinallyPriceTC, '0.0') AS 最近一次订单价格,

ISNULL(lp.PurLnetPrice, '0.0') AS 最近一次价目表,

ISNULL(ic.FinalCostPrice, '0.0') AS 料品成本

FROM cte

LEFT JOIN LatestSupplier ls

ON ls.ItemInfo_ItemCode = cte.p_code

AND ls.rn = 1

LEFT JOIN LatestPrice lp

ON lp.ItemInfo_ItemCode = cte.p_code

AND lp.rn = 1

LEFT JOIN ItemCost ic

ON ic.ItemCode = cte.p_code

AND ic.OrgID = cte.orgid

AND ic.rn = 1

WHERE cte.orgid = 1002204180083960

ORDER BY cte.px

OPTION (MAXRECURSION 20); -- 最大递归层数 20

GO

相关推荐
事圆则缓1 小时前
Kotlin 集合与序列:给页面一份清楚的数据
开发语言·windows·kotlin
事圆则缓1 小时前
Kotlin 异步与并发:从一次搜索请求走到状态流
android·开发语言·kotlin
从小白到软工2 小时前
4-3 项目-学生成绩管理系统(上)
c语言·开发语言·数据结构·经验分享·ai编程
外收内放2 小时前
Python基础语法练习题(72)
开发语言·python
无忧.芙桃2 小时前
C++语言原理与实践(十三):stack、queue 和 priority_queue的介绍与使用
开发语言·c++·栈·队列
Shaoshing2 小时前
消息发送
java·开发语言
乌暮3 小时前
从 `println` 到生产级日志:Spring Boot 日志体系完全指南
java·开发语言·jvm·spring boot·后端·学习·面试
星夜离尘563 小时前
MATLAB 定点量化误差实验 半个 LSB 的界限为什么挡不住削顶失真
开发语言·matlab