DBテクニカルサポートのチームが共有するいくつかのfoo.sql
クエリをリファクタリングして文書化する必要があります(お客様の構成などの場合)。各顧客が独自のサーバーとデータベースを持っている定期的に来るチケットのタイプがありますが、それ以外はスキーマは全体で同じです。
ストアドプロシージャは、現時点ではオプションではありません。動的とSQLCMDのどちらを使用するかについて議論しています。SQLServerは少し新しいので、どちらもあまり使用していません。
SQLCMDスクリプティング間違いなく "見た目"がきれいになり、必要に応じてクエリを読みやすくし、クエリに小さな変更を加えるのも簡単ですが、ユーザーはSQLCMDモードを有効にする必要があります。クエリは文字列操作を使用して記述されているため、構文の強調表示が失われるため、動的はより困難です。
これらは、Management Studio 2012、SQLバージョン2008R2を使用して編集および実行されています。どちらの方法の長所/短所、またはいずれかの方法でのSQL Serverの「ベストプラクティス」は何ですか。それらの1つは他より「安全」ですか?
declare @ServerName varchar(50) = 'REDACTED';
declare @DatabaseName varchar(50) = 'REDACTED';
declare @OrderIdsSeparatedByCommas varchar(max) = '597336, 595764, 594594';
declare @sql_OrderCheckQuery varchar(max) = ('
use {@DatabaseName};
select
-- stuff
from
{@ServerName}.{@DatabaseName}.[dbo].[client_orders]
as "Order"
inner join {@ServerName}.{@DatabaseName}.[dbo].[vendor_client_orders]
as "VendOrder" on "Order".o_id = "VendOrder".vco_oid
where "VendOrder".vco_oid in ({@OrderIdsSeparatedByCommas});
');
set @sql_OrderCheckQuery = replace( @sql_OrderCheckQuery, '{@ServerName}', quotename(@ServerName) );
set @sql_OrderCheckQuery = replace( @sql_OrderCheckQuery, '{@DatabaseName}', quotename(@DatabaseName) );
set @sql_OrderCheckQuery = replace( @sql_OrderCheckQuery, '{@OrderIdsSeparatedByCommas}', @OrderIdsSeparatedByCommas );
print (@sql_OrderCheckQuery); -- For debugging purposes.
execute (@sql_OrderCheckQuery);
:setvar ServerName "[REDACTED]";
:setvar DatabaseName "[REDACTED]";
:setvar OrderIdsSeparatedByCommas "597336, 595764, 594594"
use $(DatabaseName)
select
--stuff
from
$(ServerName).$(DatabaseName).[dbo].[client_orders]
as "Order"
inner join $(ServerName).$(DatabaseName).[dbo].[vendor_client_orders]
as "VendOrder" on "Order".o_id = "VendOrder".vco_oid
where "VendOrder".vco_oid in ($(OrderIdsSeparatedByCommas));
これらを邪魔にならないようにするために:
技術的に言えば、これらのオプションは両方とも、送信されるまで解析/検証されない「動的」/アドホッククエリです。また、どちらもパラメータ化されていないため、SQLインジェクションの影響を受けます(SQLCMDスクリプトでは、CMDスクリプトから変数を渡す場合、_'
_を_''
_で置き換える機会があります)。これは、変数が使用されている場所に応じて機能する場合と機能しない場合があります。
それぞれのアプローチには長所と短所があります。
サポートスタッフがアドホッククエリを実行せず、それらの変数を入力するだけの場合、それらのスクリプトを編集して不要な変更を加えることができるSSMSにいる必要はありません。
ユーザーに目的の変数値を要求するCMDスクリプトを作成し、それらの値でSQLCMD.EXEを呼び出します。 CMDスクリプトは、タイムスタンプと送信された変数値を完全に含めて、実行をファイルに記録することもできます。
SQLスクリプトごとに1つのCMDスクリプトを作成し、ネットワーク化された共有フォルダーに配置します。ユーザーがCMDスクリプトをダブルクリックすると、それが機能します。
以下はその例です。
%OrderIDsSeparatedByCommas%
_の値をSQLCMD変数$(OrderIDsSeparatedByCommas)
として渡しますテストSQLスクリプト(名前:FixProblemX.sql):
_SELECT *
FROM sys.objects
WHERE [schema_id] IN ($(OrderIdsSeparatedByCommas));
_
CMDスクリプト(名前:FixProblemX.cmd):
_@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET ScriptLogPath=\\server\share\RunSqlCmdScripts\LogFiles
CLS
SET /P ScriptServerName=Please enter in a Server Name (leave blank to exit):
IF "%ScriptServerName%" == "" GOTO :ThisIsTheEnd
REM echo %ScriptServerName%
:RequestDatabaseName
ECHO.
SET /P ScriptDatabaseName=Please enter in a Database Name (leave blank to list DBs on %ScriptServerName%):
IF "%ScriptDatabaseName%" == "" GOTO :GetDatabaseNames
SQLCMD -b -E -W -h-1 -r0 -S %ScriptServerName% -Q "SET NOCOUNT ON; IF (NOT EXISTS(SELECT [name] FROM sys.databases WHERE [name] = N'%ScriptDatabaseName%')) RAISERROR('Invalid DB name!', 16, 1);" 2> nul
IF !ERRORLEVEL! GTR 0 (
ECHO.
ECHO That Database Name is invalid. Please try again.
SET ScriptDatabaseName=
GOTO :RequestDatabaseName
)
:RequestOrderIDs
ECHO.
SET /P OrderIdsSeparatedByCommas=Please enter in the OrderIDs (separate multiple IDs with commas):
IF "%OrderIdsSeparatedByCommas%" == "" (
ECHO.
ECHO Don't play me like that. You gots ta enter in at least ONE lousy OrderID, right??
GOTO :RequestOrderIDs
)
REM Finally run SQLCMD!!
SQLCMD -E -W -S %ScriptServerName% -d %ScriptDatabaseName% -i FixProblemX.sql -v OrderIdsSeparatedByCommas=%OrderIdsSeparatedByCommas%
REM Log this execution
SET ScriptLogFile=%ScriptLogPath%\%~n0_%USERNAME%.log
REM echo %ScriptLogFile%
IF NOT EXIST %ScriptLogPath% MKDIR %ScriptLogPath%
ECHO %DATE% %TIME% ServerName=%ScriptServerName% DatabaseName=[%ScriptDatabaseName%] OrderIdsSeparatedByCommas=%OrderIdsSeparatedByCommas% >> %ScriptLogFile%
GOTO :ThisIsTheEnd
:GetDatabaseNames
ECHO.
SQLCMD -E -W -h-1 -S %ScriptServerName% -Q "SET NOCOUNT ON; SELECT [name] FROM sys.databases ORDER BY [name];"
ECHO.
GOTO :RequestDatabaseName
:ThisIsTheEnd
PAUSE
_
スクリプトの先頭に向かってScriptLogPath
変数を編集してください。
また、SQLスクリプト(_SQLCMD.EXEの_-i
_コマンドラインスイッチで指定)mightの利点完全修飾パスを持っているからですが、完全にはわかりません。