Это проблема, которую я часами изучал в прошлом. Мне кажется, что это то, что должно было бы быть решено современными RDBMS решениями, но пока я не нашел ничего, что действительно отвечало бы тому, что я считаю невероятно обычной потребностью в любом веб-приложении или приложении Windows с база данных.
Я говорю о динамической сортировке. В моем фэнтезийном мире это должно быть так просто, как что-то вроде:
ORDER BY @sortCol1, @sortCol2
Это канонический пример, данный новичками в SQL и разработчиками хранимых процедур по всем форумам в Интернете. "Почему это невозможно?" они спрашивают. Неизменно, в конце концов, кто-то приходит с лекциями о скомпилированной природе хранимых процедур, планах выполнения в целом и всевозможных других причинах, по которым невозможно поместить параметр непосредственно в предложение ORDER BY
.
Я знаю, что некоторые из вас уже думают: «Тогда пусть клиент выполняет сортировку». Естественно, это снимает нагрузку с вашей базы данных. В нашем случае, однако, наши серверы баз данных даже не выходят из строя 99% времени, и они даже не являются многоядерными или какими-либо другими бесчисленными улучшениями системной архитектуры, которые происходят каждые 6 месяцев. Только по этой причине сортировка наших баз данных не будет проблемой. Кроме того, базы данных очень хорошо сортируют. Они оптимизированы для этого, и у них были годы, чтобы сделать это правильно, язык для этого невероятно гибкий, интуитивно понятный и простой, и, прежде всего, любой начинающий автор SQL знает, как это сделать, и, что еще важнее, он знает, как его редактировать, вносить изменения, выполнять техническое обслуживание и т. д. Если ваши базы данных далеки от налогообложения и вы просто хотите упростить (и сократить!) время разработки, это кажется очевидным выбором.
Тогда есть проблема с Интернетом. Я поиграл с JavaScript, который будет выполнять сортировку HTML-таблиц на стороне клиента, но они неизбежно не будут достаточно гибкими для моих нужд, и опять же, поскольку мои базы данных не облагаются чрезмерными налогами и могут действительно выполнять сортировку легко, мне трудно оправдать время, которое потребуется, чтобы переписать или свернуть свой собственный сортировщик JavaScript. Как правило, то же самое относится и к сортировке на стороне сервера, хотя она, вероятно, уже намного предпочтительнее JavaScript. Мне не особенно нравятся накладные расходы на DataSets, поэтому подайте в суд на меня.
Но это возвращает нас к мысли, что это невозможно & mdash; точнее, не легко. С предыдущими системами я сделал невероятно хакерский способ динамической сортировки. Это не было ни красиво, ни интуитивно понятно, ни просто, ни гибко, и начинающий писатель SQL был бы потерян через несколько секунд. Уже это выглядит не столько «решением», сколько «осложнением».
Следующие примеры не предназначены для демонстрации какого-либо передового опыта или хорошего стиля кодирования или чего-либо еще, и они не указывают на мои способности как программиста T-SQL. Они такие, какие есть, и я полностью признаю, что они сбивают с толку, плохо себя чувствуют и просто взломают.
Мы передаем целочисленное значение в качестве параметра хранимой процедуре (давайте назовем параметр просто «sort»), и из этого мы определяем кучу других переменных. Например ... скажем, сортировка равна 1 (или по умолчанию):
DECLARE @sortCol1 AS varchar(20)
DECLARE @sortCol2 AS varchar(20)
DECLARE @dir1 AS varchar(20)
DECLARE @dir2 AS varchar(20)
DECLARE @col1 AS varchar(20)
DECLARE @col2 AS varchar(20)
SET @col1 = 'storagedatetime';
SET @col2 = 'vehicleid';
IF @sort = 1 -- Default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'asc';
SET @sortCol2 = @col2;
SET @dir2 = 'asc';
END
ELSE IF @sort = 2 -- Reversed order default sort.
BEGIN
SET @sortCol1 = @col1;
SET @dir1 = 'desc';
SET @sortCol2 = @col2;
SET @dir2 = 'desc';
END
Вы уже можете видеть, как, если бы я объявил больше переменных @colX для определения других столбцов, я действительно мог бы проявить творческий подход к сортировке столбцов по значению «сортировки» ... чтобы использовать его, обычно это выглядит так: как следующий невероятно грязный пункт:
ORDER BY
CASE @dir1
WHEN 'desc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir1
WHEN 'asc' THEN
CASE @sortCol1
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END,
CASE @dir2
WHEN 'desc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END DESC,
CASE @dir2
WHEN 'asc' THEN
CASE @sortCol2
WHEN @col1 THEN [storagedatetime]
WHEN @col2 THEN [vehicleid]
END
END
Очевидно, это очень урезанный пример. Реальные вещи, так как у нас обычно есть четыре или пять столбцов для поддержки сортировки, каждый с возможным дополнительным или даже третьим столбцом для сортировки в дополнение к этому (например, по убыванию даты, затем сортируется по имени по возрастанию), и каждый поддерживает направленная сортировка, которая эффективно удваивает количество случаев. Да ... это очень быстро становится волосатым.
Идея состоит в том, что можно "легко" изменить случаи сортировки так, чтобы транспортная система сортировалась до времени хранения ... но псевдо-гибкость, по крайней мере в этом простом примере, действительно заканчивается там. По сути, каждый случай, который не проходит тест (потому что наш метод сортировки не применяется к нему в этот раз), отображает значение NULL. И, таким образом, вы получите предложение, которое работает следующим образом:
ORDER BY NULL DESC, NULL, [storagedatetime] DESC, blah blah
Вы поняли идею. Это работает, потому что SQL Server эффективно игнорирует нулевые значения в порядке по предложениям. Это невероятно сложно поддерживать, как, вероятно, может увидеть любой, кто имеет базовые знания SQL. Если я потерял кого-то из вас, не расстраивайтесь. Нам потребовалось много времени, чтобы заставить это работать, и мы все еще запутались, пытаясь отредактировать это или создать новые как это. К счастью, его не нужно часто менять, иначе он быстро стал бы "не стоящим хлопот".
И все же сделал сработало.
Мой вопрос: Есть ли лучший способ?
У меня все в порядке с решениями, отличными от хранимых процедур, так как я понимаю, что это может быть просто не тот путь. Предпочтительно, я хотел бы знать, может ли кто-нибудь сделать это лучше в рамках хранимой процедуры, но если нет, то как вы все решаете, позволяя пользователю динамически сортировать таблицы данных (в том числе и в двух направлениях) с ASP.NET?
И спасибо, что прочитали (или хотя бы просмотрели) такой длинный вопрос!
PS: рад, что я не показал мой пример хранимой процедуры, которая поддерживает динамическую сортировку, динамическую фильтрацию / текстовый поиск столбцов, разбиение на страницы с помощью ROWNUMBER () OVER, AND try ... уловить откат транзакции при ошибках ... "размером с чудовище" даже не начинает их описывать.
Обновление:
- Я бы хотел избегать динамического SQL . Синтаксический анализ строки и запуск EXEC на ней лишает вас смысла хранить хранимую процедуру в первую очередь. Иногда я задаюсь вопросом, не стоило ли бы того, чтобы такие вещи были полезны, по крайней мере, в этих специальных случаях динамической сортировки. Тем не менее, я всегда чувствую себя грязно, когда создаю такие динамические строки SQL & mdash; как будто я все еще живу в мире Classic ASP.
- Во-первых, во-первых, нам нужны хранимые процедуры для безопасности . Я не могу говорить о проблемах безопасности, я только предлагаю решения. В SQL Server 2005 мы можем устанавливать разрешения (для каждого пользователя, если это необходимо) на уровне схемы для отдельных хранимых процедур, а затем напрямую отклонять любые запросы к таблицам. Критиковать за и против такого подхода, возможно, для другого вопроса, но опять же это не мое решение. Я просто обезьяна с кодом. :)