Это проблема, которую я исследовал часами раньше. Мне кажется, что это то, что должно было быть решено современными решениями СУБД, но пока я не нашел ничего, что действительно удовлетворяло бы то, что я считаю невероятно распространенной потребностью в любом веб-приложении или приложении Windows с серверной частью базы данных.
Я говорю о динамической сортировке. В моем фантастическом мире это должно быть так просто, как:
ORDER BY @sortCol1, @sortCol2
Это канонический пример, приводимый новичками в области SQL и хранимых процедур на форумах в Интернете. "Почему это невозможно?" они спрашивают. Неизменно кто-то в конце концов приходит к ним, чтобы прочесть лекцию о компилируемой природе хранимых процедур, о планах выполнения в целом и о всевозможных других причинах, по которым невозможно поместить параметр непосредственно в ORDER BY
предложение.
Я знаю, что некоторые из вас уже думают: «Тогда пусть сортировку сделает клиент». Естественно, это снимает нагрузку с вашей базы данных. В нашем случае, однако, наши серверы баз данных даже не ломают голову в 99% случаев, и они еще даже не многоядерные или какие-либо другие бесчисленные улучшения системной архитектуры, которые происходят каждые 6 месяцев. Только по этой причине не было бы проблем, если бы наши базы данных обрабатывали сортировку. Кроме того, базы данных оченьхорошо разбирается. Они оптимизированы для этого, и у них были годы, чтобы сделать это правильно, язык для этого невероятно гибкий, интуитивно понятный и простой, и, прежде всего, любой начинающий писатель SQL знает, как это сделать, и, что еще более важно, они знают, как его редактировать, вносить изменения, обслуживать и т. д. Когда ваши базы данных не облагаются налогом, и вы просто хотите упростить (и сократить!) время разработки, это кажется очевидным выбором.
Тогда есть веб-проблема. Я поигрался с JavaScript, который будет выполнять сортировку HTML-таблиц на стороне клиента, но они неизбежно недостаточно гибкие для моих нужд, и, опять же, поскольку мои базы данных не облагаются чрезмерными налогами и могут выполнять сортировку очень легко, я трудно оправдать время, которое потребуется на переписывание или внедрение собственного сортировщика JavaScript. То же самое обычно касается сортировки на стороне сервера, хотя она, вероятно, уже намного предпочтительнее JavaScript. Я не из тех, кому особенно нравятся накладные расходы на DataSets, так что подайте на меня в суд.
Но это возвращает нас к мысли, что это невозможно - или, скорее, нелегко. Я проделал с предыдущими системами невероятно хитрый способ динамической сортировки. Это не было ни красиво, ни интуитивно, ни просто, ни гибко, и начинающий писатель 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 для определения других столбцов, я действительно мог бы проявить творческий подход к столбцам для сортировки на основе значения "sort" ... чтобы использовать его, это обычно заканчивается следующим образом невероятно запутанная статья:
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
Очевидно, это очень урезанный пример. Реальный материал, поскольку у нас обычно есть четыре или пять столбцов для поддержки сортировки, каждый с возможным второстепенным или даже третьим столбцом для сортировки в дополнение к этому (например, дата по убыванию, а затем сортировка по вторичному имени по возрастанию), и каждый из них поддерживает двух- направленная сортировка, которая эффективно удваивает количество дел. Да ... это очень быстро становится волосатым.
Идея состоит в том, что можно «легко» изменить варианты сортировки, так что Vehicleid будет отсортирован до времени хранения ... но псевдогибкость, по крайней мере в этом простом примере, на этом действительно заканчивается. По сути, каждый случай, который не проходит тест (потому что наш метод сортировки на этот раз не применяется), отображает значение NULL. Таким образом, вы получите предложение, которое работает следующим образом:
ORDER BY NULL DESC, NULL, [storagedatetime] DESC, blah blah
Вы уловили идею. Это работает, потому что SQL Server фактически игнорирует нулевые значения в предложениях по порядку. Это невероятно сложно поддерживать, что, вероятно, может понять любой, кто имеет хоть какие-то базовые знания SQL. Если я потерял кого-то из вас, не расстраивайтесь. Нам потребовалось много времени, чтобы заставить его работать, и мы все еще путаемся, пытаясь отредактировать его или создать новые подобные. К счастью, его не нужно часто менять, иначе он быстро стал бы «не стоящим проблем».
Тем не менее , он сделал работу.
Тогда мой вопрос: есть ли способ лучше?
Я согласен с решениями, отличными от хранимых процедур, поскольку я понимаю, что это может быть просто не выход. Предпочтительно, я хотел бы знать, может ли кто-нибудь сделать это лучше в рамках хранимой процедуры, но если нет, как вы все справляетесь, позволяя пользователю динамически сортировать таблицы данных (в том числе в двух направлениях) с помощью ASP.NET?
И спасибо, что прочитали (или хотя бы бегло просмотрели) такой длинный вопрос!
PS: Радуйтесь, что я не показал свой пример хранимой процедуры, которая поддерживает динамическую сортировку, динамическую фильтрацию / текстовый поиск столбцов, разбиение на страницы с помощью ROWNUMBER () OVER и попробуйте ... поймать с откатом транзакции при ошибках ... «размером с чудовище» даже не начать описывать их.
Обновить:
- Я бы хотел избежать динамического SQL . Синтаксический анализ строки и запуск на ней EXEC в первую очередь лишают смысла иметь хранимую процедуру. Иногда я задаюсь вопросом, не стоит ли этого делать, по крайней мере, в этих особых случаях динамической сортировки. Тем не менее, я всегда чувствую себя грязным, когда использую подобные динамические строки SQL - как будто я все еще живу в мире классических ASP.
- В первую очередь, хранимые процедуры нужны нам для обеспечения безопасности . Я не могу звонить по соображениям безопасности, я только предлагаю решения. С помощью SQL Server 2005 мы можем устанавливать разрешения (для каждого пользователя, если это необходимо) на уровне схемы для отдельных хранимых процедур, а затем напрямую отклонять любые запросы к таблицам. Критика «за» и «против» этого подхода, возможно, для другого вопроса, но, опять же, это не мое решение. Я просто обезьяна с кодом свинца. :)