运行总计直到特定条件为真 [英] Running Total until specific condition is true

查看:70
本文介绍了运行总计直到特定条件为真的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一张表,代表庄家的牌和他们的等级.我现在正在尝试进行查询(尽可能快)以设置游戏状态.

I'm having a table representing the dealers cards and their rank. I'm now trying to make a query (as fast as possible) to set status on the game.

(As said before, only the dealer cards is shown)
W = Win
S = Stand
L = Loss
B = Blackjack (in two cards)

关于规则:庄家在 21 点获胜,如果是两张牌,则为 21 点.如果排名在 17 到 20 之间,则为 S = Stand.超过 21 是一种损失.

About the rules: The dealer wins at 21, if it's in two cards its blackjack. If the rank is between 17 and 20 it's S = stand. Over 21 is a loss.

排名:

1 (ACE) - 1 或 11 级.计为 11.

1 (ACE) - 1 or 11 rank. Counted as 11.

2-10 - 2-10 排名

2-10 - 2-10 rank

11-13(骑士 - 国王) - 10 级

11-13 (knight - king) - 10 rank

╔════╦══════╦════════╗
║ Id ║ Rank ║ Status ║
╠════╬══════╬════════╣
║  1 ║    1 ║        ║
║  2 ║    5 ║        ║
║  3 ║    8 ║ L      ║  //24 = Loss
║  4 ║    3 ║        ║
║  5 ║    1 ║        ║
║  6 ║    7 ║ W      ║  //21 = Win
║  7 ║   10 ║        ║
║  8 ║    1 ║ B      ║  //21 = Blackjack
║  9 ║   10 ║        ║
╚════╩══════╩════════╝

我尝试使用计数器来检查它是否是二十一点,然后我使用的是RunningPoint".检查卡的总和.

I've tried to use a counter to check if it's blackjack and then I'm using a "RunningPoint" to check the sum of the cards.

我现在有一个糟糕的解决方案,当它有大量数据时,它显示出非常糟糕的性能.你会怎么做,我可以做些什么来优化我的查询?当使用更多数据时,我还需要使用选项 (maxrecursion 0)

I have now a solution bad it shows very bad performance when it's a lot of data. How would you do this and what can I do to optimize my query? When using more data I also need to use option (maxrecursion 0)

(当有 100 万行时,它甚至无法运行...)

(When having 1 million rows it's not even possible to run this...)

我的例子:http://sqlfiddle.com/#!6/3855e/1

推荐答案

没有使用普通 SQL(包括窗口聚合函数)的有效解决方案,至少目前还没有人找到 :-)

There's no efficient solution using plain SQL (including Windowed Aggregate Functons), at least nobody found one, yet :-)

您的递归查询表现不佳,因为它太复杂了,这是一个简化版本:

Your recursive query performs bad because it's way too complicated, this is a simplified version:

编辑:修正了计算(Fiddle)

WITH ctePoints AS
 (
   SELECT 1 AS id
        ,rank
        ,CASE 
           WHEN rank >= 10 THEN 10
           WHEN rank = 1 THEN 11
           ELSE rank
         END AS Point
        ,1 AS Counter
   FROM dbo.BlackJack
   WHERE Id = 1

   UNION ALL

   SELECT t2.Id
        ,t2.rank
        ,CASE WHEN t1.Point < 17 THEN t1.Point ELSE 0 END 
         + CASE 
             WHEN t2.rank >= 10 THEN 10
             WHEN t2.rank = 1 THEN 11
             ELSE t2.rank
           END AS Point
        ,CASE WHEN t1.Point < 17 THEN t1.Counter + 1 ELSE 1 END AS Counter
   FROM dbo.BlackJack AS t2
   INNER JOIN ctePoints AS t1 ON t2.Id = t1.Id + 1
 ) 
SELECT ctepoints.*
     ,CASE 
        WHEN Point < 17 THEN ''
        WHEN Point < 20 THEN 'S'
        WHEN Point > 21 THEN 'L'
        WHEN Point = 21 AND Counter = 2 THEN 'B'
        ELSE 'W' 
      END AS DealerStatus            
FROM ctePoints

可能还是太慢了,因为它是逐行处理的.

It's probably still too slow, because it processes row by row.

我通常使用递归 SQL 来替换游标逻辑(因为在我的 DBMS 中它通常要快得多)但游标更新实际上可能更快(演示):

I usually use recursive SQL to replace cursor logic (because in my DBMS it's usually much faster) but a cursor update might actually be faster (Demo):

CREATE TABLE #BlackJack
(
   id INT PRIMARY KEY CLUSTERED
  ,Rank INT
  ,DealerStatus CHAR(1)
);

insert into #BlackJack (Id, Rank)
values 
(1, 1),(2, 5), (3, 8), (4, 3), (5, 1), (6, 7), (7, 10), (8, 1),(9, 10), (10, 10), (11,1);


DECLARE @Counter INT = 0
        ,@Point INT = 0
        ,@id int
        ,@Rank int
        ,@DealerStatus char(1)

DECLARE c CURSOR
FOR
SELECT id, Rank
FROM #BlackJack 
ORDER BY id FOR UPDATE OF DealerStatus

OPEN c

FETCH NEXT FROM c INTO @id, @Rank

WHILE @@FETCH_STATUS = 0
  BEGIN
    SET @counter = @counter + 1

    SET @Rank = CASE
                  WHEN @Rank >= 10 THEN 10
                  WHEN @Rank  = 1  THEN  11
                  ELSE @Rank
                END 

    SET @Point = @Point + @Rank

    SET @DealerStatus = CASE 
                          WHEN @Point < 17 THEN ''
                          WHEN @Point < 20 THEN 'S'
                          WHEN @Point > 21 THEN 'L'
                          WHEN @Point = 21 AND @Counter = 2 THEN 'B'
                          ELSE 'W' 
                        END 

    IF @Point >= 17 
    BEGIN
      UPDATE  #BlackJack 
      SET DealerStatus = @DealerStatus
      WHERE CURRENT OF c;

      SET @Point = 0

      SET @Counter = 0
    END

    FETCH NEXT FROM c INTO @id, @Rank
  END

CLOSE c
DEALLOCATE c

SELECT * FROM #BlackJack ORDER BY id

@lad2025 的古怪更新"仍然是获得预期结果的最快方法,但它使用的是未记录的功能,如果 Service Pack 破坏了它,则无法抱怨它:-)

Still @lad2025's "quirky update" is the fastest way to get the expected result, but it's using an undocumented feature and if a Service Pack breaks it there's no way to complain about it :-)

这篇关于运行总计直到特定条件为真的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆