Mysql

一種合併多個隊列的方法

  • June 23, 2020

考慮下表:

CREATE TABLE `multiqueue` (
   `ID` BIGINT(20) NOT NULL AUTO_INCREMENT,
   `CustomerID` BIGINT(20) NOT NULL,
   `Volume` INT(11) NOT NULL,
   `Content` MEDIUMTEXT NOT NULL COLLATE 'utf8mb4_unicode_ci',
   `PublishedTS` DATETIME NULL DEFAULT NULL,
   PRIMARY KEY (`ID`) USING BTREE
)
COLLATE='utf8mb4_unicode_ci'
ENGINE=InnoDB
;

該表用作多隊列,這意味著它聚合來自多個客戶(用 表示CustomerID)的請求隊列,每個請求都有一定Volume的工作。

如何編寫一個查詢,N從表中選擇最上面的行,交錯來自不同客戶的行?

如果客戶 1 發送 100 個請求,每個請求量為 1000,然後客戶 2 發送 20 個請求,每個請求量為 300,我希望查詢不會在我的程序忙於處理請求時強制任何客戶餓死響應另一個客戶的。第一次取客戶 1 的 1 個請求和客戶 2 的 3-4 個請求,處理它們,然後再接受客戶 1 的 1 個請求和客戶 2 的 3-4 個請求,以此類推。

到目前為止我已經嘗試過:

SET @runtot := 0;
SELECT q1.id1, q1.customerId1, q1.volume1, q1.content1, (@runtot := @runtot + q1.volume1) AS rt
FROM (
 SELECT ID AS id1, CustomerID AS customerId1, Content AS content1
 FROM multiqueue
 ORDER BY id1
) AS q1
WHERE @runtot < 2000

如此處所述,上面的程式碼通過所選行中某些欄位的執行總計來限制所選項目的數量。在上面的場景中,當上面的查詢正在使用時,客戶 2 會餓死。

使用的數據庫是 MariaDB(版本 10.4.13,但如果需要我可以升級到最新版本),不過 MySQL 的解決方案也應該可以工作。

對於最新版本,您可以使用視窗函式:

SELECT ID, CustomerID, Volume, Content, runtot
FROM ( 
   SELECT ID
        , CustomerID
        , Content
        , sum(Volume) over (
               partition by CustomerID 
               order by ID
          ) as runtot
   FROM multiqueue
) AS q1 
WHERE runtot < 2000;

編輯:

如果您希望每個客戶至少有 1 行,您可以添加另一個ROW_NUMBER()列舉結果的視窗函式,並在您的選擇中使用它:

SELECT ID, CustomerID, Volume, Content, runtot
FROM ( 
   SELECT ID
        , CustomerID
        , Content
        , sum(Volume) over (
               partition by CustomerID 
               order by ID
          ) as runtot
        , row_number() over (
               partition by CustomerID 
               order by ID
          ) as rn

   FROM multiqueue
) AS q1 
WHERE runtot < 2000
  OR rn = 1;

引用自:https://dba.stackexchange.com/questions/269582