Coldfusion cfl oop Sql запросы чрезвычайно медленные - PullRequest
3 голосов
/ 09 января 2020

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

В Coldfusion, есть cfl oop, который проходит через каждый тест и на лету вычисляет средний балл, максимальный балл, низкий балл для каждого теста и отображает его. Это требует постоянной загрузки, есть ли способ оптимизировать cfl oop?

Исходный запрос выглядит так:

SELECT     Quizname,
    NULLIF(QuizId, '') as  QuizId,
    NULLIF(InstructorId, '') as InstructorId,
    NULLIF(Location, '') as Location,
    cast(replace(quiz_user_quiz_percentage,'%','') as decimal(5,2)) as percentage
FROM         QuizResults
where 0=0
    and year(cast(datecompleted as date))>= 2019

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

<cfloop query="getEachQuiz" >
    <cfquery name="getStats" dbtype="query">
        SELECT 
            count(percentage) as countScore,
            max(percentage) as maxScore,
            min(percentage) as minScore,
            avg(percentage) as avgScore
        FROM data
        where Quizname= <cfqueryparam value="#getEachQuiz.Quizname#" cfsqltype="cf_sql_varchar" >
        and  QuizId= <cfqueryparam value="#getEachQuiz.QuizId#" cfsqltype="cf_sql_varchar" >
        <cfif len(getEachQuiz.InstructorId) gt 0>
            and InstructorId= <cfqueryparam value="#getEachQuiz.InstructorId#" cfsqltype="cf_sql_varchar" >
        </cfif>
        <cfif len(getEachQuiz.Location) gt 0>
            and Location=  <cfqueryparam value="#getEachQuiz.Location#" cfsqltype="cf_sql_varchar" >
        </cfif>
    </cfquery>
    <tr>
        <td>#getEachQuiz.Quizname#</td> 
        <td>#getEachQuiz.QuizId#</td>
        <td>#getStats.countScore#</td>
        <td>#numberformat(getStats.avgScore,'99.99')#%</td>
        <td>#getStats.maxScore#%</td>
        <td>#getStats.minScore#%</td>
    </tr>
</cfloop>

Ответы [ 2 ]

3 голосов
/ 10 января 2020

Вы выполняете несколько запросов CF внутри al oop. Вы должны иметь возможность заменить это на один.

<cfquery name="getStats" dbtype="query">
select quizname, quizid,instructorId, location
, count(percentage) as countScore
, min(percentage) as minScore
, max(percentage) as maxScore
, avg(percentage) as avgScore
from data
group by quizname, quizid,instructorId, location
</cfquery>

Кроме того, в вашем основном запросе замените

where 0=0
    and year(cast(datecompleted as date))>= 2019

на

where datecompleted >= '2019-01-01'
0 голосов
/ 17 января 2020

Для моего быстрого обзора кода:

  • Проверьте схему базы данных и убедитесь, что ваши типы столбцов соответствуют тем, которые им нужны.
  • Ваши столбцы позволяют NULL ? Если это так, вместо вставки и пустой строки используйте NULL. Тогда вам не нужны функции NULLIF.
  • cast(replace(quiz_user_quiz_percentage,'%','') as decimal(5,2)) as percentage> Похоже, что ваша база данных позволяет вам поместить символ "%" в этот столбец. Это специальный символ в SQL, который может осложнить вашу жизнь. Если у вас есть возможность изменить его на простое целое число, я бы это сделал. Кроме того, сделайте любое форматирование в конечном выводе.
  • where 0=0 >> Это условие не требуется, если вы не создаете какой-то динамический запрос c и хотите убедиться, что в предложении WHERE есть хотя бы одна оценка.
  • and year(cast(datecompleted as date))>= 2019 >> Если можете, избегайте функций в левой части сравнения WHERE. Это сведет на нет использование любого индекса в этом столбце и замедлит ваш запрос.
  • Использование Query of Query для создания результирующего набора может использовать много ресурсов. Я обнаружил, что большинство вещей, которые можно сделать в QoQ, гораздо лучше подходят для самого SQL.

Я предполагаю, что вы как-то проходите в тот год, который хотите отфильтровать. Таким образом, передав «2019», вы можете сделать что-то вроде ...

<cfset startDate = createDate(inputYear,1,1)> <!--- 2019-01-01 --->
<cfset endDate = dateAdd("yyyy",1,startDate)> <!--- 2020-01-01 --->

... и затем использовать эти переменные в своем запросе для фильтрации по дате.

ПРИМЕЧАНИЕ. Чтобы облегчить себе жизнь, я бы исправил столбец percentage в базе данных, чтобы исключить символ "%". См. Мои заметки ниже.

<cfquery name="getEachQuiz" ...>
SELECT qr.QuizName, qr.QuizID, qr.InstructorID, qr.Location
    , count(*) as countScore
    , max(qr.percentage) as maxScore
    , min(qr.percentage) as minScore
    , avg(qr.percentage) as avgScore
FROM QuizResults qr
WHERE qr.datecompleted >= <cfqueryparam value="2019-01-01" cfsqltype="cf_sql_date">
    AND qr.datecompleted < <cfqueryparam value="2020-01-01" cfsqltype="cf_sql_date">
GROUP BY QuizName, QuizID, qr.InstructorID, qr.Location
ORDER BY QuizName, QuizID, qr.InstructorID, qr.Location
</cfquery>

Тогда вы можете просто вывести свои результаты.

<cfoutput query="getEachQuiz">
    <tr>
        <td>#Quizname#</td> 
        <td>#QuizId#</td>
        <td>#countScore#</td>
        <td>#numberformat(avgScore,'99.99')#%</td>
        <td>#maxScore#%</td>
        <td>#minScore#%</td>
    </tr>
</cfoutput>

Однако я бы также рекомендовал включить столбец для обоих InstructorID и Location. Вы группируете и рассчитываете по тем, кто указан в вашем запросе, и если они не идентифицированы в ваших результатах, вы можете потерять контекст.

Также обратите внимание, что если вы конвертируете эти значения в NULL в исходном запросе, ваши результаты могут не дать того, что вы ожидаете. Как вы намеревались подсчитать инструктора, который имеет оба значения 'TN' и ''?

Для довольно быстрой альтернативы фильтрации дат используйте таблицу календаря (https://github.com/shawnoden/SQL_Stuff/blob/master/sql_CreateDateDimension.sql). Вы захотите изменить таблицу календаря, чтобы она соответствовала вашим данным и вашим потребностям, но они являются отличным инструментом.

<cfquery name="getEachQuiz" ...>
SELECT qr.QuizName, qr.QuizID, qr.InstructorID, qr.Location
    , count(*) as countScore
    , max(qr.percentage) as maxScore
    , min(qr.percentage) as minScore
    , avg(qr.percentage) as avgScore
FROM QuizResults qr
INNER JOIN CalendarTable ct ON qr.datecompleted = ct.theDate
    AND ct.theYear = <cfqueryparam value="#inputYear#" cfsqltype="cf_sql_integer">
GROUP BY QuizName, QuizID, qr.InstructorID, qr.Location
ORDER BY QuizName, QuizID, qr.InstructorID, qr.Location
</cfquery>

Основанный на множестве JOIN обычно будет намного быстрее, чем фильтрация с свидание. Даты могут быть огромной головной болью.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...