Суммировать все записи после последнего ничего хорошего - PullRequest
1 голос
/ 26 марта 2012

Я работаю в производственной среде, и у нас есть база данных Access, настроенная на запись, не очень хорошая часть. Две таблицы, над которыми мы сейчас работаем, - это вхождения и сортировка. Происшествия записывают детали проблемы, а Сортировка - количество отсортированных деталей и хорошо / не хорошо. У меня есть запрос, который я пытаюсь оптимизировать, который суммирует количество отсортированных деталей с момента обнаружения последнего дефекта. Вот текущий (очень грязный) запрос:

SELECT Sort.[OccurrenceID], Sum(Sort.Sorted) AS SumOfSorted
FROM Sort
WHERE 
    (((Sort.SortDate)>(select top 1 dupe.sortdate from Sort as dupe where (((dupe.[OccurrenceID])=(Sort.[OccurrenceID])) and (((dupe.repaired)<>0) or ((dupe.scrapped)<>0))) order by dupe.sortdate desc, dupe.sortshift desc, dupe.id desc)))
OR (
     ((Sort.SortDate)>=(select top 1 dupe.sortdate from Sort as dupe where (((dupe.[OccurrenceID])=(Sort.[OccurrenceID])) and (((dupe.repaired)<>0) or ((dupe.scrapped)<>0))) order by dupe.sortdate desc, dupe.sortshift desc, dupe.id desc))
 AND ((Sort.SortShift)>(select top 1 dupe.sortshift from Sort as dupe where (((dupe.[OccurrenceID])=(Sort.[OccurrenceID])) and (((dupe.repaired)<>0) or ((dupe.scrapped)<>0))) order by dupe.sortdate desc, dupe.sortshift desc, dupe.id desc)))
OR (
     ((Sort.SortDate)=(select top 1 dupe.sortdate from Sort as dupe where (((dupe.[OccurrenceID])=(Sort.[OccurrenceID])) and (((dupe.repaired)<>0) or ((dupe.scrapped)<>0))) order by dupe.sortdate desc, dupe.sortshift desc, dupe.id desc))
 AND ((Sort.SortShift)=(select top 1 dupe.sortshift from Sort as dupe where (((dupe.[OccurrenceID])=(Sort.[OccurrenceID])) and (((dupe.repaired)<>0) or ((dupe.scrapped)<>0))) order by dupe.sortdate desc, dupe.sortshift desc, dupe.id desc))
 AND ((Sort.ID)>(select top 1 dupe.id from Sort as dupe where (((dupe.[OccurrenceID])=(Sort.[OccurrenceID])) and (((dupe.repaired)<>0) or ((dupe.scrapped)<>0))) order by dupe.sortdate desc, dupe.sortshift desc, dupe.id desc)))
GROUP BY Sort.[OccurrenceID];

Это работает, но работает вечно. Я пытался преобразовать подзапрос 'dupe' в его собственный сложенный запрос и в результате получил следующий запрос, который я назвал SortRejects:

SELECT Sort.[OccurrenceID], Sort.SortDate, Sort.SortShift, Sort.ID, Sort.Scrapped, Sort.Repaired
FROM Sort
WHERE (((Sort.Scrapped)<>0)) OR (((Sort.Repaired)<>0))
ORDER BY Sort.SortDate DESC , Sort.SortShift DESC , Sort.ID DESC;

И новый окончательный запрос:

SELECT Sort.[OccurrenceID], Sum(Sort.Sorted) AS SumOfSorted
FROM Sort
WHERE 
   (((Sort.sortdate)>(select top 1 SortRejects.sortdate from SortRejects where ((SortRejects.[OccurrenceID])=(Sort.[OccurrenceID])))))
OR (
     ((Sort.sortdate)>=(select top 1 SortRejects.sortdate from SortRejects where ((SortRejects.[OccurrenceID])=(Sort.[OccurrenceID]))))
 AND ((Sort.sortshift)>(select top 1 SortRejects.sortshift from SortRejects where ((SortRejects.[OccurrenceID])=(Sort.[OccurrenceID])))))
OR (
     ((Sort.sortdate)=(select top 1 SortRejects.sortdate from SortRejects where ((SortRejects.[OccurrenceID])=(Sort.[OccurrenceID]))))
 AND ((Sort.sortshift)=(select top 1 SortRejects.sortshift from SortRejects where ((SortRejects.[OccurrenceID])=(Sort.[OccurrenceID]))))
 AND ((Sort.ID)>(select top 1 SortRejects.id from SortRejects where ((SortRejects.[OccurrenceID])=(Sort.[OccurrenceID])))))
GROUP BY Sort.[OccurrenceID];

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

Ответы [ 2 ]

0 голосов
/ 29 марта 2012

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

Если я где вы,Я бы использовал VBA для уменьшения каждого SELECT TOP 1 до единственного проверяемого значения, а затем повторно вставил бы это в основной запрос.

Public Function GetMessyQuerySQL() As String

    ' First, extract our where-clause data SortDate, SortShift, and ID'
    Dim sql As String
    sql = sql & "SELECT TOP 1 sortdate, sortshift, id "
    sql = sql & "FROM   Sort "
    sql = sql & "WHERE  (OccurrenceID = OccurrenceID) "
    sql = sql & "        AND (Sort.repaired <> 0) "
    sql = sql & "              OR (Sort.scrapped<>0) "
    sql = sql & "ORDER  BY sortdate DESC, "
    sql = sql & "          sortshift DESC, "
    sql = sql & "          id DESC;"

    Dim SortDate as Variant, SortShift As Variant, ID as Variant

    ' We assume there is always a valid data return by that query! '
    Set rs = CurrentDb().OpenRecordset(sql, dbOpenForwardOnly)
    ' Convert dates to litteral dates in the format #29/03/2012# '
    SortDate = Format(rs(0), "\#mm\/dd\/yyyy\#") 
    SortShift = Format(rs(1), "\#mm\/dd\/yyyy\#") 
    SortID = rs(2)
    rs.Close
    Set rs = Nothing

    ' Now, modify the main query with our data from the first '
    sql = vbNullString
    sql = sql & "SELECT OccurrenceID, "
    sql = sql & "       SUM(Sorted) AS SumOfSorted "
    sql = sql & "FROM   Sort "
    sql = sql & "WHERE  (SortDate > %SortDate) "
    sql = sql & "        OR ((SortDate >= %SortDate) "
    sql = sql & "            AND (SortShift > %SortShift)) "
    sql = sql & "        OR ((SortDate = %SortDate) "
    sql = sql & "            AND (SortShift = %SortShift) "
    sql = sql & "            AND (ID > %SortID)) "
    sql = sql & "GROUP  BY OccurrenceID;  "

    ' Replace the %xxx% by their proper values '
    sql = Replace(sql, "%SortDate", SortDate)
    sql = Replace(sql, "%SortShift", SortShift)
    sql = Replace(sql, "%SortID", SortID)

    ' Just return the constructed SQL '
    GetMessyQuerySQL = SQL
End Sub

Таким образом, вызов этой функции получитваш SQL-код выглядит так:

SELECT OccurrenceID,
       SUM(Sorted) AS SumOfSorted
FROM   Sort
WHERE  (SortDate > #17/02/2012#)
        OR ((SortDate >= #17/02/2012#)
             AND (SortShift > #29/03/2012#))
        OR ((SortDate = #17/02/2012#)
             AND (SortShift = #29/03/2012#)
             AND (ID > 25))
GROUP  BY OccurrenceID; 

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

Private Sub Form_Load()
     Me.RecordSource = GetMessySQL()
End Sub
0 голосов
/ 26 марта 2012

Хм ... Я не слишком разбираюсь в синтаксисе и использовании Access, так что это может быть не совсем применимо.

Возможно, что сложенный запрос генерирует временную таблицу, которая не отсортирована. Таблицы в SQL (не очень уверенные в Access) имеют без присущего порядка . Следовательно, возможно, что когда он ссылается на составленные строки запроса, план доступа генерирует изменения на основе ссылочных столбцов, пытаясь оптимизировать выбранный путь.

В любом случае, я не совсем доволен ни одним из этих запросов, что-то просто отвлекает меня. Вероятно, это постоянное повторное использование SELECT TOP 1 - обычно я бы предпочел использовать пару CTE для достижения эффекта, но я не думаю, что они доступны в Access. Итак, вот некоторые изменения:

Прежде всего, вы можете захотеть изменить с помощью SortDate, SortShift и Sort.Id на простую временную метку (вы пытаетесь найти все с момента возникновения конкретной проблемы, верно?).

Кроме того, вы слишком много заботитесь о значениях «предыдущих» строк, когда вам нужно заботиться о значениях текущих (желательных) строк (я иногда попадаю в это , тоже). Вы не заботитесь о самой верхней строке - наши результаты должны быть больше, чем все строк с дефектами.

Итак, если Sort.id постоянно увеличивается, а значения НЕ используются повторно, вы просто сможете использовать это единственное значение, что-то вроде этого (может быть в синтаксисе Access ):

SELECT a.[OccurrenceId], SUM(a.sorted) as totalSorted
FROM Sort as a
LEFT JOIN Sort as b
ON b.[OccurrenceId] = a.[OccurrenceId]
AND (b.scrapped > 0  -- This is a count, right?
     OR b.repaired > 0)  -- see above
AND b.id > a.id
WHERE b.id IS NULL
GROUP BY a.[OccurrenceId]

, который в основном получает все строки, в которых нет «больших» строк (Sort.id) с scrapped или repaired не положительными для данного вхождения. Обратите внимание, что при написании вашего запроса, если в «самой большой» строке есть какие-то списки или исправления, она вернет эту строку, без указания того, что это «неудачный» запуск.

Если Sort.id используется повторно (это может быть за смену, учитывая его использование), это усложняется:

SELECT a.OccurrenceId, SUM(a.sorted) as totalSorted
FROM Sort as a
LEFT JOIN Sort as b
ON b.[OccurrenceId] = a.[OccurrenceId]
AND (b.scrapped > 0
     OR b.repaired > 0)
AND (b.sortDate > a.sortDate
     OR (b.sortDate = a.sortDate
         AND b.sortShift > a.sortShift)
     OR (b.sortDate = a.sortDate
         AND b.sortShift = a.sortShift
         AND b.id > a.id))
WHERE b.id IS NULL
GROUP BY a.[OccurrenceId]

Я не гарантирую, что любая из этих версий будет быстрее, хотя, вероятно, будет. Они, однако, гораздо менее «искажены».

...