SQL 解鎖 - 模擬揀貨位置

更新於 2024/08/27閱讀時間約 8 分鐘
假設公司有5個配送中心(DC),如下圖,但不是所有商品都存放在所有的配送中心,舉例來說某辦公椅僅能存放在DC3和DC4,因此邏輯上美國東岸的需求會由DC3所配送(距離較近),西岸的需求會由DC4所配送。請根據上述邏輯使用SQL來模擬這商品在各地的需求會如何被配送、從哪個配送中心配送 (提供大方向即可)?
raw-image

我們在此篇會使用Tableau的 Superstores 裡的Orders,並會用Microsoft SQL Server 的SQL語法來講解概念。

我們假設所有的訂單都可以從這五個的配送中心直接配送。這五個的配送中心 ZIP code分別是12553, 29172, 60563, 80023, 92551,我們同時列出了經緯度如下表(存於[dbo].DC_ZIP)。

raw-image


計算距離

要能模擬訂單需求如何配送,我們的第一步是算出這些配送中心每筆訂單和這些配送中心的距離,才能知道哪個配送中心是離終點位置最近且有備存貨。我們使用以下的程式碼先生成一個主表。

SELECT
distinct [Product ID]
, LEFT(A.[Product ID],6) PRODUCT_GROUP
, A.[Order ID] [ORDER_ID]
, A.[Postal Code] DEST_ZIP
, A.[Latitude] DEST_LAT
, A.[Longitude] DEST_LONG
, B.DC_NUMBER
, CAST(B.Zip5 AS INT) SOURCE_ZIP
, B.[Latitude] SOURCE_LAT
, B.[Longitude] SOURCE_LONG
INTO [dbo].[SOURCE_DEST_PRODUCT_COMBO]
FROM [dbo].[Orders] A ,[dbo].[DC_ZIP] B


// SQL小貼士
// 1​
SELECT *
INTO [新資料表]
FROM [現有資料表];

// 2
// 在FROM裡,如果沒使用JOIN,直接在兩張資料表中加一個",",意味​會回傳兩表所有的可能排列
// ,也可以使用CROSS JOIN產生笛卡兒乘積 (Cartesian product)。

// 3 LEFT(欄位或字串, 左側擷取字符數)


增加一個距離欄位。

Alter table [dbo].[SOURCE_DEST_PRODUCT_COMBO]
Add Distance float;


使用經緯度求出直線距離。

Update [dbo].[SOURCE_DEST_PRODUCT_COMBO]
SET Distance =
2*ASIN(SQRT(SIN((3.14*SOURCE_LAT/180.0-3.14*DEST_LAT/180.0)/2)*SIN((3.14*SOURCE_LAT/180.0-3.14*DEST_LAT/180.0)/2)
+COS(3.14*SOURCE_LAT/180.0)*COS(3.14*DEST_LAT/180.0)*SIN((3.14*SOURCE_LONG/180.0-3.14*DEST_LONG/180.0)/2)*SIN((3.14*SOURCE_LONG/180.0-3.14*DEST_LONG/180.0)/2)))*3959


讓我們拿一些數字來驗證一下,

  • DC1 ZIP 12553 到某訂單地址 ZIP 42420: 762 英里 vs Google顯示756 英里。
raw-image


  • DC1 ZIP 12553 到某訂單地址 ZIP 90063: 2446 英里 vs Google顯示2445.8英里。
raw-image


找最近的DC

我們先來假設所有產品在每個配送中心都有存放,那邏輯上就是以最近的配送中心(RANKING = 1) 來揀貨並配送給客戶。

ALTER TABLE [dbo].[Orders]
ADD
SOURCE_DC_NUMBER INT;

UPDATE A
SET SOURCE_DC_NUMBER = B.DC_NUMBER
FROM [dbo].[Orders] A
INNER JOIN
(
SELECT *
FROM
(
SELECT DISTINCT DEST_ZIP
, DC_NUMBER
, DISTANCE
, Row_Number () OVER (PARTITION BY DEST_ZIP ORDER BY DISTANCE ASC) RANKING
FROM [dbo].SOURCE_DEST_PRODUCT_COMBO
) A
WHERE RANKING = 1
) B
ON
A.[Postal Code] = B.DEST_ZIP;


// SQL小貼士

// ROW_NUMBER()
// ROW_NUMBER()回傳不重複的累加序號,必須搭配​ OVER ()告訴SQL要如何產生序號。
// 若​需要分組排序,需在OVER()內再搭配PARTITION BY,這樣SQL會在每組組內產生序號。
// 以上述例子,讓SQL​以目的地的郵遞區號分組,並在每組目的地ZIP code以距離(與配送中心的距離)進行排序。
// => PARTITION BY 目的地的郵遞區號 ORDER BY 與配送中心的距離 ASC
// RANKING = 1 意味回傳最近的配送中心。


為避免過於複雜的狀況,我們假設每個Product Group (產品ID前六碼) 有獨特的存放分布,舉例來說。OFF-AP僅能存放在配送中心1-3。


raw-image


以這樣的例子,我們要在上述的程式碼加一些條件,除了要找最近的配送中心,還要找裡面有存貨的配送中心。以上述的例子,原本離配送中心4和5最近的訂單會被配送中心3所配送,因此配送中心3對於OFF-AP類的產品備貨要考慮整個西岸的需求。

ALTER TABLE [dbo].[Orders]
ADD
PRODUCT_GROUP NVARCHAR(255)
, SOURCE_DC_NUMBER_2 INT;

UPDATE A
SET PRODUCT_GROUP = LEFT(A.[Product ID],6)
FROM [dbo].[Orders] A;

UPDATE A
SET SOURCE_DC_NUMBER_2 = B.DC_NUMBER
FROM [dbo].[Orders] A
INNER JOIN
(
SELECT *
FROM
(
SELECT DISTINCT PRODUCT_GROUP
, DEST_ZIP
, DC_NUMBER
, DISTANCE
, Row_Number () Over (PARTITION BY PRODUCT_GROUP, DEST_ZIP ORDER BY DISTANCE Asc) RANKING
FROM [dbo].SOURCE_DEST_PRODUCT_COMBO
WHERE STOCKED = 'STOCKED'
) A
WHERE RANKING = 1
) B
ON
A.PRODUCT_GROUP = B.PRODUCT_GROUP
AND
A.[Postal Code] = B.DEST_ZIP;


// SQL小貼士
// 由於加上了每組Product Group存放分布不同,在PARTITION BY後要以Product Group+目的地郵遞區號進行分組
// => PARTITION BY Product Group, 目的地的郵遞區號 ORDER BY 與配送中心的距離 ASC
// 而且沒有存放的組合被納入排序中​。=> WHERE STOCKED = 'STOCKED'
// RANKING = 1 意味回傳最近且有存放此Product Group的配送中心。


遵循著我們上述的思考脈絡,希望能對您思考這類問題有所助益,也謝謝您撥冗完食。這些內容是根據我過去在外商的經驗,非常歡迎業界先進留言與我交流,我也會不定期補充我的觀點,也歡迎Follow我的Threads,持續都會有這類問題討論和生產力提升的點子喔!

avatar-img
31會員
59內容數
我們秉持著從原人進化的精神,不斷追求智慧的累積和工具的運用來提升生產力。我們相信,每一個成員都擁有無限的潛力,透過學習和實踐,不斷成長和進步。
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
DigNo Ape 數遊原人 的其他內容
處理美國地區 ZIP Code 郵遞區號相關的資料問題應該是許多外企資料分析師每天都會處理的問題,ZIP Code 通常是在做區域相關分析的最小、訂單能記錄到最細的標準化單位,引此了解一些處理ZIP Code的眉角對於資料處理、分析、視覺化,以至於建構模型是非常重要
處理美國地區 ZIP Code 郵遞區號相關的資料問題應該是許多外企資料分析師每天都會處理的問題,ZIP Code 通常是在做區域相關分析的最小、訂單能記錄到最細的標準化單位,引此了解一些處理ZIP Code的眉角對於資料處理、分析、視覺化,以至於建構模型是非常重要
你可能也想看
Google News 追蹤
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
※ 為什麼我們需要 Transaction? 當我們談到 Transaction(交易)時,指的是一組不可分割的 SQL 操作。這些操作結果只能成功或失敗,以確保資料庫的一致性和完整性。Transaction 是資料庫操作中的一個「邏輯單位」,包含多個操作步驟。如果其中任何一個步驟失敗,整個 Tr
Thumbnail
※ 什麼是ORDER BY? 可以讓SELECT出來的結果,根據你想要的方式排序。簡單說,用於對查詢結果進行排序。 ※ 語法: SELECT select_list FROM table_name ORDER BY column1 [ASC|DESC], column2 [ASC|DESC]
Thumbnail
在工作情境中手動執行SQL語法更新中文字時,有時會遇到中文字顯示問號(?)的情況。這篇文章將介紹如何解決手動執行SQL語法時造成中文顯示問號(?)的方法。
Thumbnail
本文將介紹 SQL 中的連接(JOIN),連接(JOIN)是用於結合來自兩個或多個資料表的相關數據,建議讀過我之前發佈的幾篇"SQL學習筆記"之後再來看這篇。
※什麼是資料 : 說明: 「資料」(information/data) 是網路應用程式的核心。 使用者分享的照片、電商販賣的產品,或是搜索引擎提供的餐廳評價都是資料。 主要特徵: 生活中任何基本的事實 (fact) 或是值 (value) 都可以被稱為資料。例如:你的名字、你的生日。 因
Thumbnail
題目敘述 題目會給我們兩張資料表,第一張是Sales,第二張是Product。 第一張是Sales表格,裡面分別有sale_id、 product_id、year、quantity、price等欄位。其中(sale_id、 product_id)做為複合主鍵Primary key Table:
Thumbnail
*合作聲明與警語: 本文係由國泰世華銀行邀稿。 證券服務係由國泰世華銀行辦理共同行銷證券經紀開戶業務,定期定額(股)服務由國泰綜合證券提供。   剛出社會的時候,很常在各種 Podcast 或 YouTube 甚至是在朋友間聊天,都會聽到各種市場動態、理財話題,像是:聯準會降息或是近期哪些科
Thumbnail
※ 為什麼我們需要 Transaction? 當我們談到 Transaction(交易)時,指的是一組不可分割的 SQL 操作。這些操作結果只能成功或失敗,以確保資料庫的一致性和完整性。Transaction 是資料庫操作中的一個「邏輯單位」,包含多個操作步驟。如果其中任何一個步驟失敗,整個 Tr
Thumbnail
※ 什麼是ORDER BY? 可以讓SELECT出來的結果,根據你想要的方式排序。簡單說,用於對查詢結果進行排序。 ※ 語法: SELECT select_list FROM table_name ORDER BY column1 [ASC|DESC], column2 [ASC|DESC]
Thumbnail
在工作情境中手動執行SQL語法更新中文字時,有時會遇到中文字顯示問號(?)的情況。這篇文章將介紹如何解決手動執行SQL語法時造成中文顯示問號(?)的方法。
Thumbnail
本文將介紹 SQL 中的連接(JOIN),連接(JOIN)是用於結合來自兩個或多個資料表的相關數據,建議讀過我之前發佈的幾篇"SQL學習筆記"之後再來看這篇。
※什麼是資料 : 說明: 「資料」(information/data) 是網路應用程式的核心。 使用者分享的照片、電商販賣的產品,或是搜索引擎提供的餐廳評價都是資料。 主要特徵: 生活中任何基本的事實 (fact) 或是值 (value) 都可以被稱為資料。例如:你的名字、你的生日。 因
Thumbnail
題目敘述 題目會給我們兩張資料表,第一張是Sales,第二張是Product。 第一張是Sales表格,裡面分別有sale_id、 product_id、year、quantity、price等欄位。其中(sale_id、 product_id)做為複合主鍵Primary key Table: