Как запустить очень большой запрос (SQL Server и ColdFusion)

StackOverflow https://stackoverflow.com/questions/226899

  •  03-07-2019
  •  | 
  •  

Вопрос

У меня есть достаточно простой запрос (на этот раз), из которого мне нужны ВСЕ результаты (я сохраняю их в электронной таблице Excel).Время ожидания самого запроса на сервере истекает, так как же мне запустить его так, чтобы этого не произошло?

Это было полезно?

Решение

Самый простой способ - разбить домен запроса на несколько частей. Например, добавьте в предложение WHERE выражение, которое выбирает только первую половину диапазона клавиш, а затем выполните второй запрос, чтобы выбрать нижнюю половину. Затем объедините вывод.

Другие советы

Вы можете увеличить время ожидания запроса для страницы:

<cfsetting requestTimeout="3600" />

Это гарантирует, что у вас есть время для обработки всех записей.

Вы также можете разбить список на " chunks " ;. Потребуется некоторая настройка, чтобы определить оптимальный размер фрагмента, но вы можете получить результаты по 100 или 1000 строк за раз и использовать & Lt; cfflush & Gt; выдвинуть результаты на экран по мере их появления. Этот подход также имеет преимущество, заключающееся в использовании меньшего количества памяти на сервере coldFusion, поскольку каждая строка, извлеченная с сервера SQL, загружается в память CFML и остается там до тех пор, пока переменная объекта запроса не будет перезаписана или не выйдет из области видимости (в конце страница). Это означает, что вы можете легко заполнить память coldFusion чтением нескольких сотен тысяч строк, особенно если строки & Quot; wide & Quot; (т. е. содержащие большие varchars или тексты).

прежде всего, я бы проверил, почему этот запрос занимает так много времени.

что вы можете сделать на уровне базы данных, чтобы повысить производительность запроса?похоже, возможно, у вас неправильно проиндексирована база данных.возьмите запрос и закиньте его в какую-нибудь программу, с помощью которой вы сможете проанализировать план выполнения.найдите задержки и устраните их.

чтобы повысить производительность, рассмотрите возможность создания индексированных представлений, если ваша база данных поддерживает подобные вещи.

далее рассмотрим кэширование некоторых частей запроса.нет никакой причины выполнять вычисления по историческим данным для каждого запроса, когда это можно было бы выполнить один раз, а затем кэшировать где-нибудь в таблице.

что касается конца coldfusion.убедитесь, что вы используете java.io.BufferedWriter для создания электронной таблицы.использование обычного метода конкатенации строк в CF выполняется очень медленно, а BufferedWriter - бесконечно быстрее.Прилагается CFC, который я создал для создания электронных таблиц с разделителями в виде таблиц, вы можете изменить его в соответствии с вашими потребностями.

<!--- 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>

Как уже отмечали другие, вы можете попытаться увеличить время ожидания запроса на странице, хотя это не рекомендуется, если выполнение вашего запроса измеряется в минутах , а не в секундах или миллисекундах. CF будет обслуживать только определенное количество запросов одновременно, поэтому вы должны быть осторожны с блокировкой одного из этих запросов, ожидающих выполнения 5-минутного запроса.

Если вы используете SQL Server или Oracle, я думаю, что CFQUERY предоставляет свой собственный атрибут времени ожидания для запроса, который вы можете установить. Опять же, это не рекомендуется для действительно длительных запросов.

По моему опыту, если ваш запрос настолько сложен или возвращает столько данных, что для его выполнения требуется минут , то пришло время отделить выполнение запроса от инициирующего запроса. Это. Есть несколько способов сделать это, например:

<Ол>
  • Создайте систему очередей для записи ожидающих запросов на обслуживание. Это может быть таблица БД, файл XML на диске и т. Д. Когда ваш пользователь запрашивает свои данные, вы регистрируете этот запрос в этой очереди.

  • Напишите запланированное задание (например, Java, DTS или запланированную страницу CF), которое периодически проверяет эту очередь на работу. В зависимости от ваших потребностей вы можете выделять фоновый поток для обработки каждого запроса или, возможно, запланированная задача обрабатывает его напрямую. Если вы используете запланированные CF-страницы, вам нужно разбить общую рабочую нагрузку на более мелкие куски, которые можно обрабатывать итеративно, в противном случае у вас возникнет та же проблема, что и сейчас.

  • Как только запланированная задача определяет, что запрос был выполнен, она запускает какое-то уведомление о том, что обработка готова. Например, вы можете отправить электронное письмо пользователю, чтобы сообщить ему, что данные готовы, со ссылкой для загрузки файла .csv, созданного на диске.

  • Очевидно, правильный выбор во многом зависит от конкретной решаемой проблемы. В общем, я бы попробовал эти вещи в следующем порядке:

    <Ол>
  • Агрессивно атакуйте время выполнения запроса. Вы можете использовать индексы или лучше писать на T-SQL?
  • Если запрос занимает минуту или две и выполняется очень редко, увеличение времени ожидания страницы или запроса может быть приемлемым.
  • Если запрос выполняется часто или занимает более 2-3 минут, откусите пулю и создайте систему пакетной обработки или организации очередей для обработки запроса в фоновом режиме.
  • Вы можете установить время ожидания для каждого запроса, хотя объединение нескольких запросов может быть лучшим подходом.

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

    Используйте индексацию правильно. Создавайте внешние ключи везде, где только можно. Запросы никогда не будут превышать время ожидания для нормализованной базы данных.

    Будьте очень осторожны с соединениями и предложениями, например, если в вашем запросе есть предложение group by вместо использования предложения where, предложение having будет работать быстрее. Таким образом сокращается время выполнения запроса.

    Используйте оценку стоимости, чтобы проверить, какая таблица занимает больше времени или нуждается в нормализации в вашей базе данных.

    Я бы выбросил запрос в отдельный поток, загрузив его в постоянную область (например, сеанс). Перейдите на страницу, которая проверяет наличие запроса. Повторяйте проверку до тех пор, пока запрос не будет создан, затем перейдите на страницу, которая отображает / обрабатывает / изменяет его.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top