2017-10-05 1 views
0

I (think) 표준 SQL의 관점에서 웹 사이트 재고 가용성 추적과 관련된 재미있는 윈도 잉 시나리오가 있습니다.BigQuery - 주식 원가 계산을위한 창구

이 주어진 제품

추진 및 주식의 '손에'양에서 끌어 이벤트 우리는이 문제에 관한 3 개 이벤트 유형을 기반으로 시간이 지남에 주식 위치보기를 구축을 위해 노력한다 StocklevelUpdated (PUSH) : 매일 밤 자정에웨어 하우스의 특정 제품에 대해 onHandQty 가용성 수준에 대한 새 업데이트를 제공합니다. onHandQty가 각 제품의 다음 날 거래를위한 새로운 가치로 간주하면 이는 기본적으로 어려운 '재설정'입니다. (참고 : 실제로 변경 사항이없는 경우에도 매일 밤 기록을 보냅니다.)

(PULL)을 OrderAccepted : 그럼 하루의 과정 동안,이 시나리오에서 주식에 ​​대한 음의 값을 가지고 제품에 대한 많은 'OrderAccepted'이벤트가 'onHandQtyDelta'(지금은 덜 판매). -2 주문한 제품 2 대. 이 판매 가능한 주) 아래

가 tabluar 인에 다시 추가 할뿐만 아니라 제품의 수량 cancelleations가있을 수 있습니다,이는 'onHandQtyDelta'에 대한 양 (+)의 값을 갖는다 :

가 OrderCancelled 의 시간 순서대로 데이터를 약간 단순화 한 버전의보기 (참고 : 여기에는 하나의 제품이 있지만 물론 많은 것들이 표시됩니다.)

enter image description here

onHandQtyDelta -이 이벤트

onHandQty의 결과로 onHandQty에 대한 변경 -이 시간에 델타 영향을 그 시점에서 순 긍정적이다.

이제 상기 이미지는 실제로 모든 값 (29이 자정 재설정 중 하나 참고 있지만) 좋게 ledgering를 나타내지 만, 모든 이러한 데이터 가능하고 값 중 하나에 대해 유도 될 필요 각 주문 이벤트 유형 즉 1이 누락되었습니다.

onHandQty : 실제 데이터 집합에서 onHandQty가 정의 된 행만 'StocklevelUpdated'이벤트입니다. 본질적으로 이것은 자정 (예 : 29)에 제품에 대한이 값을 '재설정'합니다. 저널링은 기본적으로 이들 중 가장 가까운 곳까지 추적해야합니다. 그러나 onHandQtyDelta는 파생되어야합니다.

onHandQtyDelta OrderAccepts 및 OrderCancelled 이벤트에만 onHandQty를 계산하는 데 사용해야하는이 값이 있습니다. (행 수백만의 10 초를 거기에 주어진)

enter image description here

효율적으로이 작업을 수행하는 방법을 수행하는 데이터의 현실은 다음과 같은 작업을 할 수 있도록

그래서 그림은 천 단어를 구사 이?

내 생각은 그게 뭔지보고 다시 이전 기록 onHandQty 값으로 보는 윈도우와 '지연'기능을 사용하는 것입니다, 다음 새 onHandQty 값을 마련하기 위해 추가 또는 뺄셈을한다.

문제는 재귀 문제입니다. 이전 이벤트 자체가 이전처럼 돌아가는 등 .... 실제로 실제 값이있는 유일한 이벤트이므로 stocklevelUpdated 이벤트가 발생할 때까지 문제가 발생합니다. 다음에서 앞으로 일하십시오. 그러나 당신이 그 사건을 얻기 위해 얼마나 멀리 돌아갈지를 알지 못할 때 윈도우 잉을 사용하는 방법 - 그 사이에 어떤 수의 OrderAccepts와 Cancells가있을 수 있습니다 (또는 아무도 없습니다)

아마도 배열과 똑똑한 무엇인가 주어진 products 행을 배열에 넣고 일부 배열을 수행합니까?

나는 창문이 길을 생각하고 간단한 해결책으로 어둡게 느껴진다 고 생각한다. 모든 세부를 위해 유감스럽게 생각하지만 didnt는 내가 도움이 필요로하고 있었던 것에 관해 막연하다. 다음은

다음
WITH stock_changes AS (
SELECT 
    "StocklevelUpdated" AS eventName, 
    Timestamp("2017-06-29T23:59:59") AS stockLevelEventAt, 
    "PRODUCT_190035001612" AS productId, 
    null AS onHandQtyDelta, 
    23 AS onHandQty 
UNION ALL (
SELECT 
    "StocklevelUpdated" AS eventName, 
    Timestamp("2017-06-29T23:59:59") AS stockLevelEventAt, 
    "PRODUCT_4545423454545" AS productId, 
    null AS onHandQtyDelta, 
    120 AS onHandQty) 
UNION ALL (
    SELECT 
    "OrderAccepted" AS eventName, 
    Timestamp("2017-06-30T01:02:20") AS stockLevelEventAt, 
    "PRODUCT_190035001612" AS productId, 
    -2 AS onHandQtyDelta, 
    null AS onHandQty) 
UNION ALL (
    SELECT 
    "OrderAccepted" AS eventName, 
    Timestamp("2017-06-30T02:19:20") AS stockLevelEventAt, 
    "PRODUCT_190035001612" AS productId, 
    -3 AS onHandQtyDelta, 
    null AS onHandQty) 
UNION ALL (
    SELECT 
    "OrderAccepted" AS eventName, 
    Timestamp("2017-06-30T05:13:20") AS stockLevelEventAt, 
    "PRODUCT_4545423454545" AS productId, 
    -3 AS onHandQtyDelta, 
    null AS onHandQty) 
UNION ALL (
    SELECT 
    "OrderCancelled" AS eventName, 
    Timestamp("2017-06-30T13:02:20") AS stockLevelEventAt, 
    "PRODUCT_190035001612" AS productId, 
    +2 AS onHandQtyDelta, 
    null AS onHandQty) 
UNION ALL (
    SELECT 
    "OrderCancelled" AS eventName, 
    Timestamp("2017-06-30T11:02:20") AS stockLevelEventAt, 
    "PRODUCT_4545423454545" AS productId, 
    2 AS onHandQtyDelta, 
    null AS onHandQty) 
UNION ALL (
SELECT 
    "StocklevelUpdated" AS eventName, 
    Timestamp("2017-06-30T23:59:59") AS stockLevelEventAt, 
    "PRODUCT_190035001612" AS productId, 
    null AS onHandQtyDelta, 
    29 AS onHandQty) 
UNION ALL (
SELECT 
    "StocklevelUpdated" AS eventName, 
    Timestamp("2017-06-30T23:59:59") AS stockLevelEventAt, 
    "PRODUCT_4545423454545" AS productId, 
    null AS onHandQtyDelta, 
    140 AS onHandQty) 
) 
SELECT * 
FROM stock_changes 
order by productId, stockLevelEventAt ASC 

답변

1

가 BigQuery에 표준 SQL

#standardSQL 
WITH stock_changes AS (
    SELECT "StocklevelUpdated" AS eventName, TIMESTAMP("2017-06-29T23:59:59") AS stockLevelEventAt, 
    "PRODUCT_190035001612" AS productId, NULL AS onHandQtyDelta, 23 AS onHandQty UNION ALL 
    SELECT "StocklevelUpdated", TIMESTAMP("2017-06-29T23:59:59"),"PRODUCT_4545423454545",NULL, 120 UNION ALL 
    SELECT "OrderAccepted", TIMESTAMP("2017-06-30T01:02:20"),"PRODUCT_190035001612",-2, NULL UNION ALL 
    SELECT "OrderAccepted", TIMESTAMP("2017-06-30T02:19:20"),"PRODUCT_190035001612",-3, NULL UNION ALL 
    SELECT "OrderAccepted", TIMESTAMP("2017-06-30T05:13:20"),"PRODUCT_4545423454545",-3, NULL UNION ALL 
    SELECT "OrderCancelled", TIMESTAMP("2017-06-30T13:02:20"),"PRODUCT_190035001612",+2, NULL UNION ALL 
    SELECT "OrderCancelled", TIMESTAMP("2017-06-30T11:02:20"),"PRODUCT_4545423454545",2, NULL UNION ALL 
    SELECT "StocklevelUpdated", TIMESTAMP("2017-06-30T23:59:59"),"PRODUCT_190035001612",NULL, 29 UNION ALL 
    SELECT "StocklevelUpdated", TIMESTAMP("2017-06-30T23:59:59"),"PRODUCT_4545423454545",NULL, 140 
) 
SELECT 
    eventName, stockLevelEventAt, productId, 
    delta AS onHandQtyDelta, IFNULL(onHandQty, onHand) AS onHandQty 
FROM (
    SELECT *, 
    SUM(IFNULL(onHandQty,0) - delta) 
     OVER(PARTITION BY productId, format_timestamp('%Y-%m-%d', stockLevelEventAt) 
     ORDER BY stockLevelEventAt DESC 
     rows BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS onHand 
    FROM (
    SELECT eventName, stockLevelEventAt, productId, onHandQty, 
     CASE 
     WHEN prev IS NULL THEN IFNULL(onHandQtyDelta, 0) 
     ELSE onHandQty - prev - delta 
     END AS delta 
    FROM (
     SELECT *, 
     SUM(IFNULL(onHandQtyDelta,0)) OVER(PARTITION BY productId, format_timestamp('%Y-%m-%d', stockLevelEventAt) ORDER BY stockLevelEventAt) AS delta, 
     LAG(onHandQty) OVER(PARTITION BY productId, eventName ORDER BY stockLevelEventAt) AS prev 
     FROM stock_changes 
    ) 
) 
) 
ORDER BY productId, stockLevelEventAt ASC 
입니다 (난 그냥 이미지를 만드는 목적으로 제품 및 시간을 분류) 해제 작업 시작 테스트 데이터 집합을 제공합니다

결과는 다음과 같습니다.

Row eventName   stockLevelEventAt  productId onHandQtyDelta onHandQty  
1 StocklevelUpdated 2017-06-29 23:59:59 UTC PRODUCT_190035001612 0 23 
2 OrderAccepted  2017-06-30 01:02:20 UTC PRODUCT_190035001612 -2 21 
3 OrderAccepted  2017-06-30 02:19:20 UTC PRODUCT_190035001612 -3 18 
4 OrderCancelled  2017-06-30 13:02:20 UTC PRODUCT_190035001612 2 20 
5 StocklevelUpdated 2017-06-30 23:59:59 UTC PRODUCT_190035001612 9 29 
6 StocklevelUpdated 2017-06-29 23:59:59 UTC PRODUCT_4545423454545 0 120 
7 OrderAccepted  2017-06-30 05:13:20 UTC PRODUCT_4545423454545 -3 117 
8 OrderCancelled  2017-06-30 11:02:20 UTC PRODUCT_4545423454545 2 119 
9 StocklevelUpdated 2017-06-30 23:59:59 UTC PRODUCT_4545423454545 21 140 

대부분 더 최적화 될 수 있습니다 -하지만 논리를 구현하는 데 더 집중하고 최적화에 덜 집중합니다.

+0

미카 일의 와우 덕분에 와우 덕분에 - 앉아서 조금만 더 소중히해야합니다. 먼저 올바른 결과를 얻는 것이 가장 좋은 출발이며, 1,200 만 행 정도를 최적화 할 수 있습니다. –

+0

. 이 쿼리는 실제 데이터를 수백만 행으로 확장 할 수있는 좋은 기회입니다.이 값은 productID/days/etc에 따라 다릅니다. 분포. 당신은 단지 시도해보십시오 : O) 그리고 알기에 알맞은 –

+0

을 알게된다면 동의하고 투표하는 것을 잊지 마십시오! 전체 데이터 세트로 SQL을 살펴보십시오. 실제로 'productId + location'이 productId + location이 아닌 productId (즉, 동일한 제품 ID, 다른 위치)라는 '열'위치 (2 개의 값 'UK'또는 'US')가있는 경우 어떻게 변경 될까요? 나는 Ive가 파티션과 순서에 위치를 추가함으로써 그것을 얻었지만 다만 점검하고 싶다고 생각한다. 또한 onHandQty가 음수 (이전 0 일 때 orderCreated를 가졌지 만 음수가된다) .... 현재는 'null'로 표시됩니다. 굉장한 감사합니다! –