Sqlserver
 sql >> डेटाबेस >  >> RDS >> Sqlserver

SQL सर्वर में गतिशील SQL निष्पादन

डायनेमिक SQL एक स्टेटमेंट है जिसे रनटाइम पर बनाया और निष्पादित किया जाता है, जिसमें आमतौर पर डायनामिक रूप से जेनरेट किए गए SQL स्ट्रिंग पार्ट्स, इनपुट पैरामीटर या दोनों होते हैं।

गतिशील रूप से उत्पन्न SQL कमांड को बनाने और चलाने के लिए विभिन्न विधियाँ उपलब्ध हैं। वर्तमान लेख उन्हें तलाशने, उनके सकारात्मक और नकारात्मक पहलुओं को परिभाषित करने, और कुछ सामान्य परिदृश्यों में प्रश्नों को अनुकूलित करने के लिए व्यावहारिक दृष्टिकोण प्रदर्शित करने वाला है।

हम गतिशील SQL को निष्पादित करने के लिए दो तरीकों का उपयोग करते हैं:EXEC कमांड और sp_executesql संग्रहीत प्रक्रिया।

EXEC/EXECUTE कमांड का उपयोग करना

पहले उदाहरण के लिए, हम AdventureWorks . से एक साधारण गतिशील SQL कथन बनाते हैं डेटाबेस। उदाहरण में एक फ़िल्टर है जो संयोजित स्ट्रिंग चर @AddressPart के माध्यम से पारित किया गया है और अंतिम आदेश में निष्पादित किया गया है:

USE AdventureWorks2019

-- Declare variable to hold generated SQL statement
DECLARE @SQLExec NVARCHAR(4000) 
DECLARE @AddressPart NVARCHAR(50) = 'a'

-- Build dynamic SQL
SET @SQLExec = 'SELECT * FROM Person.Address WHERE AddressLine1 LIKE ''%' + @AddressPart + '%'''

-- Execute dynamic SQL 
EXEC (@SQLExec)

ध्यान दें कि स्ट्रिंग कॉन्सटेनेशन द्वारा निर्मित क्वेरीज़ SQL इंजेक्शन भेद्यताएँ दे सकती हैं। मैं दृढ़ता से सलाह दूंगा कि आप इस विषय से परिचित हों। यदि आप इस तरह के विकास आर्किटेक्चर का उपयोग करने की योजना बना रहे हैं, विशेष रूप से सार्वजनिक-सामना करने वाले वेब एप्लिकेशन में, यह उपयोगी से अधिक होगा।

इसके बाद, हमें हैंडल स्ट्रिंग कॉन्सटेनेशन में NULL मान हैं . उदाहरण के लिए, पिछले उदाहरण से @AddressPart आवृत्ति चर इस मान को पारित करने पर संपूर्ण SQL कथन को अमान्य कर सकता है।

इस संभावित समस्या से निपटने का सबसे आसान तरीका एक वैध SQL कथन बनाने के लिए ISNULL फ़ंक्शन का उपयोग करना है :

SET @SQLExec = 'SELECT * FROM Person.Address WHERE AddressLine1 LIKE ''%' + ISNULL(@AddressPart, ‘ ‘) + '%'''


जरूरी! EXEC कमांड को कैश्ड निष्पादन योजनाओं का पुन:उपयोग करने के लिए डिज़ाइन नहीं किया गया है! यह प्रत्येक निष्पादन के लिए एक नया बनाएगा।

इसे प्रदर्शित करने के लिए, हम एक ही क्वेरी को दो बार निष्पादित करेंगे, लेकिन इनपुट पैरामीटर के एक अलग मान के साथ। फिर, हम दोनों मामलों में निष्पादन योजनाओं की तुलना करते हैं:

USE AdventureWorks2019

-- Case 1
DECLARE @SQLExec NVARCHAR(4000) 
DECLARE @AddressPart NVARCHAR(50) = 'a'
 
SET @SQLExec = 'SELECT * FROM Person.Address WHERE AddressLine1 LIKE ''%' + @AddressPart + '%'''

EXEC (@SQLExec)

-- Case 2
SET @AddressPart = 'b'
 
SET @SQLExec = 'SELECT * FROM Person.Address WHERE AddressLine1 LIKE ''%' + @AddressPart + '%'''

EXEC (@SQLExec)

-- Compare plans
SELECT chdpln.objtype
,      chdpln.cacheobjtype
,      chdpln.usecounts
,      sqltxt.text
  FROM sys.dm_exec_cached_plans as chdpln
       CROSS APPLY sys.dm_exec_sql_text(chdpln.plan_handle) as sqltxt
 WHERE sqltxt.text LIKE 'SELECT *%';

विस्तारित प्रक्रिया का उपयोग करना sp_executesql

इस प्रक्रिया का उपयोग करने के लिए, हमें इसे एक SQL कथन, इसमें प्रयुक्त मापदंडों की परिभाषा और उनके मान देने की आवश्यकता है। सिंटैक्स निम्न है:

sp_executesql @SQLStatement, N'@ParamNameDataType' , @Parameter1 = 'Value1'

आइए एक सरल उदाहरण से शुरू करते हैं जो दिखाता है कि किसी कथन और पैरामीटर को कैसे पास किया जाए:

EXECUTE sp_executesql  
               N'SELECT *  
                     FROM Person.Address
	       WHERE AddressLine1 LIKE ''%'' + @AddressPart + ''%''', -- SQL Statement
              N'@AddressPart NVARCHAR(50)',  -- Parameter definition
             @AddressPart = 'a';  -- Parameter value

EXEC कमांड के विपरीत, sp_executesql विस्तारित संग्रहीत प्रक्रिया निष्पादन योजनाओं का पुन:उपयोग करती है यदि एक ही कथन के साथ निष्पादित की जाती है लेकिन विभिन्न पैरामीटर। इसलिए, sp_executesql का उपयोग करना बेहतर है EXEC . से अधिक आदेश :

EXECUTE sp_executesql  
               N'SELECT *  
                     FROM Person.Address
	       WHERE AddressLine1 LIKE ''%'' + @AddressPart + ''%''', -- SQL Statement
              N'@AddressPart NVARCHAR(50)',  -- Parameter definition
             @AddressPart = 'a';  -- Parameter value

EXECUTE sp_executesql  
               N'SELECT *  
                     FROM Person.Address
	       WHERE AddressLine1 LIKE ''%'' + @AddressPart + ''%''', -- SQL Statement
              N'@AddressPart NVARCHAR(50)',  -- Parameter definition
             @AddressPart = 'b';  -- Parameter value

SELECT chdpln.objtype
,      chdpln.cacheobjtype
,      chdpln.usecounts
,      sqltxt.text
  FROM sys.dm_exec_cached_plans as chdpln
       CROSS APPLY sys.dm_exec_sql_text(chdpln.plan_handle) as sqltxt
  WHERE sqltxt.text LIKE '%Person.Address%';

संग्रहीत कार्यविधियों में गतिशील SQL

अब तक हम स्क्रिप्ट में डायनेमिक SQL का इस्तेमाल करते थे। हालांकि, वास्तविक लाभ तब स्पष्ट हो जाते हैं जब हम कस्टम प्रोग्रामिंग ऑब्जेक्ट्स - उपयोगकर्ता संग्रहीत प्रक्रियाओं में इन निर्माणों को निष्पादित करते हैं।

आइए एक प्रक्रिया बनाएं जो विभिन्न इनपुट प्रक्रिया पैरामीटर मानों के आधार पर एडवेंचरवर्क्स डेटाबेस में एक व्यक्ति की तलाश करेगी। उपयोगकर्ता इनपुट से, हम गतिशील SQL कमांड का निर्माण करेंगे और इसे कॉल करने वाले उपयोगकर्ता एप्लिकेशन को परिणाम वापस करने के लिए निष्पादित करेंगे:

CREATE OR ALTER PROCEDURE [dbo].[test_dynSQL]  
(
  @FirstName		 NVARCHAR(100) = NULL	
 ,@MiddleName        NVARCHAR(100) = NULL	
 ,@LastName			 NVARCHAR(100) = NULL	
)
AS          
BEGIN      
SET NOCOUNT ON;  
 
DECLARE @SQLExec    	NVARCHAR(MAX)
DECLARE @Parameters		NVARCHAR(500)
 
SET @Parameters = '@FirstName NVARCHAR(100),
  		            @MiddleName NVARCHAR(100),
			@LastName NVARCHAR(100)
			'
 
SET @SQLExec = 'SELECT *
	 	           FROM Person.Person
		         WHERE 1 = 1
		        ' 
IF @FirstName IS NOT NULL AND LEN(@FirstName) > 0 
   SET @SQLExec = @SQLExec + ' AND FirstName LIKE ''%'' + @FirstName + ''%'' '

IF @MiddleName IS NOT NULL AND LEN(@MiddleName) > 0 
                SET @SQLExec = @SQLExec + ' AND MiddleName LIKE ''%'' 
                                                                    + @MiddleName + ''%'' '

IF @LastName IS NOT NULL AND LEN(@LastName) > 0 
 SET @SQLExec = @SQLExec + ' AND LastName LIKE ''%'' + @LastName + ''%'' '

EXEC sp_Executesql @SQLExec
	         ,             @Parameters
 , @[email protected],  @[email protected],  
                                                @[email protected]
 
END 
GO

EXEC [dbo].[test_dynSQL] 'Ke', NULL, NULL

sp_executesql में OUTPUT पैरामीटर

हम उपयोग कर सकते हैं sp_executesql चयन कथन द्वारा लौटाए गए मान को सहेजने के लिए OUTPUT पैरामीटर के साथ। जैसा कि नीचे दिए गए उदाहरण में दिखाया गया है, यह क्वेरी द्वारा आउटपुट वेरिएबल @Output:

को लौटाई गई पंक्तियों की संख्या प्रदान करता है।
DECLARE @Output INT

EXECUTE sp_executesql  
        N'SELECT @Output = COUNT(*)
            FROM Person.Address
	       WHERE AddressLine1 LIKE ''%'' + @AddressPart + ''%''', -- SQL Statement
              N'@AddressPart NVARCHAR(50), @Output INT OUT',  -- Parameter definition
             @AddressPart = 'a', @Output = @Output OUT;  -- Parameters

SELECT @Output

sp_executesql प्रक्रिया के साथ SQL इंजेक्शन से सुरक्षा

SQL इंजेक्शन के जोखिम को महत्वपूर्ण रूप से कम करने के लिए आपको दो सरल गतिविधियाँ करनी चाहिए। सबसे पहले, कोष्ठक में तालिका के नाम संलग्न करें। दूसरा, कोड में जांचें कि क्या डेटाबेस में टेबल मौजूद हैं। ये दोनों तरीके नीचे दिए गए उदाहरण में मौजूद हैं।

हम एक सरल संग्रहीत कार्यविधि बना रहे हैं और इसे मान्य और अमान्य मापदंडों के साथ निष्पादित कर रहे हैं:

CREATE OR ALTER PROCEDURE [dbo].[test_dynSQL] 
(
  @InputTableName NVARCHAR(500)
)
AS 
BEGIN 
  DECLARE @AddressPart NVARCHAR(500)
  DECLARE @Output INT
  DECLARE @SQLExec NVARCHAR(1000) 

  IF EXISTS(SELECT 1 FROM sys.objects WHERE type = 'u' AND name = @InputTableName)
  BEGIN

      EXECUTE sp_executesql  
        N'SELECT @Output = COUNT(*)
            FROM Person.Address
	       WHERE AddressLine1 LIKE ''%'' + @AddressPart + ''%''', -- SQL Statement
              N'@AddressPart NVARCHAR(50), @Output INT OUT',  -- Parameter definition
             @AddressPart = 'a', @Output = @Output OUT;  -- Parameters

       SELECT @Output
  END
  ELSE
  BEGIN
     THROW 51000, 'Invalid table name given, possible SQL injection. Exiting procedure', 1 
  END
END


EXEC [dbo].[test_dynSQL] 'Person'
EXEC [dbo].[test_dynSQL] 'NoTable'

EXEC कमांड और sp_executesql संग्रहित प्रक्रिया की फ़ीचर तुलना

EXEC कमांड sp_executesql संग्रहीत कार्यविधि
कोई कैश योजना पुन:उपयोग नहीं कैश योजना का पुन:उपयोग
एसक्यूएल इंजेक्शन के लिए बहुत कमजोर एसक्यूएल इंजेक्शन के लिए बहुत कम असुरक्षित
कोई आउटपुट चर नहीं आउटपुट चर का समर्थन करता है
कोई पैरामीट्रिजेशन नहीं पैरामीट्रिजेशन का समर्थन करता है

निष्कर्ष

इस पोस्ट ने SQL सर्वर में डायनेमिक SQL कार्यक्षमता को लागू करने के दो तरीकों का प्रदर्शन किया। हमने सीखा है कि sp_executesql . का उपयोग करना बेहतर क्यों है उपलब्ध होने पर प्रक्रिया। साथ ही, हमने EXEC कमांड का उपयोग करने की विशिष्टता और SQL इंजेक्शन को रोकने के लिए उपयोगकर्ता इनपुट को साफ करने की मांगों को स्पष्ट किया है।

SQL सर्वर प्रबंधन स्टूडियो v18 (और उच्चतर) में संग्रहीत कार्यविधियों की सटीक और आरामदायक डिबगिंग के लिए, आप विशेष T-SQL डीबगर सुविधा का उपयोग कर सकते हैं, जो लोकप्रिय dbForge SQL पूर्ण समाधान का एक हिस्सा है।


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL सर्वर में एकाधिक पंक्तियों से टेक्स्ट को एक टेक्स्ट स्ट्रिंग में कैसे संयोजित करें

  2. SQL क्वेरी से चर कैसे सेट करें?

  3. SQL सर्वर में INNER JOIN का उपयोग करके मैं एकाधिक तालिकाओं से कैसे हटाऊं?

  4. SQL सर्वर 2005 में फ़ोन नंबर संग्रहीत करने के लिए किस डेटाटाइप का उपयोग किया जाना चाहिए?

  5. SQL सर्वर में "सर्वर डेटा एक्सेस के लिए कॉन्फ़िगर नहीं है" को कैसे ठीक करें