Pregunta

Tengo una consulta razonablemente simple (esta vez) de la que necesito TODOS los resultados (los estoy almacenando en una hoja de cálculo de Excel). La consulta en sí agota el tiempo de espera del servidor, entonces, ¿cómo hago para ejecutarla sin que eso suceda?

¿Fue útil?

Solución

La forma más fácil sería dividir el dominio de la consulta en varias partes. Por ejemplo, agregue a la cláusula WHERE una expresión que seleccione solo la primera mitad del rango de teclas, luego ejecute una segunda consulta para seleccionar la mitad inferior. Luego combine la salida.

Otros consejos

Puede aumentar el tiempo de espera de la solicitud para la página:

<cfsetting requestTimeout="3600" />

Esto asegurará que tenga tiempo para procesar todas las entradas.

También es posible que desee dividir la lista en " trozos " ;. Se necesitará un poco de ajuste para descubrir cuál es el tamaño óptimo del fragmento, pero puede obtener los resultados 100 o 1000 líneas a la vez y usar & Lt; cfflush & Gt; para enviar los resultados a la pantalla a medida que estén disponibles. Este enfoque también tiene la ventaja de usar menos memoria en el servidor coldFusion, ya que cada fila retirada del servidor SQL se carga en la memoria CFML, y permanece allí hasta que la variable del objeto de consulta se sobrescribe o queda fuera de alcance (al final de la página). Esto significa que puede llenar fácilmente la memoria de ColdFusion leyendo varios cientos de miles de filas, especialmente si las filas son & "; Ancho &"; (es decir, que contiene grandes varchars o textos).

En primer lugar, comprobaría por qué esta consulta está tardando tanto.

qué puede hacer a nivel de base de datos para mejorar el rendimiento de la consulta. Parece que tal vez no tiene la base de datos indexada correctamente. tome la consulta y tírela a algún programa que pueda analizar el plan de ejecución. busque los retrasos y diríjase a ellos.

para obtener más rendimiento, busque crear vistas indexadas si su base de datos admite ese tipo de cosas.

siguiente vistazo al almacenamiento en caché de algunas partes de la consulta. no hay razón para hacer cálculos sobre datos históricos para cada solicitud cuando se puede hacer una vez y luego se almacena en caché en una tabla en algún lugar.

en cuanto al final de la fusión en frío. asegúrese de estar usando java.io.BufferedWriter para crear la hoja de cálculo. El uso de un método normal de concatenación de cadenas en CF es lento y BufferedWriter es infinitamente más rápido. Se adjunta un CFC que creé para crear hojas de cálculo delimitadas por tabulaciones, puede modificarlo para satisfacer sus necesidades.

<!--- 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 otros han señalado, podría intentar aumentar el tiempo de espera de la solicitud de la página, aunque esto no es aconsejable si la ejecución de su consulta se mide en minutos , en lugar de segundos o milisegundos. CF solo atenderá un número determinado de solicitudes a la vez, por lo que debe tener cuidado al bloquear una de esas solicitudes en espera de una consulta de 5 minutos para completar.

Si está utilizando SQL Server u Oracle, creo que CFQUERY expone su propio atributo de tiempo de espera por consulta que puede establecer. Nuevamente, esto no es aconsejable para consultas de larga duración.

En mi experiencia, si su consulta es tan compleja o devuelve tantos datos que tarda minutos en ejecutarse, entonces es hora de desacoplar la ejecución de la consulta de la solicitud que inicia eso. Hay varias formas de hacerlo, como:

  1. Cree algún tipo de sistema de cola para registrar solicitudes de servicio pendientes. Esto podría ser una tabla de base de datos, un archivo XML en el disco, etc. Cuando su usuario solicita sus datos, registra esa solicitud en esta cola.

  2. Escriba una tarea programada (por ejemplo, Java, DTS o una página CF programada) que verifique periódicamente esta cola para ver si funciona. Dependiendo de sus necesidades, puede separar un hilo de fondo para manejar cada solicitud, o tal vez la tarea programada lo maneja directamente. Si está utilizando páginas CF programadas, querrá dividir la carga de trabajo total en trozos más pequeños que se pueden manejar de forma iterativa, de lo contrario tendrá el mismo problema que tiene ahora.

  3. Una vez que la tarea programada determina que se ha completado una solicitud, inicia algún tipo de notificación de que el procesamiento está listo. Por ejemplo, puede enviar un correo electrónico al usuario para decirle que los datos están listos, con un enlace para descargar un archivo .csv que se creó en el disco.

Obviamente, la elección correcta depende mucho del problema específico que se resuelva. En general, probaría estas cosas, en este orden:

  1. Atacar agresivamente el tiempo de ejecución de la consulta. ¿Puedes usar índices o escribir mejor T-SQL?
  2. Si la consulta tarda uno o dos minutos y se ejecuta con poca frecuencia, puede ser aceptable aumentar el tiempo de espera de la página o la consulta.
  3. Si la consulta se ejecuta con frecuencia, o toma más de 2-3 minutos, muerda la viñeta y cree un sistema de procesamiento por lotes o en cola para manejar la consulta en segundo plano.

Puede establecer el tiempo de espera por solicitud, aunque combinar múltiples consultas puede ser un mejor enfoque.

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

Utilice la indexación correctamente. Crea claves foráneas siempre que puedas. Las consultas nunca agotarán el tiempo para una base de datos que está normalizada.

Tenga mucho cuidado con las uniones y cláusulas, como si tuviera una cláusula group by en su consulta allí en lugar de utilizar la cláusula where, la cláusula having funcionará más rápido. Reduciendo así el tiempo de ejecución de la consulta.

Use la estimación de costos para verificar qué tabla está tomando más tiempo o necesita normalización en su base de datos.

Tiraría la consulta en un hilo separado, cargándola en un alcance persistente (por ejemplo, sesión). Reenviar a una página que verifique la existencia de la consulta. Repita la comprobación hasta que exista la consulta, luego diríjase a una página que muestre / procese / lo que sea.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top