使用SQL将数量分配给不同的存储桶

时间:2015-08-12 18:34:11

标签: sql postgresql distribution buckets

我正在尝试将一些SQL代码放在一起,这些代码能够在具有有限容量的桶中分配特定数量的特定订单但在单独的事务中。例如,见下文。

我有2个桶,每个桶都有一定的容量(整数):

Tower

我在一些交易中有一定数量的数据:

Class<Tower> towerClass = (Class<Tower>) Class.forName(classString);
Tower tower = towerClass.newInstance();
tower.staticMethod();

我想在运行SQL代码后得到以下结果,该代码应保留事务编号并告诉我每个存储桶能容纳多少数量。必须按照桶号的顺序填充桶,并按交易号的顺序填写:

bucket       capacity
1            100
2            50

如果没有更多的存储桶,并且仍有数量需要进行分区,则应该转到“超龄”列,如上例所示。

2 个答案:

答案 0 :(得分:1)

DROP TABLE IF EXISTS buckets;
CREATE TABLE buckets (
    bucket_id bigserial primary key,
    capacity integer);

-- something to put in the buckets
DROP TABLE IF EXISTS transactions;
CREATE TABLE transactions (
    transaction_id bigserial 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);


WITH buckets AS (
    -- expand buckets (create a row per bucket capacity)
    SELECT row_number() OVER () bucket_row_id, *
    FROM (
        -- slot = a unit of capacity
        SELECT *, generate_series(1,b.capacity) slot 
        FROM buckets b
    ) useless_alias
), xact AS (
    -- expand transactions, creating an id per unit of quantity
    SELECT row_number() OVER () unit_row_id, *
    FROM (
        -- an item per transaction quantity
        SELECT *, generate_series(1,t.quantity) unit 
        FROM transactions t
    ) 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, CASE WHEN bucket_id IS NULL THEN 'overage' ELSE bucket_id::text END bucket_id , count(unit_row_id) quantity_bucketed
FROM filled
GROUP BY 1,2
ORDER BY 1,2

警告:我没有尝试从&#34; overage&#34;中添加一个额外的专栏。在填充存储桶时,它与overage 在示例情况下,只有1个事务具有超额。我假设在您的真实用例中,如果有更多交易,您真的希望看到每笔交易的数量没有被删除。

答案 1 :(得分:0)

-我已经编辑了@ kirk-roybal上面提供的答案,以适合SQL 2017语法。

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