AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 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

Você pode usar COUNT DISTINCT com uma cláusula OVER?

  • 772

Estou tentando melhorar o desempenho da seguinte consulta:

        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                            

Atualmente, com meus dados de teste, leva cerca de um minuto. Eu tenho uma quantidade limitada de entrada em alterações no procedimento armazenado geral em que essa consulta reside, mas provavelmente posso fazer com que eles modifiquem essa consulta. Ou adicione um índice. Tentei adicionar o seguinte índice:

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

E na verdade dobrou a quantidade de tempo que a consulta leva. Eu recebo o mesmo efeito com um índice NON-CLUSTERED.

Eu tentei reescrever da seguinte forma sem nenhum efeito.

        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                            

Em seguida, tentei usar uma função de janela como esta.

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

Neste ponto eu comecei a receber o erro

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

Então eu tenho duas perguntas. Primeiro você não pode fazer um COUNT DISTINCT com a cláusula OVER ou eu apenas escrevi incorretamente? E segundo, alguém pode sugerir uma melhoria que eu ainda não tenha tentado? Para sua informação, esta é uma instância do SQL Server 2008 R2 Enterprise.

EDIT: Aqui está um link para o plano de execução original. Também devo observar que meu grande problema é que essa consulta está sendo executada 30 a 50 vezes.

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

EDIT2: Aqui está o loop completo em que a instrução está conforme solicitado nos comentários. Estou verificando com a pessoa que trabalha com isso regularmente quanto ao propósito do loop.

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 respostas
  • 38422 Views

2 respostas

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

    Essa construção não tem suporte no momento no SQL Server. Poderia (e deveria, na minha opinião) ser implementado em uma versão futura.

    Aplicando uma das soluções alternativas listadas no item de feedback relatando essa deficiência, sua consulta pode ser reescrita como:

    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;
    

    O plano de execução resultante é:

    Plano

    Isso tem a vantagem de evitar um Eager Table Spool para proteção de Halloween (devido à auto-junção), mas introduz uma ordenação (para a janela) e uma construção Lazy Table Spool muitas vezes ineficiente para calcular e aplicar o SUM OVER (PARTITION BY)resultado a todas as linhas na janela. Como ele funciona na prática é um exercício que só você pode realizar.

    A abordagem geral é difícil de fazer funcionar bem. Aplicar atualizações (especialmente aquelas baseadas em uma auto-junção) recursivamente a uma estrutura grande pode ser boa para depuração, mas é uma receita para um desempenho ruim. Varreduras grandes repetidas, vazamentos de memória e problemas de Halloween são apenas alguns dos problemas. A indexação e (mais) tabelas temporárias podem ajudar, mas é necessária uma análise muito cuidadosa, especialmente se o índice for atualizado por outras instruções no processo (a manutenção de índices afeta as escolhas do plano de consulta e adiciona E/S).

    Em última análise, resolver o problema subjacente resultaria em um trabalho de consultoria interessante, mas é demais para este site. Espero que esta resposta aborde a questão superficial.


    Interpretação alternativa da consulta original (resulta na atualização de mais linhas):

    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;
    

    Plano 2

    Nota: eliminar a classificação (por exemplo, fornecendo um índice) pode reintroduzir a necessidade de um Eager Spool ou qualquer outra coisa para fornecer a proteção de Halloween necessária. Sort é um operador de bloqueio, portanto, fornece separação de fase completa.

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

    Necromante:

    É relativamente simples emular uma contagem distinta sobre a partição com 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
    

    Edit:
    Advertência: Obviamente, você precisa adicionar WHERE ADR IS NOT NULL se houver valores nulos.

    • 7

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

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

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve