Это проблема, которую я исследовал часами раньше. Мне кажется, что это то, что должно было быть решено современными решениями СУБД, но пока я не нашел ничего, что действительно удовлетворяло бы то, что я считаю невероятно распространенной потребностью в любом веб-приложении или приложении 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 мы можем устанавливать разрешения (для каждого пользователя, если это необходимо) на уровне схемы для отдельных хранимых процедур, а затем напрямую отклонять любые запросы к таблицам. Критика «за» и «против» этого подхода, возможно, для другого вопроса, но, опять же, это не мое решение. Я просто обезьяна с кодом свинца. :)
источник
Ответы:
Да, это больно, и то, как вы это делаете, похоже на то, что делаю я:
Для меня это все же намного лучше, чем создание динамического SQL из кода, который превращается в кошмар масштабируемости и обслуживания для администраторов баз данных.
То, что я делаю из кода, - это рефакторинг разбиения по страницам и сортировки, так что у меня, по крайней мере, не будет много повторений с заполнением значений для
@SortExpr
и@SortDir
.Что касается SQL, сохраняйте одинаковую структуру и форматирование для разных хранимых процедур, чтобы он был по крайней мере аккуратным и узнаваемым, когда вы входите, чтобы внести изменения.
источник
Этот подход предотвращает двойное дублирование сортируемых столбцов в порядке следования и является немного более читаемым IMO:
источник
Динамический SQL по-прежнему возможен. Вам просто нужно решить, будет ли этот вариант более приемлемым, чем то, что у вас есть сейчас.
Вот статья, которая показывает это: http://www.4guysfromrolla.com/webtech/010704-1.shtml .
источник
Мои приложения часто это делают, но все они динамически строят SQL. Однако когда я имею дело с хранимыми процедурами, я делаю следующее:
select * from dbo.fn_myData() where ... order by ...
так, чтобы вы могли динамически указать там порядок сортировки.Тогда, по крайней мере, динамическая часть находится в вашем приложении, но база данных по-прежнему выполняет тяжелую работу.
источник
Техника хранимых процедур (взлом?), Которую я использовал, чтобы избежать динамического SQL для определенных заданий, - это иметь уникальный столбец сортировки. То есть,
Это легко превратить в отправку - вы можете объединить поля в столбце mySort, изменить порядок с помощью математических функций или функций даты и т. Д.
Тем не менее, предпочтительно я использую свои asp.net gridviews или другие объекты со встроенной сортировкой, чтобы выполнять сортировку для меня ПОСЛЕ получения данных из Sql-Server. Или даже если он не встроен - например, таблицы данных и т. Д. В asp.net.
источник
Есть несколько способов взломать это.
Предпосылки:
Затем вставьте во временную таблицу:
Метод № 2: настройте связанный сервер обратно на себя, затем выберите из него с помощью openquery: http://www.sommarskog.se/share_data.html#OPENQUERY
источник
Может быть и третий вариант, поскольку у вашего сервера много свободных циклов - используйте вспомогательную процедуру для сортировки через временную таблицу. Что-то вроде
Предостережение: я не тестировал это, но он «должен» работать в SQL Server 2005 (который создаст временную таблицу из набора результатов без предварительного указания столбцов).
источник
В какой-то момент не стоит ли отойти от хранимых процедур и просто использовать параметризованные запросы, чтобы избежать такого рода хакерских атак?
источник
Согласен, использую клиентскую часть. Но похоже, что это не тот ответ, который вы хотите слышать.
Итак, он идеален, как есть. Я не знаю, почему вы хотели бы это изменить или даже спросить: «Есть ли способ лучше». Действительно, его следует называть «Путь». Кроме того, похоже, что он отлично работает и соответствует потребностям проекта и, вероятно, будет достаточно расширяемым на долгие годы. Поскольку ваши базы данных не облагаются налогом, а сортировка действительно проста, она должна оставаться такой на долгие годы.
Я бы не стал беспокоиться.
источник
Когда вы просматриваете отсортированные результаты, динамический SQL - хороший вариант. Если вы параноидально относитесь к SQL-инъекции, вы можете использовать номера столбцов вместо имени столбца. Я сделал это перед тем, как использовать отрицательные значения для спуска. Что-то вроде этого...
Тогда вам просто нужно убедиться, что число находится внутри от 1 до # столбцов. Можно даже расширить этот список номеров столбцов и разобрать , что в таблицу целых чисел , используя функцию , как это . Затем вы построили бы предложение order by следующим образом ...
Один из недостатков заключается в том, что вам нужно помнить порядок каждого столбца на стороне клиента. Особенно, если вы не отображаете все столбцы или отображаете их в другом порядке. Когда клиент хочет выполнить сортировку, вы сопоставляете имена столбцов с порядком столбцов и генерируете список целых чисел.
источник
Аргументом против выполнения сортировки на стороне клиента является большой объем данных и разбивка на страницы. Когда количество строк превышает то, что вы можете легко отобразить, вы часто выполняете сортировку как часть пропуска / приема, которую вы, вероятно, захотите запустить в SQL.
Для Entity Framework вы можете использовать хранимую процедуру для обработки текстового поиска. Если вы столкнулись с той же проблемой сортировки, решение, которое я видел, состоит в том, чтобы использовать сохраненную процедуру для поиска, возвращающую только ключ id, установленный для совпадения. Затем выполните повторный запрос (с сортировкой) к базе данных, используя идентификаторы в списке (содержит). EF справляется с этим довольно хорошо, даже если набор идентификаторов довольно большой. Да, это два пути туда и обратно, но он позволяет вам всегда сохранять сортировку в БД, что может быть важно в некоторых ситуациях, и не позволяет вам писать лодку логики в хранимой процедуре.
источник
Как насчет обработки сортировки по материалам, отображающим результаты - сеткам, отчетам и т. Д., А не по SQL?
РЕДАКТИРОВАТЬ:
Чтобы прояснить ситуацию, поскольку этот ответ был отклонен ранее, я немного уточню ...
Вы заявили, что знаете о сортировке на стороне клиента, но хотите избежать ее. Это, конечно, ваш выбор.
Однако я хочу отметить, что, выполняя это на стороне клиента, вы можете получать данные ОДИН РАЗ, а затем работать с ними, как хотите - вместо того, чтобы каждый раз совершать несколько поездок туда и обратно на сервер. вид меняется.
Ваш SQL Server сейчас не облагается налогом, и это здорово. Не должно быть. Но то, что он еще не перегружен, не означает, что он останется таким навсегда.
Если вы используете какой-либо из новых материалов ASP.NET для отображения в Интернете, многие из них уже встроены.
Стоит ли добавлять столько кода в каждую хранимую процедуру только для сортировки? Опять ваш звонок.
В конечном итоге я не буду отвечать за его поддержку. Но подумайте о том, что будет происходить при добавлении / удалении столбцов в различных наборах данных, используемых хранимыми процедурами (требующих изменений в операторах CASE), или когда внезапно вместо сортировки по двум столбцам пользователь решает, что им нужно три - требуя, чтобы вы теперь обновили каждую из ваших хранимых процедур, использующих этот метод.
Для меня стоит получить работающее клиентское решение и применить его к нескольким отображаемым данным, обращенным к пользователю, и покончить с этим. Если добавлен новый столбец, он уже обработан. Если пользователь хочет отсортировать по нескольким столбцам, он может отсортировать по двум или двадцати из них.
источник
Извините, я опоздал на вечеринку, но вот еще один вариант для тех, кто действительно хочет избежать динамического SQL, но хочет гибкости, которую он предлагает:
Вместо динамической генерации SQL «на лету» напишите код для генерации уникального процесса для всех возможных вариантов. Затем вы можете написать метод в коде, чтобы просмотреть параметры поиска и выбрать соответствующую процедуру для вызова.
Если у вас есть всего несколько вариантов, вы можете просто создать процессы вручную. Но если у вас много вариантов, то вместо того, чтобы поддерживать их все, вы просто поддерживаете свой генератор процессов, чтобы он воссоздал их.
В качестве дополнительного преимущества вы получите лучшие планы SQL для повышения производительности, делая это тоже.
источник
Это решение может работать только в .NET, я не знаю.
Я получаю данные в C # с начальным порядком сортировки в предложении SQL order by, помещаю эти данные в DataView, кэширую их в переменной сеанса и использую для создания страницы.
Когда пользователь щелкает заголовок столбца для сортировки (или страницы, или фильтра), я не возвращаюсь в базу данных. Вместо этого я возвращаюсь к своему кэшированному DataView и устанавливаю его свойство «Sort» на выражение, которое я создаю динамически, точно так же, как я бы сделал динамический SQL. (Я выполняю фильтрацию таким же образом, используя свойство "RowFilter").
Вы можете увидеть / почувствовать его работу в демонстрации моего приложения BugTracker.NET на http://ifdefined.com/btnet/bugs.aspx
источник
Вам следует избегать сортировки SQL Server, если это не необходимо. Почему бы не выполнить сортировку на сервере приложений или на стороне клиента? Также .NET Generics выполняет исключительную сортировку
источник