1

特定の数量を特定の順序で別のトランザクションで、この数量の容量が制限されているバケット間で分配できる SQL コードをまとめようとしています。たとえば、以下を参照してください。

それぞれ特定の容量 (整数) を持つ 2 つのバケットがあります。

bucket       capacity
1            100
2            50

いくつかのトランザクションでバケット化する特定の数量があります。

transaction   quantity
1             50
2             60
3             20
4             40

SQL コードを実行した後、次の結果が必要です。これにより、トランザクション番号が保持され、各バケットがその数量をどれだけ保持できたかがわかります。バケットは、バケット番号の順序、およびトランザクション番号の順序で満たされる必要があります。

transaction   quantity_bucketed   bucket_id   overage
1             50                  1           0
2             50                  1           0
2             10                  2           0
3             20                  2           0
4             20                  2           20

これ以上バケットがなく、バケット化する数量がまだある場合は、上記の例のように「超過」列に移動する必要があります。

4

2 に答える 2

0

-- SQL 2017 構文に収まるように、@kirk-roybal が提供した上記の回答を編集しました。

DROP TABLE IF EXISTS dbo.Numbers;

DECLARE @UpperBound INT = 1000000;

;WITH cteN(Number) AS
(
  SELECT ROW_NUMBER() OVER (ORDER BY s1.[object_id]) - 1
  FROM sys.all_columns AS s1
  CROSS JOIN sys.all_columns AS s2
)
SELECT [Number] INTO dbo.Numbers
FROM cteN WHERE [Number] <= @UpperBound;

CREATE CLUSTERED INDEX IX_dboNumber ON dbo.Numbers([Number])


DROP TABLE IF EXISTS #buckets;
CREATE TABLE #buckets (
    bucket_id int identity(1,1) primary key,
    capacity integer);

-- something to put in the buckets
DROP TABLE IF EXISTS #transactions;
CREATE TABLE #transactions (
    transaction_id int identity(1,1) primary key,
    quantity integer);

-- create 2 buckets with different capacities
INSERT INTO #buckets (capacity)
VALUES (100),(50);

-- create some traffic to put in the buckets
INSERT INTO #transactions (quantity)
VALUES (50),(60),(20),(40);

select * from #buckets
select * from #transactions;


WITH buckets AS (
    -- expand buckets (create a row per bucket capacity)
    SELECT bucket_row_id = row_number() OVER (Order By bucket_id) , *
    FROM (
        -- slot = a unit of capacity
        SELECT b.*, N.Number slot 
        FROM #buckets b
        CROSS JOIN dbo.Numbers N
        WHERE N.Number >0 AND N.Number <= b.capacity
    ) useless_alias
), xact AS (
    -- expand transactions, creating an id per unit of quantity
    SELECT unit_row_id = row_number() OVER (Order by transaction_id) , *
    FROM (
        -- an item per transaction quantity
        SELECT t.*, N.Number unit 
        FROM #transactions t
        CROSS JOIN dbo.Numbers N 
        WHERE N.Number > 0 AND N.Number <= t.quantity
    ) useless_alias
), filled AS (
    -- join buckets to transactions on slots=units
    --   slots with no units = wasted bucket capacity
    --   units with no slots = overage
    SELECT b.*, x.*
    FROM xact x
    FULL JOIN buckets b ON b.bucket_row_id = x.unit_row_id
)
-- finally, do the do
SELECT transaction_id
    , bucket_id = CASE WHEN bucket_id IS NULL THEN 'overage' ELSE CAST(bucket_id as varchar(200)) END  
    , count(unit_row_id) quantity_bucketed
FROM filled
GROUP BY transaction_id,  CASE WHEN bucket_id IS NULL THEN 'overage' ELSE CAST(bucket_id as varchar(200)) END 
ORDER BY 1,2

于 2019-12-08T16:04:15.137 に答える