Мне было поручено обновить внутреннюю структуру, которую мы используем внутри компании. Фреймворк выполняет одну из функций: вы передаете ему запрос, и он возвращает количество строк, содержащихся в запросе (фреймворк интенсивно использует DataReaders, поэтому нам нужно общее количество перед обработкой пользовательского интерфейса).
Запрос, по которому необходимо выполнить подсчет, может отличаться от проекта к проекту (SOL-внедрение не является проблемой, запрос не из пользовательского ввода, просто жестко запрограммирован другим программистом, когда они используют инфраструктуру для их проект.) и мне сказали, что просто попросить программистов написать второй запрос на счетчик недопустимо.
В настоящее время решение состоит в том, чтобы сделать следующее (я не писал это, мне просто сказали это исправить).
//executes query and returns record count
public static int RecordCount(string SqlQuery, string ConnectionString, bool SuppressError = false)
{
//SplitLeft is just myString.Substring(0, myString.IndexOf(pattern)) with some error checking. and InStr is just a wrapper for IndexOf.
//remove order by clause (breaks count(*))
if (Str.InStr(0, SqlQuery.ToLower(), " order by ") > -1)
SqlQuery = Str.SplitLeft(SqlQuery.ToLower(), " order by ");
try
{
//execute query
using (SqlConnection cnSqlConnect = OpenConnection(ConnectionString, SuppressError))
using (SqlCommand SqlCmd = new SqlCommand("select count(*) from (" + SqlQuery + ") as a", cnSqlConnect))
{
SqlCmd.CommandTimeout = 120;
return (Int32)SqlCmd.ExecuteScalar();
}
}
catch (Exception ex)
{
if (SuppressError == false)
MessageBox.Show(ex.Message, "Sql.RecordCount()");
return -1;
}
}
Однако он разбивается на такие запросы, как (опять же, не мой запрос, мне просто нужно, чтобы он работал)
select [ClientID], [Date], [Balance]
from [Ledger]
where Seq = (select top 1 Seq
from [Ledger] as l
where l.[ClientID] = [Ledger].[ClientID]
order by [Date] desc, Seq desc)
and Balance <> 0)
, поскольку он удаляет все после order by
и прерывает запрос. Я думал, что могу перейти от простого сопоставления строк к более сложному парсеру, но прежде чем я это сделаю, я хотел спросить, есть ли лучший способ.
ОБНОВЛЕНИЕ: Предложение по порядку отброшено, потому что если вы включите его, используя мой метод или CTE, вы получите ошибку The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
Некоторые дополнительные сведения: Этот каркас используется для написания приложений преобразования. Мы пишем приложения для извлечения данных из старой базы данных клиентов и переноса их в формат нашей базы данных, когда клиент покупает наше программное обеспечение CRM . Часто мы работаем с исходными таблицами, которые плохо написаны и могут иметь размер несколько гигабайт. У нас нет ресурсов для хранения всей таблицы в памяти, поэтому мы используем DataReader для извлечения данных, чтобы все не было в памяти сразу. Однако требование - это индикатор выполнения с общим количеством записей, которые нужно обработать. Эта функция RecordCount используется для определения максимума индикатора выполнения. Он работает довольно хорошо, единственное препятствие - если программисту, пишущему преобразование, нужно упорядочить вывод данных, с условием order by
в большинстве внешних разрывов запросов count(*)
Частичное решение: Я придумал это, пытаясь понять, оно не будет работать 100% времени, но я думаю, что оно будет лучше, чем текущее решение
Если я нахожу заказ по предложению, то проверяю, является ли первое, что есть в запросе, это выбор (а не последующие топы), я заменяю этот начальный текст на select top 100 percent
. Это работает лучше, но я не публикую это как решение, так как надеюсь на универсальное решение.