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

如果没有更多的桶,并且还有要分桶的数量,它应该像上面的示例一样转到“overage”列。

4

2 回答 2

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

警告:我没有尝试从“超额”中增加一列。填充桶时,超龄适合哪个桶有点无关紧要。在示例案例中,只有 1 个事务超额。我假设在您的实际用例中,如果有更多交易,您真的希望看到每笔交易的数量没有被分桶。

于 2015-08-20T20:17:01.003 回答
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

于 2019-12-08T16:04:15.137 回答