टी-एसक्यूएल संग्रहीत प्रक्रिया जो कई आईडी मानों को स्वीकार करती है


145

क्या एक संग्रहीत प्रक्रिया के लिए एक पैरामीटर के रूप में आईडी की सूची को पारित करने के लिए एक सुंदर तरीका है?

उदाहरण के लिए, मुझे अपने संग्रहीत कार्यविधि के द्वारा विभाग 1, 2, 5, 7, 20 वापस चाहिए। अतीत में, मैं आईडी के अल्पविराम सीमांकित सूची में पारित कर चुका हूं, नीचे दिए गए कोड की तरह, लेकिन यह वास्तव में गंदा महसूस कर रहा है।

SQL सर्वर 2005 मेरी एकमात्र लागू सीमा है जो मुझे लगता है।

create procedure getDepartments
  @DepartmentIds varchar(max)
as
  declare @Sql varchar(max)     
  select @Sql = 'select [Name] from Department where DepartmentId in (' + @DepartmentIds + ')'
  exec(@Sql)

यहाँ एक्सएमएल विधि का एक संस्करण है जो मुझे अभी मिला है।
जेसन

5
यदि आप SQL Server 2008 पर हैं, तो आप तालिका-मान्य पैरामीटर का उपयोग कर सकते हैं। http://www.sqlteam.com/article/sql-server-2008-table-valued-parameters
इयान नेल्सन

यह मेरे लिए उपयोगी था: sqlmag.com/t-sql/passing-multivalued-variables-stored-procedure
Zameer

जवाबों:


237

Erland Sommarskog ने पिछले 16 वर्षों से इस प्रश्न के आधिकारिक उत्तर को बनाए रखा है: SQL सर्वर में Arrays और Lists

किसी सरणी या सूची को क्वेरी में पास करने के लिए कम से कम एक दर्जन तरीके हैं; प्रत्येक के पास अपने विशिष्ट पेशेवरों और विपक्ष हैं।

मैं वास्तव में इन सभी विकल्पों में से ट्रेडऑफ के बारे में जानने के लिए लेख को पढ़ने के लिए पर्याप्त अनुशंसा नहीं कर सकता ।


11

हाँ, आपका वर्तमान समाधान SQL इंजेक्शन हमलों से ग्रस्त है।

सबसे अच्छा समाधान जो मैंने पाया है वह एक फ़ंक्शन का उपयोग करना है जो पाठ को शब्दों में विभाजित करता है (यहां कुछ पोस्ट किए गए हैं, या आप इसे मेरे ब्लॉग से उपयोग कर सकते हैं ) और फिर उसे अपनी तालिका में शामिल करें। कुछ इस तरह:

SELECT d.[Name]
FROM Department d
    JOIN dbo.SplitWords(@DepartmentIds) w ON w.Value = d.DepartmentId

14
मुझे यकीन नहीं है कि यह "SQL इंजेक्शन के हमलों का खतरा है" जब तक संग्रहित खरीद सीधे अविश्वसनीय ग्राहकों से कॉल करने योग्य नहीं होती है, जिस स्थिति में आपको बड़ी समस्याएं हैं। सर्विस लेयर कोड को @DepboxIds स्ट्रिंग को जोरदार टाइप किए गए डेटा (जैसे int [] विभाग में) से उत्पन्न करना चाहिए, जिस स्थिति में आप ठीक होंगे।
एंथनी

बहुत बढ़िया समाधान, @Matt हैमिल्टन। पता है कि क्या यह किसी की मदद करेगा, लेकिन मुझे SQL सर्वर 2008r पर अधिक सटीक परिणाम मिले जब मैं "ज्वाइन dbo.SplitWords (@MyParameterArray) p पर CHARINDEX (pvalue, d.MyFieldToSearch)> 0" का उपयोग करके पाठ फ़ील्ड खोज रहा था
डार्कलोकी

3

यदि आप मानों के साथ काम करने जा रहे हैं तो एक विधि जिस पर आप विचार कर सकते हैं, वह यह है कि उन्हें पहले एक अस्थायी तालिका में लिखा जाए। फिर आप सामान्य की तरह ही इसमें शामिल होते हैं।

इस तरह, आप केवल एक बार पार्स कर रहे हैं।

'स्प्लिट' यूडीएफ में से एक का उपयोग करना सबसे आसान है, लेकिन इतने सारे लोगों ने उन लोगों के उदाहरण पोस्ट किए हैं, मुझे लगा कि मैं एक अलग मार्ग पर जाऊंगा;)

यह उदाहरण आपके (#tmpDept) में शामिल होने और आपके द्वारा पास किए गए विभाग आईडी के साथ भरने के लिए एक अस्थायी तालिका बनाएगा। मैं मान रहा हूं कि आप उन्हें अल्पविराम से अलग कर रहे हैं, लेकिन आप निश्चित रूप से बदल सकते हैं - जो आप चाहते हैं।

IF OBJECT_ID('tempdb..#tmpDept', 'U') IS NOT NULL
BEGIN
    DROP TABLE #tmpDept
END

SET @DepartmentIDs=REPLACE(@DepartmentIDs,' ','')

CREATE TABLE #tmpDept (DeptID INT)
DECLARE @DeptID INT
IF IsNumeric(@DepartmentIDs)=1
BEGIN
    SET @DeptID=@DepartmentIDs
    INSERT INTO #tmpDept (DeptID) SELECT @DeptID
END
ELSE
BEGIN
        WHILE CHARINDEX(',',@DepartmentIDs)>0
        BEGIN
            SET @DeptID=LEFT(@DepartmentIDs,CHARINDEX(',',@DepartmentIDs)-1)
            SET @DepartmentIDs=RIGHT(@DepartmentIDs,LEN(@DepartmentIDs)-CHARINDEX(',',@DepartmentIDs))
            INSERT INTO #tmpDept (DeptID) SELECT @DeptID
        END
END

यह आपको एक विभाग की आईडी, कई आईडी के बीच में कॉमा के साथ, या यहां तक ​​कि कई आईडी के कॉमा और उनके बीच रिक्त स्थान से गुजरने की अनुमति देगा।

तो अगर आपने कुछ ऐसा किया है:

SELECT Dept.Name 
FROM Departments 
JOIN #tmpDept ON Departments.DepartmentID=#tmpDept.DeptID
ORDER BY Dept.Name

आपको उन सभी डिपार्टमेंट आईडी के नाम दिखाई देंगे, जिन्हें आपने पास किया था ...

फिर से, अस्थायी तालिका को आबाद करने के लिए एक फ़ंक्शन का उपयोग करके इसे सरल बनाया जा सकता है ... मैंने मुख्य रूप से इसे बिना किसी ऊब को मारने के लिए एक के बिना किया था :-P

- केविन फेयरचाइल्ड


3

आप XML का उपयोग कर सकते हैं।

उदाहरण के लिए

declare @xmlstring as  varchar(100) 
set @xmlstring = '<args><arg value="42" /><arg2>-1</arg2></args>' 

declare @docid int 

exec sp_xml_preparedocument @docid output, @xmlstring

select  [id],parentid,nodetype,localname,[text]
from    openxml(@docid, '/args', 1) 

कमांड sp_xml_preparedocument में बनाया गया है।

यह उत्पादन का उत्पादन करेगा:

id  parentid    nodetype    localname   text
0   NULL        1           args        NULL
2   0           1           arg         NULL
3   2           2           value       NULL
5   3           3           #text       42
4   0           1           arg2        NULL
6   4           3           #text       -1

जिसके पास आपकी आवश्यकता के सभी (अधिक) हैं।


2

एक सुपरफास्ट XML विधि, यदि आप एक संग्रहीत प्रक्रिया का उपयोग करना चाहते हैं और विभाग आईडी की अल्पविराम से अलग की गई सूची को पास करना चाहते हैं:

Declare @XMLList xml
SET @XMLList=cast('<i>'+replace(@DepartmentIDs,',','</i><i>')+'</i>' as xml)
SELECT x.i.value('.','varchar(5)') from @XMLList.nodes('i') x(i))

सारा श्रेय गुरु ब्रैड शुल्ज के ब्लॉग को जाता है


-3

इसको आजमाओ:

@list_of_params varchar(20) -- value 1, 2, 5, 7, 20 

SELECT d.[Name]
FROM Department d
where @list_of_params like ('%'+ CONVERT(VARCHAR(10),d.Id)  +'%')

बहुत आसान।


1
बहुत सरल - और बहुत गलत। लेकिन यहां तक ​​कि अगर आप अपने कोड में समस्या को ठीक करेंगे तो यह बहुत धीमा होगा। विवरण के लिए स्वीकृत उत्तर में "वास्तव में धीमी विधियाँ" लिंक देखें।
सेबेस्टियन माइन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.