Построение ORDER BY
динамически довольно проста. Я предполагаю, что вы проезжаете такие параметры, как:
@OrderByCol1 NVARCHAR(255),
@OrderByCol2 NVARCHAR(255),
...etc...
Это может быть и не включать направление, например. N'MyColumn DESC'
. Таким образом, то вы можете построить это вместе следующим образом:
DECLARE @sql NVARCHAR(MAX);
SELECT @sql = N'SELECT ...
FROM ...
WHERE ...
ORDER BY NULL'
+ COALESCE(',' + @OrderByCol1, '')
+ COALESCE(',' + @OrderByCol2, '')
...etc...;
PRINT @sql;
--EXEC sp_executesql @sql;
Поскольку мы, по-видимому нужно резюмировать Целые инъекции SQL беседу каждый раз ответ даже упоминает динамический SQL, я добавлю несколько примеров.
Если они могут только сортировать по возрастанию, вы можете предотвратить SQL-инъекцию, просто обернув значения параметров в QUOTENAME()
.
+ COALESCE(',' + QUOTENAME(@OrderByCol1), '')
+ COALESCE(',' + QUOTENAME(@OrderByCol2), '')
В противном случае, вы можете также разделить параметры друг от друга пробелом (предполагая, что ваши имена столбцов не содержат пробелов, которые они не должны!), И проверить, что левая сторона всегда присутствует в sys.columns
.
IF @OrderByCol1 IS NOT NULL AND EXISTS
(
SELECT 1 FROM sys.columns
WHERE [object_id] = OBJECT_ID('dbo.MyTable')
AND name = LTRIM(LEFT(@OrderByCol1, CHARINDEX(' ', @OrderByCol1)))
)
BEGIN
SET @sql += ',' + @OrderByCol1;
END
Вы также можете иметь чеки есть в случае, если они не проходят ни в какой-либо из параметров, или только передать значение в параметр # 4 и т.д. выше делает это.
Возможно, было бы лучше передать их при использовании TVP, тогда вам не нужно устанавливать произвольные и искусственные ограничения на количество столбцов, которые они могут выбрать. Вот один пример трехколоночного ТВП, который позволяет вам передавать в порядке порядка по столбцам, диктовать порядок, в котором они применяются, и указывать порядок сортировки для каждого. Это также облегчает проверку того, что каждый столбец действительно является столбцом (и, если вы называете столбец [1;truncate table dbo.something]
, вы заслуживаете того, что получаете ...).
Во-первых, создайте следующий определяемый пользователем тип таблицы в базе данных:
CREATE TYPE dbo.OrderByColumns AS TABLE
(
[Sequence] TINYINT PRIMARY KEY,
ColumnName SYSNAME NOT NULL,
Direction VARCHAR(4) NOT NULL DEFAULT 'ASC'
);
Тогда:
DECLARE @x dbo.OrderByColumns;
INSERT @x SELECT 1, N'name', 'ASC';
INSERT @x SELECT 2, N'ID', 'DESC';
INSERT @x SELECT 3, N'1;truncate table dbo.whatever', 'DESC';
-- the above could be a parameter to your stored procedure
-- and could be populated in a DataTable in your application
DECLARE @sql NVARCHAR(MAX) = N'SELECT ... FROM ...
WHERE ... ORDER BY NULL';
SELECT @sql += ',' + QUOTENAME(x.ColumnName) + ' ' + x.Direction
FROM sys.columns AS c
INNER JOIN @x AS x
ON c.name = x.ColumnName
AND c.[object_id] = OBJECT_ID('dbo.MyTable')
ORDER BY x.[Sequence] OPTION (MAXDOP 1);
PRINT @sql;
В то время как вы можете сделать это с помощью CASE
, порождая ORDER BY
динамически - особенно, когда это влияет на выбор плана - на самом деле может быть лучше для производительности. Со статическим запросом вы получаете план на все, что было вначале @order_column
, а затем оно используется повторно, даже если другой столбец заказов мог привести к другому, более эффективному плану. Различные планы, скорее всего, с разными предложениями ORDER BY
, потому что для них требуются разные операторы СОРТ. Вы можете немного обойти эту проблему, используя OPTION (RECOMPILE)
, что гарантирует, что вы каждый раз получаете новый план, но теперь вы платите за компиляцию каждый раз, даже если один и тот же заказ всегда или почти всегда используется.
При использовании динамического SQL каждая версия запроса оптимизируется отдельно. Планирование кеш-памяти - проблема, которая несколько компенсируется настройкой сервера optimize for ad hoc workloads
. Это не позволяет SQL Server кэшировать весь план для конкретного варианта запроса до тех пор, пока эта конкретная вариация не будет использоваться дважды.
Да, используя динамический SQL - создайте свои операторы SQL внутри вашей процедуры с или без предложения ORDER BY. Затем выполните этот оператор SQL с помощью 'exec sp_executesql ....' –
создайте динамический запрос и выполните с sp_executesql – Miller
... и не забудьте защитить от SQL Injection. – RBarryYoung