Pergunta

Eu tenho uma consulta razoavelmente simples (desta vez) que eu preciso TODOS os resultados de volta a partir de (eu estou armazenando-os em uma planilha do Excel). A consulta em si vezes fora do servidor, assim como faço para ir sobre como executá-lo sem que isso aconteça?

Foi útil?

Solução

A maneira mais fácil seria para quebrar o domínio da consulta em várias partes. Por exemplo, adicionar a cláusula WHERE uma expressão que seleciona apenas a primeira metade do intervalo de chave, em seguida, executar uma segunda consulta para selecionar a metade inferior. Em seguida, mesclar a saída.

Outras dicas

Você pode aumentar o limite de tempo pedido para a página:

<cfsetting requestTimeout="3600" />

Isto irá certificar-se de que você tem tempo para processar todas as entradas.

Você também pode querer dividir a lista em "pedaços". Isso irá requerer algum ajuste para descobrir o que o tamanho ideal pedaço é, mas você poderia pegar os resultados 100 ou 1000 linhas de cada vez, e uso para empurrar os resultados para a tela assim que estiverem disponíveis. Esta abordagem também tem a vantagem de utilizar menos memória no servidor do ColdFusion, como cada linha puxado para trás a partir do SQL servidor é carregado na memória CFML, e fica lá até que a variável objeto de consulta é substituído ou sai do escopo (no final de a página). Isto significa que você pode facilmente encher a memória ColdFusion lendo centenas de milhares de linhas, especialmente se as linhas são "wide" (ou seja, contendo grandes varchars ou textos).

Em primeiro lugar eu iria verificar para ver por que esta consulta está demorando tanto.

O que você pode fazer no nível de banco de dados para melhorar o desempenho da consulta. soa como talvez você não tem o banco de dados indexados corretamente. tomar a consulta e jogá-lo em algum programa que você pode analisar o plano de execução. olhar para os lags e resolvê-los.

para obter mais desempenho, olhar para a criação de exibições indexadas se o seu banco de dados suporta esse tipo de coisa.

próxima olhada cache algumas partes do limite de consulta. não há nenhuma razão para estar fazendo cálculos em dados históricos para cada pedido quando poderia ser feito uma vez e depois armazenadas em cache em um algum lugar da tabela.

como para o final do ColdFusion. certifique-se que você está usando java.io.BufferedWriter para criar a planilha. usando um método normal de concatenação no CF é o cão lenta e BufferedWriter é infinitamente mais rápido. Anexado é um CFC que eu criei para criar tabed delimitado planilhas, você pode modificá-lo para atender às suas necessidades.

<!--- init --->
<cffunction name="init" access="public" returntype="Any" output="false">
    <cfargument name="name" type="string" required="true">
    <cfset var local = {}>

    <!--- name of file when downloading --->
    <cfset variables.name = arguments.name & ".xls">
    <!--- name of temp file --->
    <cfset variables.filename = CreateUUID() & ".csv">
    <!--- full path to temp file for downloading --->
    <cfset variables.fullfilename = expandpath("/_temp") & "\" & variables.filename>
    <!--- file write java object --->
    <cfset variables.filewriter = CreateObject("java","java.io.FileWriter").init(
            variables.fullfilename
            ,JavaCast("boolean","true")
        )>
    <!--- buffered writer java object --->
    <cfset variables.bufferedwriter = CreateObject("java","java.io.BufferedWriter").init(
                variables.filewriter
            )>
    <!--- row delimeter --->
    <cfset variables.row = chr(10)>
    <!--- col delimeter --->
    <cfset variables.col = chr(9)>
    <!--- header container --->
    <cfset variables.headers = []>
    <!--- data container --->
    <cfset variables.data = []>
    <cfset newrow()>
    <cfreturn this>
</cffunction>


<!--- addheader --->
<cffunction name="addheader" access="public" returntype="void" output="false">
    <cfargument name="str" type="string" required="true">
    <cfset arrayappend(variables.headers, arguments.str)>
</cffunction>

<!--- newrow --->
<cffunction name="newrow" access="public" returntype="void" output="false">
    <cfset arrayappend(variables.data, arraynew(1))>
    <cfset variables.data_counter = arraylen(variables.data)>
</cffunction>

<!--- adddata --->
<cffunction name="adddata" access="public" returntype="void" output="false">
    <cfargument name="str" type="string" required="true">
    <cfset arrayappend(variables.data[variables.data_counter], arguments.str)>
</cffunction>

<!--- flush --->
<cffunction name="flush" access="public" returntype="void" output="false">
    <cfset var local = {}>

    <!--- write headers --->
    <cfset local.counter = 0>
    <cfset local.headers_count = arraylen(variables.headers)>
    <cfloop array="#variables.headers#" index="local.header">
        <cfset local.counter++>
        <cfset variables.bufferedwriter.write(local.header & variables.col)>
    </cfloop>

    <cfif not arrayisempty(variables.headers)>
        <cfset variables.bufferedwriter.write(variables.row)>
    </cfif>

    <!--- write data --->
    <cfloop array="#variables.data#" index="local.data">
        <cfloop array="#local.data#" index="local.cell">
            <cfset variables.bufferedwriter.write(local.cell & variables.col)>
        </cfloop>
        <cfset variables.bufferedwriter.write(variables.row)>
    </cfloop>

    <cfset variables.bufferedwriter.close()>
    <cfsetting showdebugoutput="No">
    <cfheader name="Content-Description" value="File Transfer">
    <cfheader name="Content-Disposition" value="attachment;filename=#variables.name#">
    <cfcontent type="application/vnd.ms-excel" file="#variables.fullfilename#" deletefile="true" reset="true">
</cffunction>

Como os outros têm para fora pontas, você pode tentar aumentar o tempo limite do pedido da página, embora esta não é aconselhável se a execução de sua consulta é medido em minutos , em vez de segundos ou milissegundos. CF só vai servir um determinado número de pedidos de cada vez, para que você quer ser cuidadoso sobre o bloqueio até um desses pedidos em espera em uma consulta de 5 minutos para ser concluído.

Se você estiver usando o SQL Server ou Oracle, eu acho CFQUERY expõe seu próprio atributo timeout-consulta por que você pode definir. Novamente, isto não é aconselhável para consultas muito longo em execução.

Na minha experiência, se sua consulta ou é tão complexa ou dados retornos tanto, que leva minutos para correr, então é hora para desacoplar a execução da consulta a partir da solicitação que inicia isto. Há uma série de maneiras que você poderia fazer isso, como:

  1. Criar algum tipo de sistema de filas para gravar pendentes pedidos de serviço. Esta poderia ser uma tabela DB, um arquivo XML no disco, etc. Quando o usuário solicita seus dados se registar esse pedido com essa fila.

  2. Escrever uma tarefa agendada (por exemplo, Java, DTS, ou uma página CF programada) que verifica periodicamente esta fila para o trabalho. Dependendo de suas necessidades você pode girar fora uma discussão de fundo para lidar com cada pedido, ou talvez as alças tarefa agendada-lo diretamente. Se você estiver usando páginas CF programadas, você vai querer quebrar a carga horária total em pedaços menores que podem ser tratadas de forma iterativa, caso contrário você terá o mesmo problema que você tem agora.

  3. Uma vez que a tarefa agendada determina que o pedido tenha sido preenchido, ele arranca algum tipo de notificação que o processamento está pronto. Por exemplo, você pode enviar e-mail do usuário para dizer-lhes os dados estão prontos, com um link para baixar um arquivo .csv que foi criado no disco.

Obviamente, a escolha certa depende muito do problema específico a ser resolvido. Em geral, eu ia tentar essas coisas, nesta ordem:

  1. agressivamente atacar o tempo de execução da consulta. você pode usar índices ou escrever melhor T-SQL?
  2. Se a consulta leva um minuto ou dois, e é gerido muito raramente, aumentando página ou consulta o tempo limite pode ser aceitável.
  3. Se a consulta for executada, muitas vezes, ou leva mais do que 2-3 minutos, morder a bala e construir um sistema de dosagem ou na fila para lidar com a consulta em segundo plano.

Você pode definir o tempo limite em uma base por solicitação embora fusão de várias consultas podem ser uma abordagem melhor.

<cfsetting 
enableCFoutputOnly = "yes|no" 
requestTimeOut = "value in seconds"
showDebugOutput = "yes|no" >

Use indexando corretamente. Criar chaves estrangeiras onde quer que você pode. As consultas nunca será tempo para um db que está normalizada.

Tenha muito cuidado com associações e cláusulas, como se você tem cláusula group by na sua consulta lá em vez de usar cláusula where, cláusula having vai trabalhar mais rápido. Assim, reduzindo o tempo de execução da consulta.

Use a estimativa de custos para verificar qual a tabela está tomando a maior parte do tempo ou precisa de normalização na sua db.

eu iria jogar a consulta em um segmento separado, carregá-lo em um escopo persistente (por exemplo sessão). Encaminhar para uma página que verifica a existência da consulta. Repita o cheque até existe a consulta, em seguida, encaminhar para uma página que exibe / processos / whatevers-lo.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top