AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 76628
Accepted
Kenneth Fisher
Kenneth Fisher
Asked: 2014-09-16 12:58:35 +0800 CST2014-09-16 12:58:35 +0800 CST 2014-09-16 12:58:35 +0800 CST

您可以将 COUNT DISTINCT 与 OVER 子句一起使用吗?

  • 772

我正在尝试提高以下查询的性能:

        UPDATE  [#TempTable]
        SET     Received = r.Number
        FROM    [#TempTable] 
        INNER JOIN (SELECT  AgentID,
                            RuleID,
                            COUNT(DISTINCT (GroupId)) Number
                    FROM    [#TempTable]
                    WHERE   Passed = 1
                    GROUP BY AgentID,
                            RuleID
                   ) r ON r.RuleID = [#TempTable].RuleID AND
                          r.AgentID = [#TempTable].AgentID                            

目前使用我的测试数据大约需要一分钟。我对该查询所在的所有存储过程的更改输入有限,但我可能可以让他们修改这个查询。或者添加索引。我尝试添加以下索引:

CREATE CLUSTERED INDEX ix_test ON #TempTable(AgentID, RuleId, GroupId, Passed)

它实际上使查询花费的时间增加了一倍。我使用非聚集索引获得了相同的效果。

我尝试如下重写它,但没有任何效果。

        WITH r AS (SELECT  AgentID,
                            RuleID,
                            COUNT(DISTINCT (GroupId)) Number
                    FROM    [#TempTable]
                    WHERE   Passed = 1
                    GROUP BY AgentID,
                            RuleID
            ) 
        UPDATE  [#TempTable]
        SET     Received = r.Number
        FROM    [#TempTable] 
        INNER JOIN r 
            ON r.RuleID = [#TempTable].RuleID AND
               r.AgentID = [#TempTable].AgentID                            

接下来我尝试使用这样的窗口函数。

        UPDATE  [#TempTable]
        SET     Received = COUNT(DISTINCT (CASE WHEN Passed=1 THEN GroupId ELSE NULL END)) 
                    OVER (PARTITION BY AgentId, RuleId)
        FROM    [#TempTable] 

此时我开始收到错误

Msg 102, Level 15, State 1, Line 2
Incorrect syntax near 'distinct'.

所以我有两个问题。首先你不能用 OVER 子句做一个 COUNT DISTINCT 还是我写错了?其次,任何人都可以提出我尚未尝试过的改进建议吗?仅供参考,这是一个 SQL Server 2008 R2 Enterprise 实例。

编辑:这是原始执行计划的链接。我还应该注意,我的大问题是这个查询运行了 30-50 次。

https://onedrive.live.com/redir?resid=4C359AF42063BD98%21772

EDIT2:这是评论中要求的语句所在的完整循环。我正在与定期与此一起工作的人核实循环的目的。

DECLARE @Counting INT              
SELECT  @Counting = 1              

--  BEGIN:  Cascading Rule check --           
WHILE @Counting <= 30              
    BEGIN      

        UPDATE  w1
        SET     Passed = 1
        FROM    [#TempTable] w1,
                [#TempTable] w3
        WHERE   w3.AgentID = w1.AgentID AND
                w3.RuleID = w1.CascadeRuleID AND
                w3.RulePassed = 1 AND
                w1.Passed = 0 AND
                w1.NotFlag = 0      

        UPDATE  w1
        SET     Passed = 1
        FROM    [#TempTable] w1,
                [#TempTable] w3
        WHERE   w3.AgentID = w1.AgentID AND
                w3.RuleID = w1.CascadeRuleID AND
                w3.RulePassed = 0 AND
                w1.Passed = 0 AND
                w1.NotFlag = 1        

        UPDATE  [#TempTable]
        SET     Received = r.Number
        FROM    [#TempTable] 
        INNER JOIN (SELECT  AgentID,
                            RuleID,
                            COUNT(DISTINCT (GroupID)) Number
                    FROM    [#TempTable]
                    WHERE   Passed = 1
                    GROUP BY AgentID,
                            RuleID
                   ) r ON r.RuleID = [#TempTable].RuleID AND
                          r.AgentID = [#TempTable].AgentID                            

        UPDATE  [#TempTable]
        SET     RulePassed = 1
        WHERE   TotalNeeded = Received              

        SELECT  @Counting = @Counting + 1              
    END
sql-server sql-server-2008-r2
  • 2 2 个回答
  • 38422 Views

2 个回答

  • Voted
  1. Best Answer
    Paul White
    2014-09-17T11:20:52+08:002014-09-17T11:20:52+08:00

    SQL Server 目前不支持此构造。它可以(并且在我看来应该)在未来的版本中实现。

    应用报告此缺陷的反馈项中列出的解决方法之一,您的查询可以重写为:

    WITH UpdateSet AS
    (
        SELECT 
            AgentID, 
            RuleID, 
            Received, 
            Calc = SUM(CASE WHEN rn = 1 THEN 1 ELSE 0 END) OVER (
                PARTITION BY AgentID, RuleID) 
        FROM 
        (
            SELECT  
                AgentID,
                RuleID,
                Received,
                rn = ROW_NUMBER() OVER (
                    PARTITION BY AgentID, RuleID, GroupID 
                    ORDER BY GroupID)
            FROM    #TempTable
            WHERE   Passed = 1
        ) AS X
    )
    UPDATE UpdateSet
    SET Received = Calc;
    

    生成的执行计划是:

    计划

    这具有避免万圣节保护的急切表假脱机(由于自连接)的优点,但它引入了排序(用于窗口)和通常低效的惰性表假脱机构造来计算并将SUM OVER (PARTITION BY)结果应用于所有行在窗口中。它在实践中的表现是一个只有你才能做的练习。

    整体方法很难使其表现良好。以递归方式将更新(尤其是基于自连接的更新)应用于大型结构可能有利于调试,但它会导致性能下降。重复的大扫描、内存溢出和万圣节问题只是其中的一些问题。索引和(更多)临时表可以提供帮助,但需要非常仔细的分析,特别是如果索引被进程中的其他语句更新(维护索引会影响查询计划选择并添加 I/O)。

    最终,解决潜在问题会带来有趣的咨询工作,但对于这个网站来说太多了。我希望这个答案可以解决表面问题。


    原始查询的替代解释(导致更新更多行):

    WITH UpdateSet AS
    (
        SELECT 
            AgentID, 
            RuleID, 
            Received, 
            Calc = SUM(CASE WHEN Passed = 1 AND rn = 1 THEN 1 ELSE 0 END) OVER (
                PARTITION BY AgentID, RuleID) 
        FROM 
        (
            SELECT  
                AgentID,
                RuleID,
                Received,
                Passed,
                rn = ROW_NUMBER() OVER (
                    PARTITION BY AgentID, RuleID, Passed, GroupID
                    ORDER BY GroupID)
            FROM    #TempTable
        ) AS X
    )
    UPDATE UpdateSet
    SET Received = Calc
    WHERE Calc > 0;
    

    计划 2

    注意:消除排序(例如通过提供索引)可能会重新引入对 Eager Spool 或其他东西的需求,以提供必要的万圣节保护。Sort 是一个阻塞算子,所以它提供了全相分离。

    • 32
  2. Quandary
    2015-12-19T01:36:27+08:002015-12-19T01:36:27+08:00

    死灵术:

    使用 DENSE_RANK 模拟分区上不同的计数相对简单:

    ;WITH baseTable AS
    (
                  SELECT 'RM1' AS RM, 'ADR1' AS ADR
        UNION ALL SELECT 'RM1' AS RM, 'ADR1' AS ADR
        UNION ALL SELECT 'RM2' AS RM, 'ADR1' AS ADR
        UNION ALL SELECT 'RM2' AS RM, 'ADR2' AS ADR
        UNION ALL SELECT 'RM2' AS RM, 'ADR2' AS ADR
        UNION ALL SELECT 'RM2' AS RM, 'ADR3' AS ADR
        UNION ALL SELECT 'RM3' AS RM, 'ADR1' AS ADR
        UNION ALL SELECT 'RM2' AS RM, 'ADR1' AS ADR
        UNION ALL SELECT 'RM3' AS RM, 'ADR1' AS ADR
        UNION ALL SELECT 'RM3' AS RM, 'ADR2' AS ADR
    )
    ,CTE AS
    (
        SELECT RM, ADR, DENSE_RANK() OVER(PARTITION BY RM ORDER BY ADR) AS dr 
        FROM baseTable 
        -- Caveat: If there are NULL-values, add "WHERE ADR IS NOT NULL" here
    )
    SELECT
         RM
        ,ADR
        
        ,COUNT(CTE.ADR) OVER (PARTITION BY CTE.RM ORDER BY ADR) AS cnt1 
        ,COUNT(CTE.ADR) OVER (PARTITION BY CTE.RM) AS cnt2 
        -- Geht nicht / Doesn't work 
        --,COUNT(DISTINCT CTE.ADR) OVER (PARTITION BY CTE.RM ORDER BY CTE.ADR) AS cntDist
        ,MAX(CTE.dr) OVER (PARTITION BY CTE.RM ORDER BY CTE.RM) AS cntDistEmu 
    FROM CTE
    

    编辑:
    警告:如果有空值,显然您需要添加 WHERE ADR IS NOT NULL。

    • 7

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve