Kullanıcı tarafından paylaşılan sorgular: Dinamik SQL ve SQLCMD


15

Ben foo.sqlDB teknik destek ekibi (müşteri yapılandırmaları ve bunun gibi şeyler için) tarafından paylaşılacak bir dizi sorguları yeniden gözden geçirmek ve belgelemek zorunda. Her müşterinin kendi sunucularına ve veritabanlarına sahip olduğu düzenli olarak gelen bilet türleri vardır, ancak aksi takdirde şema genel olarak aynıdır.

Saklı yordamlar şu anda bir seçenek değildir. Dinamik veya SQLCMD kullanmak tartışıyorum, SQL Server'da biraz yeni olduğum için ya da çok fazla kullanmadım.

SQLCMD komut dosyaları Bana kesinlikle "daha temiz" görünüyor ve gerektiğinde sorguları okumak ve küçük değişiklikler yapmak daha kolay, ama aynı zamanda kullanıcıyı SQLCMD modunu etkinleştirmeye zorluyor. Dinamik, sözdizimi vurgulaması, dize manipülasyonu kullanılarak yazılan sorgu nedeniyle kaybedildiğinden daha zordur.

Bunlar Management Studio 2012, SQL 2008R2 sürümü kullanılarak düzenleniyor ve çalıştırılıyor. Herhangi bir yöntemin bazı artıları / eksileri veya bir yöntem veya diğeri SQL Server "en iyi uygulamaları" bazıları nelerdir? Biri diğerinden "daha güvenli" mi?

Dinamik örnek:

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);

SQLCMD örneği:

: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));

use ...Senaryonuzun amacı nedir ? Sonraki sorgunun doğru yürütülmesi için önemli mi? Ben soruyorum çünkü geçerli veritabanını değiştirmek sorgunuzun beklenen sonuçlardan biri, dinamik SQL sürümü SQLCMD varyasyon (aksine, dış kapsamda değil, yalnızca dinamik sorgu kapsamında değiştirir ders, sadece bir kapsamı vardır).
Andriy M

Bu usekomut dosyasında kapsam yine de değiştirilmeyeceğinden, ifade büyük olasılıkla dışarıda bırakılabilir. Çapraz sunucu aramaları olacak ama bu yazının kapsamı dışında olabilir kullanım vakaları az sayıda var.
Phrancis

Yanıtlar:


13

Bunları yoldan çıkarmak için:

  • Teknik olarak konuşursak, bu seçeneklerin her ikisi de gönderilinceye kadar ayrıştırılmayan / doğrulanmayan "dinamik" / ad hoc sorgulardır. Onlar SQLCMD komut dosyaları ile olsa (parametreli olmadığı için o zaman değiştirmek için bir fırsat var bir CMD komut bir değişkene geçiyoruz Ve her ikisi de, SQL Enjeksiyon duyarlıdır 'ile ''ya da değil çalışma yere bağlı olabilir, değişkenler kullanılmaktadır).

  • Her yaklaşımın artıları ve eksileri vardır:

    • SSMS'deki SQL komut dosyaları kolayca düzenlenebilir (bu bir gereksinimse harikadır) ve sonuçlarla çalışmak SQLCMD çıktısından daha kolaydır. Aşağı tarafta, kullanıcı bir IDE'dedir, bu yüzden SQL'i karıştırmak kolaydır ve IDE, SQL'in bunu yapmasını bilmeden çok çeşitli değişiklikler yapmayı kolaylaştırır.
    • Komut dosyalarını SQLCMD.EXE aracılığıyla çalıştırmak kullanıcının kolayca değişiklik yapmasına izin vermez (komut dosyasını bir düzenleyicide düzenlemeden ve önce kaydetmeden). Kullanıcıların komut dosyalarını değiştirmemesi gerekiyorsa bu harikadır. Bu yöntem aynı zamanda her yürütme işleminin günlüğe kaydedilmesine de olanak tanır. Aşağı tarafta, komut dosyalarını düzenli olarak düzenlemeye ihtiyaç varsa, o zaman oldukça hantal olur. Veya kullanıcıların bir sonuç kümesinin 100 bin satırını taraması ve / veya bu sonuçları Excel'e veya başka bir şeye kopyalaması gerekiyorsa, bu yaklaşımda da zordur.

Destek görevlileriniz geçici sorgular yapmıyorsa ve yalnızca bu değişkenleri dolduruyorsa, bu komut dosyalarını düzenleyebilecekleri ve istenmeyen değişiklikler yapabildikleri SSMS'de olmaları gerekmez.

Kullanıcı istenen değişken değerleri için istemi CMD komut dosyaları oluşturmak ve daha sonra bu değerlerle SQLCMD.EXE çağırmak . CMD betiği, yürütmeyi zaman damgası ve gönderilen değişken değerleri ile birlikte bir dosyaya kaydedebilir.

Her SQL komut dosyası için bir CMD komut dosyası oluşturun ve ağa bağlı bir paylaşılan klasöre yerleştirin. Bir kullanıcı CMD betiğini çift tıklar ve çalışır.

İşte bir örnek:

  • kullanıcıdan sunucu adını ister (bu konuda henüz hata kontrolü yapılmaz)
  • kullanıcıdan veritabanı adı ister
    • boş bırakılırsa, belirtilen sunucudaki veritabanlarını listeler ve yeniden istenir
    • veritabanı adı geçersizse, kullanıcıdan tekrar istenir
  • kullanıcıdan OrderIDsSeparatedByCommas ister
    • boşsa, kullanıcıdan tekrar ister
  • %OrderIDsSeparatedByCommas%SQLCMD değişkeni olarak geçen SQL komut dosyasını çalıştırır$(OrderIDsSeparatedByCommas)
  • yürütme tarihini, saatini, SunucuAdı, VeritabanıAdı ve OrderIDsSeparatedByCommas komut dosyasını çalıştıran Windows Oturum Açma adında bir günlük dosyasına kaydeder (bu şekilde, günlük dizini ağsa ve bunu kullanan birden çok kişi varsa, herhangi bir yazma olmaz günlük dosyasındaki, USERNAME dosyasının giriş başına dosyaya kaydedilip girmeyeceği gibi bir çekişme olabilir)
    • günlük dosyası dizini yoksa, oluşturulacaktır

SQL komut dosyasını test et ( name : FixProblemX.sql ):

SELECT  *
FROM    sys.objects
WHERE   [schema_id] IN ($(OrderIdsSeparatedByCommas));

CMD komut dosyası (adlandırılmış: 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

ScriptLogPathDeğişkeni komut dosyasının üst kısmına doğru düzenlediğinizden emin olun .

Ayrıca, (tarafından belirtilen SQL komut -iiçin komut satırı anahtarı sqlCmd.exe ) olabilir tam olarak nitelenmiş yolunu sahip yarar, ama tamamen emin değil.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.