उपयोगकर्ता-साझा क्वेरीज़: डायनेमिक SQL बनाम SQLCMD


15

मुझे कई foo.sqlप्रश्नों को रिफ्लेक्टर और दस्तावेज करना है, जो डीबी टेक सपोर्ट (ग्राहक कॉन्फ़िगरेशन और उस तरह की चीजों के लिए) की एक टीम द्वारा साझा किए जाएंगे। टिकट के प्रकार हैं जो नियमित रूप से आते हैं जहां प्रत्येक ग्राहक के पास अपने सर्वर और डेटाबेस होते हैं, लेकिन अन्यथा स्कीमा पूरे बोर्ड में समान होते हैं।

मौजूदा समय में संग्रहीत प्रक्रियाएं एक विकल्प नहीं हैं। मैं इस बात पर बहस कर रहा हूं कि डायनेमिक या एसक्यूसीएमडी का उपयोग करना है या नहीं, मैंने एसक्यूएल सर्वर पर थोड़ा नया होने के कारण इसका अधिक उपयोग नहीं किया है।

एसक्यूसीएमडी स्क्रिप्टिंग मुझे लगता है कि निश्चित रूप से "क्लीनर" मुझे लगता है, और आवश्यकतानुसार पढ़ने और प्रश्नों में छोटे बदलाव करना आसान है, लेकिन उपयोगकर्ता को एसक्यूसीएमडी मोड को सक्षम करने के लिए भी मजबूर करता है। डायनामिक अधिक कठिन है क्योंकि स्ट्रिंग हेरफेर का उपयोग करके क्वेरी को लिखे जाने के कारण सिंटैक्स हाइलाइटिंग हानि है।

इन्हें प्रबंधन स्टूडियो 2012, SQL संस्करण 2008R2 का उपयोग करके संपादित और चलाया जा रहा है। किसी एक विधि या दूसरे तरीके पर SQL सर्वर "बेस्ट प्रैक्टिस" में से किसी विधि के कुछ पेशेवरों / विपक्षों में से कुछ हैं? क्या उनमें से एक दूसरे की तुलना में "सुरक्षित" है?

गतिशील उदाहरण:

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 उदाहरण:

: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 ...आपकी स्क्रिप्ट का उद्देश्य क्या है ? क्या बाद की क्वेरी के सही निष्पादन के लिए यह महत्वपूर्ण है? मैं पूछ रहा हूं क्योंकि यदि वर्तमान डेटाबेस को बदलना आपकी क्वेरी के अपेक्षित परिणामों में से एक है, तो गतिशील SQL संस्करण केवल इसे गतिशील क्वेरी के दायरे में बदल देगा, बाहरी दायरे में नहीं, SQLCMD भिन्नता के विपरीत (जो कि, बेशक, केवल एक गुंजाइश है)।
एंड्री एम ०

useबयान शायद, बाहर छोड़ा जा सकता है के रूप में गुंजाइश इस विशेष स्क्रिप्ट के दौरान वैसे भी बदला नहीं जाएगा। मेरे पास उपयोग मामलों की एक छोटी संख्या है जहां क्रॉस-सर्वर खोज होगी लेकिन यह इस पद के दायरे से परे हो सकती है।
फ्राँसिस

जवाबों:


13

बस इन्हें रास्ते से हटाने के लिए:

  • तकनीकी रूप से कहें, तो ये दोनों विकल्प "डायनामिक" / तदर्थ प्रश्न हैं जिन्हें तब तक पार्स / मान्य नहीं किया जाता है जब तक कि उन्हें सबमिट नहीं किया जाता है। और दोनों SQL इंजेक्शन लिए अतिसंवेदनशील होते हैं, क्योंकि वे (parameterized नहीं कर रहे हैं SQLCMD स्क्रिप्ट के साथ हालांकि, यदि आप एक अध्यक्ष एवं प्रबंध निदेशक स्क्रिप्ट से एक चर में से गुजर रहे हैं तो आप को बदलने के लिए एक अवसर के लिए क्या है 'के साथ '', या काम नहीं जहां पर निर्भर करता है हो सकता है जो चर का उपयोग किया जा रहा है)।

  • प्रत्येक दृष्टिकोण के लिए पेशेवरों और विपक्ष हैं:

    • SSMS में SQL स्क्रिप्ट को आसानी से संपादित किया जा सकता है (जो कि आवश्यकता होने पर बहुत अच्छा है) और परिणाम के साथ काम करना SQLCMD से आउटपुट की तुलना में आसान है। डाउन-साइड पर, उपयोगकर्ता एक आईडीई में है, इसलिए एसक्यूएल को गड़बड़ाना आसान है, और आईडीई यह करने के लिए एसक्यूएल को जाने बिना एक महान परिवर्तन करना आसान बनाता है।
    • SQLCMD.EXE के माध्यम से स्क्रिप्ट चलाना उपयोगकर्ता को आसानी से परिवर्तन करने की अनुमति नहीं देता है (किसी संपादक में स्क्रिप्ट को संपादित किए बिना और फिर इसे पहले सहेजना)। यह बहुत अच्छा है अगर उपयोगकर्ताओं को स्क्रिप्ट्स को बदलना नहीं चाहिए। यह विधि इसके प्रत्येक निष्पादन को लॉग करने की अनुमति भी देती है। डाउन-साइड पर, यदि स्क्रिप्ट को नियमित रूप से संपादित करने की आवश्यकता है, तो यह काफी बोझिल होगा। या, यदि उपयोगकर्ताओं को परिणाम सेट की 100k पंक्तियों के माध्यम से स्कैन करने और / या उन परिणामों को एक्सेल या कुछ पर कॉपी करने की आवश्यकता होती है, तो यह इस दृष्टिकोण में भी मुश्किल है।

यदि आपके समर्थक लोग तदर्थ प्रश्न नहीं कर रहे हैं और केवल उन चरों को भर रहे हैं, तो उन्हें SSMS में रहने की आवश्यकता नहीं है जहाँ वे उन लिपियों को संपादित कर सकते हैं और अवांछित परिवर्तन कर सकते हैं।

मैं वांछित चर मानों के लिए उपयोगकर्ता को संकेत देने के लिए CMD स्क्रिप्ट बनाऊंगा और फिर उन मूल्यों के साथ SQLCMD.EXE को कॉल करूंगा । सीएमडी स्क्रिप्ट निष्पादन को एक फ़ाइल में भी दर्ज कर सकती है, जो समय स्टैम्प और चर मानों के साथ पूरी होती है।

प्रति SQL स्क्रिप्ट और नेटवर्क साझा किए गए स्थान में एक CMD स्क्रिप्ट बनाएं। एक उपयोगकर्ता CMD स्क्रिप्ट पर डबल-क्लिक करता है और यह सिर्फ काम करता है।

यहाँ एक उदाहरण है कि:

  • सर्वर नाम के लिए उपयोगकर्ता को संकेत देता है (उस पर कोई त्रुटि जाँच नहीं)
  • डेटाबेस नाम के लिए उपयोगकर्ता को संकेत देता है
    • यदि खाली छोड़ दिया जाता है, तो यह निर्दिष्ट सर्वर पर डेटाबेस को सूचीबद्ध करेगा और फिर से संकेत दिया जाएगा
    • यदि डेटाबेस का नाम अमान्य है, तो उपयोगकर्ता को फिर से संकेत दिया जाएगा
  • उपयोगकर्ता के लिए OrderIDsSeparatedByCommas का संकेत देता है
    • यदि रिक्त है, तो उपयोगकर्ता को फिर से संकेत देता है
  • SQL स्क्रिप्ट चलाता है, %OrderIDsSeparatedByCommas%SQLCMD चर के मूल्य के रूप में गुजर रहा है$(OrderIDsSeparatedByCommas)
  • निष्पादन दिनांक, समय, ServerName, DatabaseName, और OrderIDSeparatedByCommas लॉग में लॉग इन करने के लिए एक लॉग फ़ाइल जिसका नाम Windows लॉग इन स्क्रिप्ट चल रहा है (इस तरह, यदि लॉग डायरेक्टरी नेटवर्क है और इस का उपयोग करने वाले कई लोग हैं, तो कोई लेखन नहीं होगा लॉग फ़ाइल पर विवाद जैसे कि USERNAME को प्रति प्रविष्टि फ़ाइल में लॉग इन किया जाना हो सकता है)
    • यदि लॉग फ़ाइल निर्देशिका मौजूद नहीं है, तो इसे बनाया जाएगा

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 लिए कमांड-लाइन स्विच द्वारा निर्दिष्ट ) पूरी तरह से योग्य पथ होने से लाभ हो सकता है, लेकिन पूरी तरह से निश्चित नहीं है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.