2014-01-22 5 views
1

У меня есть несколько сложная хранимая процедура. Я хочу, чтобы пользователи могли выбирать столбцы для заказа. Они должны иметь возможность выбирать столько или несколько столбцов, сколько захотите.Дополнительный заказ В хранимой процедуре?

Есть ли способ реализовать это в хранимой процедуре? Как передать имена столбцов в процедуру, а затем отразить те, которые заключают в порядок? Отмечая, что будет переменное количество столбцов.

Я понимаю, как передать параметры, просто не знаю, если это будет возможно динамически создавать заказ по статье в хранимой процедуре

+5

Да, используя динамический SQL - создайте свои операторы SQL внутри вашей процедуры с или без предложения ORDER BY. Затем выполните этот оператор SQL с помощью 'exec sp_executesql ....' –

+3

создайте динамический запрос и выполните с sp_executesql – Miller

+2

... и не забудьте защитить от SQL Injection. – RBarryYoung

ответ

4

Построение 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 кэшировать весь план для конкретного варианта запроса до тех пор, пока эта конкретная вариация не будет использоваться дважды.

+0

'set @ orderbycol1 = '1; truncate table dbo.ReallyImportantTable'' –

+0

@Ben да, видите ли вы, у меня есть целый абзац о том, как вы можете помешать SQL-инъекции? –

+0

моя ошибка. Отметьте это до многозадачности при написании комментариев! –

2

Вы можете собрать SQL динамически и выполнить его с помощью sp_executesql. Однако это теряет некоторые из преимуществ производительности и безопасности, которые вы получаете от использования параметризованной хранимой процедуры, которая не является динамической.

Если возможные столбцы ORDER BY представляют собой конечный список, вы можете использовать CASE WHEN в предложении ORDER BY, чтобы изменить порядок на основе переданного параметра. Например. если вы прошли в параметре называется @order_column, вы можете сделать

ORDER BY 
CASE WHEN @order_column='ColumnA' 
THEN ColumnA END 
CASE WHEN @order_column='ColumnB' 
THEN ColumnB END 
+0

Ну, фактически, генерируя «ORDER BY» динамически - особенно когда это влияет на выбор плана - на самом деле может быть лучше * для производительности. При статическом запросе вы получаете план для любого '@ order_column', тогда он будет использоваться повторно, даже если другой столбец упорядочения мог привести к другому, более эффективному плану. Когда вы используете динамический SQL, каждая версия запроса оптимизируется отдельно. Распространение кеша плана - это проблема, которая несколько компенсируется «оптимизацией для специальных рабочих нагрузок». –

+0

Хм, интересно, и хороший момент. –

+0

Проголосовали за то, что вы не полагались на динамический sql, но, к сожалению, Аарон прав. Если OP не является стороной пейджингового сервера, я предлагаю заказать клиентскую сторону. – jean

Смежные вопросы