perf(selfop): skip unavailable dashboard aggregates

This commit is contained in:
2026-10-13 03:45:01 +08:00
parent 7ae91eb276
commit ca4e7086e9
3 changed files with 52 additions and 7 deletions
@@ -17,7 +17,9 @@ public final class AnalyticsInventorySql {
private static String defectiveQuantity(){return "CASE WHEN MIN(CASE WHEN product_quality NOT IN (0,1) THEN 0 WHEN product_quality=1 THEN CASE WHEN "+valid("physical_quantity")+" THEN 1 ELSE 0 END ELSE 1 END)=1 "
+"THEN SUM(CASE WHEN product_quality=1 THEN physical_quantity ELSE 0 END) END";}
static String cte(AnalyticsSqlRequest p){return cte(p,false);}
private static String cte(AnalyticsSqlRequest p,boolean stockFirst){
static String dashboardCte(AnalyticsSqlRequest p){return cte(p,false,true);}
private static String cte(AnalyticsSqlRequest p,boolean stockFirst){return cte(p,stockFirst,false);}
private static String cte(AnalyticsSqlRequest p,boolean stockFirst,boolean dashboard){
return "WITH candidates AS ("+keys(p)+"), profiles AS (SELECT c.customer_code,c.sku_code,COALESCE(i.product_name,s.product_name) product_name,"
+"CASE WHEN i.quantity_unit='PCS' THEN 1 ELSE 0 END stock_unit_valid,"
+"CASE WHEN s.sku_code IS NULL OR s.quantity_unit='PCS' THEN 1 ELSE 0 END sales_unit_valid,"
@@ -29,9 +31,10 @@ public final class AnalyticsInventorySql {
+" stock AS (SELECT customer_code,sku_code,"+quantity("physical_quantity")+" physical_quantity,"+quantity("frozen_quantity")+" frozen_quantity,"
+quantity("transit_quantity")+" transit_quantity,MAX(CASE WHEN product_quality=0 AND "+valid("available_quantity")+" THEN available_quantity END) available_quantity,"
+defectiveQuantity()+" defective_physical_quantity,MAX(observed_at_utc) observed_at_utc "
+"FROM ll_selfop_inventory_snapshot WHERE snapshot_run_id=#{p.inventoryRun} AND"+scope(p)+" GROUP BY customer_code,sku_code),"
+"FROM ll_selfop_inventory_snapshot WHERE "+(dashboard&&!p.stockFresh()?"1=0 /* dashboard unused stock */ AND ":"")
+"snapshot_run_id=#{p.inventoryRun} AND"+scope(p)+" GROUP BY customer_code,sku_code),"
+(stockFirst?stockPageKeys():"")
+salesCte(p,stockFirst)
+salesCte(p,stockFirst,dashboard&&!p.complete30())
+" values_by_sku AS (SELECT c.customer_code,c.sku_code,c.product_name,c.unit_valid,"
+"CASE WHEN c.sales_evidence IS NULL THEN c.stock_evidence WHEN c.stock_evidence IS NULL THEN c.sales_evidence ELSE LEAST(c.sales_evidence,c.stock_evidence) END evidence_at_utc,"
+stockValue("physical_quantity")+","+stockValue("available_quantity")+","+stockValue("frozen_quantity")+","+stockValue("transit_quantity")+","+stockValue("defective_physical_quantity")+",st.observed_at_utc,"
@@ -47,12 +50,12 @@ public final class AnalyticsInventorySql {
+"WHEN physical_quantity>0 AND available_quantity>0 AND outbound_quantity30d IS NOT NULL THEN CASE WHEN outbound_quantity30d>0 AND available_quantity*30>outbound_quantity30d*#{o.coverageDaysThreshold} THEN 1 ELSE 0 END END above_threshold "
+"FROM qualified q), result AS (SELECT c.*,CASE WHEN physical_quantity IS NULL OR available_quantity IS NULL OR outbound_quantity30d IS NULL THEN 1 ELSE 0 END unassessable FROM calculated c) ";
}
private static String salesCte(AnalyticsSqlRequest p,boolean stockFirst){String d=stockFirst?"d.":"";
private static String salesCte(AnalyticsSqlRequest p,boolean stockFirst,boolean unused){String d=stockFirst?"d.":"";
return " sales AS (SELECT "+(stockFirst?"/*+ JOIN_ORDER(k,d) INDEX(d idx_selfop_daily_sku_cover) */ ":"")
+d+"customer_code,"+d+"sku_code,SUM("+d+"outbound_quantity) observed_quantity,SUM("+d+"outbound_order_count) order_count,"
+"MIN(CASE WHEN "+d+"quantity_unit='PCS' AND "+d+"outbound_quantity>=0 THEN 1 ELSE 0 END) unit_valid FROM ll_selfop_sku_daily_sales "
+(stockFirst?"d JOIN stock_paged k ON k.customer_code=d.customer_code AND k.sku_code=d.sku_code ":"")
+"WHERE "+d+"sales_run_id=#{p.salesRun} AND"+scope(p,d)+" AND "+d+"business_date>=#{p.from} AND "+d+"business_date<#{p.until} GROUP BY "+d+"customer_code,"+d+"sku_code),";
+"WHERE "+(unused?"1=0 /* dashboard unused sales */ AND ":"")+d+"sales_run_id=#{p.salesRun} AND"+scope(p,d)+" AND "+d+"business_date>=#{p.from} AND "+d+"business_date<#{p.until} GROUP BY "+d+"customer_code,"+d+"sku_code),";
}
private static String stockAmount(String field){return "CASE WHEN c.stock_unit_valid=1 AND #{p.stockFresh}=1 THEN st."+field+" END";}
private static String stockValue(String field){return stockAmount(field)+" "+field;}
@@ -7,7 +7,12 @@ import java.util.Map;
public final class DashboardReadSql {
private DashboardReadSql(){}
public static String report(Map<String,Object> args){
var p=(AnalyticsSqlRequest)args.get("p");StringBuilder sql=new StringBuilder(AnalyticsInventorySql.cte(p));
var p=(AnalyticsSqlRequest)args.get("p");
// Only dashboard outputs are considered here. Without complete30 all sales-dependent
// rankings are empty; observed sales still come from the independent daily query.
// Stale stock quantities are already masked to NULL. Keep both profile versions so
// candidate identities, observation evidence and unknown reasons remain unchanged.
StringBuilder sql=new StringBuilder(AnalyticsInventorySql.dashboardCte(p));
// SKU identity is unique already; DISTINCT is a MySQL merge barrier so the four consumers
// share one calculation rather than expanding profile/stock/sales joins for each ranking.
sql.append(", dashboard_result AS (SELECT DISTINCT * FROM result)");