टी-एसक्यूएल संग्रहीत प्रक्रिया में मैं वैकल्पिक मापदंडों का उपयोग कैसे कर सकता हूं?


185

मैं एक तालिका के माध्यम से खोज करने के लिए एक संग्रहीत प्रक्रिया बना रहा हूं। मेरे पास कई अलग-अलग खोज क्षेत्र हैं, जिनमें से सभी वैकल्पिक हैं। क्या एक संग्रहीत प्रक्रिया बनाने का एक तरीका है जो इसे संभाल लेगा? मान लीजिए कि मेरे पास चार फ़ील्ड्स हैं: ID, FirstName, LastName और Title। मैं ऐसा कुछ कर सकता था:

CREATE PROCEDURE spDoSearch
    @FirstName varchar(25) = null,
    @LastName varchar(25) = null,
    @Title varchar(25) = null
AS
    BEGIN
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
            FirstName = ISNULL(@FirstName, FirstName) AND
            LastName = ISNULL(@LastName, LastName) AND
            Title = ISNULL(@Title, Title)
    END

इस तरह का काम करता है। हालाँकि यह उन रिकॉर्ड्स को नज़रअंदाज़ करता है जहाँ FirstName, LastName या Title NULL हैं। यदि शीर्षक खोज मापदंडों में निर्दिष्ट नहीं है, तो मैं उन रिकॉर्ड्स को शामिल करना चाहता हूं जहां शीर्षक NULL - FirstName और LastName के लिए समान है। मुझे पता है कि मैं शायद गतिशील एसक्यूएल के साथ ऐसा कर सकता हूं लेकिन मैं इससे बचना चाहूंगा।


यहाँ एक नज़र है: stackoverflow.com/questions/11396919/…
मारियो Eis

2
निम्न कथन का अनुसरण करने का प्रयास करें: codeISNULL (FirstName, ') = ISNULL (@FirstName,' ') - यह प्रत्येक NULL को एक रिक्त स्ट्रिंग बना देगा और जिनकी तुलना eq के माध्यम से की जा सकती है। ऑपरेटर। यदि आप सभी शीर्षक प्राप्त करना चाहते हैं यदि इनपुट पैरामीटर शून्य है, तो कुछ ऐसा प्रयास करें: codeFirstName = @FirstName OR @FirstName IS NULL।
baHI

जवाबों:


257

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

इसे पढ़ें और सभी तरीकों पर विचार करें। आपकी सर्वोत्तम विधि आपके मापदंडों, आपके डेटा, आपके स्कीमा और आपके वास्तविक उपयोग पर निर्भर करेगी:

Erland Sommarskog द्वारा टी-एसक्यूएल में गतिशील खोज स्थितियां

एर्लैंड सोमरस्कोग द्वारा डायनेमिक एसक्यूएल का अभिशाप और आशीर्वाद

यदि आपके पास उचित SQL Server 2008 संस्करण (SQL 2008 SP1 CU5 (10.0.2746) और बाद में) है, तो आप वास्तव में एक इंडेक्स का उपयोग करने के लिए इस छोटी चाल का उपयोग कर सकते हैं:

OPTION (RECOMPILE)अपनी क्वेरी में जोड़ें , एरलैंड के लेख को देखें , और SQL सर्वर स्थानीय चर के रनटाइम मानों के आधार पर क्वेरी प्लान बनाने ORसे (@LastName IS NULL OR LastName= @LastName)पहले से भीतर से हल करेगा , और एक सूचकांक का उपयोग किया जा सकता है।

यह किसी भी SQL सर्वर संस्करण (उचित परिणाम लौटाएं) के लिए काम करेगा, लेकिन यदि आप SQL 2008 SP1 CU5 (10.0.2746) और बाद में हैं, तो केवल OPTION (RECOMPILE) को शामिल करें। विकल्प (रीकैपाइल) आपकी क्वेरी को फिर से जोड़ देगा, केवल सूचीबद्ध संस्करण इसे स्थानीय चर के वर्तमान समय के मूल्यों के आधार पर फिर से जोड़ देगा, जो आपको सबसे अच्छा प्रदर्शन देगा। यदि SQL Server 2008 के उस संस्करण पर नहीं है, तो बस उस लाइन को छोड़ दें।

CREATE PROCEDURE spDoSearch
    @FirstName varchar(25) = null,
    @LastName varchar(25) = null,
    @Title varchar(25) = null
AS
    BEGIN
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
                (@FirstName IS NULL OR (FirstName = @FirstName))
            AND (@LastName  IS NULL OR (LastName  = @LastName ))
            AND (@Title     IS NULL OR (Title     = @Title    ))
        OPTION (RECOMPILE) ---<<<<use if on for SQL 2008 SP1 CU5 (10.0.2746) and later
    END

15
और / या पूर्वता से सावधान रहें। और इस पर पूर्ववर्ती स्थिति है, इसलिए उचित कोष्ठक के बिना यह उदाहरण अपेक्षित परिणाम नहीं देगा ... तो यह पढ़ें @LastName)) और (@TitleIS पूरा या (शीर्षक = @ शीर्षक))
Bliek

... (@FirstName IS NULL OR (FirstName = @FirstName) होना चाहिए ... (FirstName = Coalesce (@ firstname, FirstName))
fcm

कोष्ठक मत भूलना, अन्यथा यह काम नहीं करेगा।
पाब्लो कार्रास्को हर्नांडेज़

27

@ केएम का उत्तर जहां तक ​​जाता है, अच्छा है, लेकिन सलाह के अपने शुरुआती बिट्स पर पूरी तरह से विफल रहता है;

..., कॉम्पैक्ट कोड को अनदेखा करें, कोड को दोहराने के बारे में चिंता करने पर ध्यान न दें ...

यदि आप सर्वश्रेष्ठ प्रदर्शन प्राप्त करना चाहते हैं तो आपको वैकल्पिक मानदंडों के प्रत्येक संभावित संयोजन के लिए एक bespoke क्वेरी लिखनी चाहिए। यह चरम लग सकता है, और यदि आपके पास बहुत सारे वैकल्पिक मानदंड हैं तो यह हो सकता है, लेकिन प्रदर्शन अक्सर प्रयास और परिणाम के बीच एक व्यापार-बंद है। व्यवहार में, पैरामीटर संयोजनों का एक सामान्य सेट हो सकता है जिसे बीस्पोक प्रश्नों के साथ लक्षित किया जा सकता है, फिर अन्य सभी संयोजनों के लिए एक सामान्य क्वेरी (अन्य उत्तरों के अनुसार)।

CREATE PROCEDURE spDoSearch
    @FirstName varchar(25) = null,
    @LastName varchar(25) = null,
    @Title varchar(25) = null
AS
BEGIN

    IF (@FirstName IS NOT NULL AND @LastName IS NULL AND @Title IS NULL)
        -- Search by first name only
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
            FirstName = @FirstName

    ELSE IF (@FirstName IS NULL AND @LastName IS NOT NULL AND @Title IS NULL)
        -- Search by last name only
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
            LastName = @LastName

    ELSE IF (@FirstName IS NULL AND @LastName IS NULL AND @Title IS NOT NULL)
        -- Search by title only
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
            Title = @Title

    ELSE IF (@FirstName IS NOT NULL AND @LastName IS NOT NULL AND @Title IS NULL)
        -- Search by first and last name
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
            FirstName = @FirstName
            AND LastName = @LastName

    ELSE
        -- Search by any other combination
        SELECT ID, FirstName, LastName, Title
        FROM tblUsers
        WHERE
                (@FirstName IS NULL OR (FirstName = @FirstName))
            AND (@LastName  IS NULL OR (LastName  = @LastName ))
            AND (@Title     IS NULL OR (Title     = @Title    ))

END

इस दृष्टिकोण का लाभ यह है कि bespoke क्वेरी द्वारा संभाले गए सामान्य मामलों में क्वेरी उतनी ही कुशल है, जितनी कि यह हो सकता है - बिना किसी मापदंड के कोई प्रभाव नहीं पड़ता है। इसके अलावा, अनुक्रमणिका और अन्य प्रदर्शन संवर्द्धन को सभी संभावित स्थितियों को संतुष्ट करने के बजाय विशिष्ट bespoke प्रश्नों पर लक्षित किया जा सकता है।


निश्चित रूप से प्रत्येक मामले के लिए एक अलग संग्रहीत प्रक्रिया लिखना बेहतर होगा। फिर स्पूफिंग और पुनर्संयोजन के बारे में चिंता न करें।
जोडरेल

5
यह कहे बिना जाना चाहिए कि यह दृष्टिकोण जल्दी से एक बुरा सपना बन जाता है।
एटारियो

3
@Atario रखरखाव बनाम प्रदर्शन में आसानी एक आम व्यापार बंद है, यह जवाब प्रदर्शन की दिशा में सक्षम है।
Rhys जोन्स

26

आप निम्नलिखित मामले में कर सकते हैं,

CREATE PROCEDURE spDoSearch
   @FirstName varchar(25) = null,
   @LastName varchar(25) = null,
   @Title varchar(25) = null
AS
  BEGIN
      SELECT ID, FirstName, LastName, Title
      FROM tblUsers
      WHERE
        (@FirstName IS NULL OR FirstName = @FirstName) AND
        (@LastNameName IS NULL OR LastName = @LastName) AND
        (@Title IS NULL OR Title = @Title)
END

हालांकि डेटा पर निर्भर कभी-कभी बेहतर गतिशील क्वेरी बनाते हैं और उन्हें निष्पादित करते हैं।


10

पार्टी में पांच साल की देरी।

यह स्वीकार किए गए उत्तर के दिए गए लिंक में उल्लिखित है, लेकिन मुझे लगता है कि यह SO पर एक स्पष्ट उत्तर के हकदार है - प्रदान किए गए मापदंडों के आधार पर गतिशील रूप से क्वेरी का निर्माण। उदाहरण के लिए:

सेट अप

-- drop table Person
create table Person
(
    PersonId INT NOT NULL IDENTITY(1, 1) CONSTRAINT PK_Person PRIMARY KEY,
    FirstName NVARCHAR(64) NOT NULL,
    LastName NVARCHAR(64) NOT NULL,
    Title NVARCHAR(64) NULL
)
GO

INSERT INTO Person (FirstName, LastName, Title)
VALUES ('Dick', 'Ormsby', 'Mr'), ('Serena', 'Kroeger', 'Ms'), 
    ('Marina', 'Losoya', 'Mrs'), ('Shakita', 'Grate', 'Ms'), 
    ('Bethann', 'Zellner', 'Ms'), ('Dexter', 'Shaw', 'Mr'),
    ('Zona', 'Halligan', 'Ms'), ('Fiona', 'Cassity', 'Ms'),
    ('Sherron', 'Janowski', 'Ms'), ('Melinda', 'Cormier', 'Ms')
GO

प्रक्रिया

ALTER PROCEDURE spDoSearch
    @FirstName varchar(64) = null,
    @LastName varchar(64) = null,
    @Title varchar(64) = null,
    @TopCount INT = 100
AS
BEGIN
    DECLARE @SQL NVARCHAR(4000) = '
        SELECT TOP ' + CAST(@TopCount AS VARCHAR) + ' *
        FROM Person
        WHERE 1 = 1'

    PRINT @SQL

    IF (@FirstName IS NOT NULL) SET @SQL = @SQL + ' AND FirstName = @FirstName'
    IF (@LastName IS NOT NULL) SET @SQL = @SQL + ' AND FirstName = @LastName'
    IF (@Title IS NOT NULL) SET @SQL = @SQL + ' AND Title = @Title'

    EXEC sp_executesql @SQL, N'@TopCount INT, @FirstName varchar(25), @LastName varchar(25), @Title varchar(64)', 
         @TopCount, @FirstName, @LastName, @Title
END
GO

प्रयोग

exec spDoSearch @TopCount = 3
exec spDoSearch @FirstName = 'Dick'

पेशेवरों:

  • लिखना और समझना आसान है
  • लचीलापन - आसानी से ट्रिकियर फ़िल्टरिंग (जैसे गतिशील टॉप) के लिए क्वेरी उत्पन्न करें

विपक्ष:

  • उपलब्ध मापदंडों, अनुक्रमित और डेटा की मात्रा के आधार पर संभावित प्रदर्शन समस्याएं

सीधे जवाब नहीं, लेकिन समस्या उर्फ ​​बड़ी तस्वीर से संबंधित है

आमतौर पर, ये फ़िल्टरिंग संग्रहीत कार्यविधियाँ आसपास नहीं तैरती हैं, लेकिन कुछ सेवा स्तर से कॉल की जा रही हैं। यह SQL से सर्विस लेयर तक बिजनेस लॉजिक (फ़िल्टरिंग) को स्थानांतरित करने का विकल्प छोड़ देता है।

एक उदाहरण प्रदान की गई फिल्टर के आधार पर क्वेरी उत्पन्न करने के लिए LINQ2SQL का उपयोग कर रहा है:

    public IList<SomeServiceModel> GetServiceModels(CustomFilter filters)
    {
        var query = DataAccess.SomeRepository.AllNoTracking;

        // partial and insensitive search 
        if (!string.IsNullOrWhiteSpace(filters.SomeName))
            query = query.Where(item => item.SomeName.IndexOf(filters.SomeName, StringComparison.OrdinalIgnoreCase) != -1);
        // filter by multiple selection
        if ((filters.CreatedByList?.Count ?? 0) > 0)
            query = query.Where(item => filters.CreatedByList.Contains(item.CreatedById));
        if (filters.EnabledOnly)
            query = query.Where(item => item.IsEnabled);

        var modelList = query.ToList();
        var serviceModelList = MappingService.MapEx<SomeDataModel, SomeServiceModel>(modelList);
        return serviceModelList;
    }

पेशेवरों:

  • प्रदान किए गए फ़िल्टर के आधार पर गतिशील रूप से उत्पन्न क्वेरी। कोई पैरामीटर सूँघने या recompile संकेत की जरूरत नहीं है
  • OOP दुनिया में उन लोगों के लिए लिखने के लिए कुछ आसान है
  • आम तौर पर अनुकूल प्रदर्शन, चूंकि "सरल" प्रश्नों को जारी किया जाएगा (उपयुक्त अनुक्रमित अभी भी हालांकि आवश्यक हैं)

विपक्ष:

  • LINQ2QL सीमाएँ तक पहुँचा जा सकता है और LINQ2Objects के लिए एक डाउनग्रेड मजबूर कर रहा है या मामले के आधार पर शुद्ध SQL समाधान पर वापस जा रहा है
  • LINQ के लापरवाह लेखन से भयानक प्रश्न उत्पन्न हो सकते हैं (या कई प्रश्न, यदि नेविगेशन गुण लोड हैं)

1
सुनिश्चित करें कि आपके सभी मध्यवर्ती स्ट्रिंग्स N '' के बजाय '' हैं - यदि आपकी SQL 8000 वर्णों से अधिक है तो आप ट्रंकेशन समस्याओं में चलेंगे।
एलन सिंगफील्ड

1
साथ ही, आपको संग्रहीत कार्यविधि पर "EXECUTE AS OWNER" खंड के साथ रखने की आवश्यकता हो सकती है, यदि आपने उपयोगकर्ता के लिए प्रत्यक्ष चयन की अनुमति से इनकार कर दिया है। यदि आप इस खंड का उपयोग करते हैं, तो SQL इंजेक्शन से बचने के लिए वास्तव में सावधान रहें।
एलन सिंगफील्ड

8

अपनी WHEREस्थिति बढ़ाएँ :

WHERE
    (FirstName = ISNULL(@FirstName, FirstName)
    OR COALESCE(@FirstName, FirstName, '') = '')
AND (LastName = ISNULL(@LastName, LastName)
    OR COALESCE(@LastName, LastName, '') = '')
AND (Title = ISNULL(@Title, Title)
    OR COALESCE(@Title, Title, '') = '')

यानी बूलियन स्थितियों के साथ विभिन्न मामलों को मिलाएं।


-3

यह भी काम करता है:

    ...
    WHERE
        (FirstName IS NULL OR FirstName = ISNULL(@FirstName, FirstName)) AND
        (LastName IS NULL OR LastName = ISNULL(@LastName, LastName)) AND
        (Title IS NULL OR Title = ISNULL(@Title, Title))
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.