SQL सर्वर सम्मिलित यदि सर्वोत्तम अभ्यास मौजूद नहीं है


152

मेरे पास एक Competitionsपरिणाम तालिका है जो एक तरफ टीम के सदस्य के नाम और उनकी रैंकिंग रखती है ।

दूसरी ओर मुझे अद्वितीय प्रतियोगियों के नामों की एक तालिका बनाए रखने की आवश्यकता है :

CREATE TABLE Competitors (cName nvarchar(64) primary key)

अब मेरे पास प्रथम तालिका में कुछ २००,००० परिणाम हैं और जब प्रतियोगी तालिका खाली होती है तो मैं यह कर सकता हूं:

INSERT INTO Competitors SELECT DISTINCT Name FROM CompResults

और क्वेरी को केवल 11,000 नामों को सम्मिलित करने के लिए कुछ 5 सेकंड लगते हैं।

अब तक यह एक महत्वपूर्ण आवेदन नहीं है, इसलिए मैं महीने में एक बार प्रतियोगी तालिका को काट सकता हूं , जब मुझे कुछ 10,000 पंक्तियों के साथ नई प्रतियोगिता के परिणाम प्राप्त होंगे।

लेकिन नए और मौजूदा प्रतियोगियों के साथ नए परिणाम जोड़े जाने पर सबसे अच्छा अभ्यास क्या है? मैं मौजूदा प्रतियोगी तालिका को छोटा नहीं करना चाहता

मुझे केवल नए प्रतियोगियों के लिए INSERT स्टेटमेंट करने की आवश्यकता है और यदि वे मौजूद हैं तो कुछ भी नहीं करें।


70
कृपया, एक कॉलम को अपना प्राथमिक (और इस प्रकार: क्लस्टरिंग) कुंजी बनाएं NVARCHAR(64)!! सबसे पहले - यह एक बहुत व्यापक कुंजी है - 128 बाइट तक; और दूसरा यह परिवर्तनशील आकार है - फिर से: इष्टतम नहीं है ... यह आपके लिए सबसे खराब विकल्प के बारे में है - आपका प्रदर्शन नरक होगा, और तालिका और सूचकांक विखंडन हर समय 99.9% होगा .....
marc_s

4
मार्क की एक अच्छी बात है। अपने पीके के रूप में नाम का उपयोग न करें। एक आईडी का उपयोग करें, अधिमानतः int या कुछ हल्का।
रीचर्ड

6
देखें किम्बर्ली ट्रिप के ब्लॉग पोस्ट क्या एक बनाता है पर अच्छा स्थिर, बढ़ती अद्वितीय, संकीर्ण,: क्लस्टरिंग कुंजी। आपकी cNameचार श्रेणियों में से तीन में विफल रहता है .... (यह संकीर्ण नहीं है, यह शायद स्थिर नहीं है, और यह निश्चित रूप से कभी नहीं बढ़ रहा है)
marc_s

मैं एक प्रतियोगी के नाम तालिका में INT प्राथमिक कुंजी को जोड़ने में बिंदु नहीं देख सकता हूं, जहां सभी प्रश्न नाम पर होंगे, जैसे '% XXXXX%' जैसे नाम 'इसलिए मुझे हमेशा नाम पर एक अद्वितीय सूचकांक की आवश्यकता होती है। लेकिन हाँ, मैं इस बिंदु को परिवर्तनशील लंबाई में नहीं देख सकता हूँ ..
डिडिएर लेवी

3
a) विखंडन से बचना और b) यदि यह अन्य तालिकाओं में विदेशी कुंजी है तो डुप्लिकेट किए गए डेटा नेक्सेसरी (जो कि गति का विचार है) से बड़ा है
JamesRyan

जवाबों:


214

शब्दशः आप पूछ रहे हैं "ऐसे प्रतियोगी सम्मिलित करें जहाँ पहले से मौजूद नहीं है":

INSERT Competitors (cName)
SELECT DISTINCT Name
FROM CompResults cr
WHERE
   NOT EXISTS (SELECT * FROM Competitors c
              WHERE cr.Name = c.cName)

2
खैर, यह वही है जो मैं SO पर विजय प्राप्त करने से पहले किया होगा। लेकिन मेरे विचार का मूल यह है: सप्ताह में एक बार स्क्रैच से नाम तालिका के पुनर्निर्माण के खिलाफ यह कितना अच्छा प्रदर्शन करेगा? (यह केवल कुछ ही सेकंड लगते हैं याद रखें)
डिडिएर लेवी

3
@ डिडिएर लेवी: दक्षता? जब आप केवल भिन्नताओं के साथ अद्यतन कर सकते हैं, तो क्यों काटें, पुन: बनाएँ। अर्थात: BEGIN TRAN DELETE COMPResults INSERT COMPResults .. COMMIT TRAN = अधिक काम।
gbn

@ जीबी - क्या आपके जवाब के बजाय सुरक्षित रूप से यहां-और तर्क का उपयोग करने का कोई तरीका है? मेरा एक संबंधित प्रश्न है। क्या आप कृपया मेरी मदद कर सकते हैं? stackoverflow.com/questions/21889843/…
स्टीम

53

एक अन्य विकल्प यह है कि आप अपने मौजूदा प्रतियोगियों की तालिका के साथ अपनी परिणाम तालिका में शामिल हों और अलग-अलग रिकॉर्डों को फ़िल्टर करके नए प्रतियोगियों को खोजें, जो इस मेल से मेल नहीं खाते:

INSERT Competitors (cName)
SELECT  DISTINCT cr.Name
FROM    CompResults cr left join
        Competitors c on cr.Name = c.cName
where   c.cName is null

नया वाक्यविन्यास मेरिज भी ऐसा करने के लिए एक कॉम्पैक्ट, सुरुचिपूर्ण और कुशल तरीका प्रदान करता है:

MERGE INTO Competitors AS Target
USING (SELECT DISTINCT Name FROM CompResults) AS Source ON Target.Name = Source.Name
WHEN NOT MATCHED THEN
    INSERT (Name) VALUES (Source.Name);

1
इस मामले में मर्ज कमाल का है, यह जैसा कहता है, वैसा ही करता है।
वोरोबाय 1326

मेरा निश्चित रूप से यह मानना ​​है कि एसक्यूएल सर्वर को उप क्वेरी दृष्टिकोण के विपरीत, अनुकूलन के लिए सर्वोत्तम संभव संकेत देते हुए जाने का यह सही तरीका है।
मैड नील्सन

4
MERGE स्टेटमेंट में अभी भी बहुत सारे मुद्दे हैं। बस Google "SQL मर्ज प्रॉब्लम्स" - कई ब्लॉगर्स ने इस पर चर्चा की है।
डेविड विल्सन

क्यों MERGE स्टेटमेंट में As Target है, लेकिन INSERT स्टेटमेंट में कोई टारगेट नहीं है? अधिक अंतर हैं जो समतुल्यता को पकड़ना मुश्किल बनाते हैं।
पीटर

32

पता नहीं क्यों किसी और ने यह अभी तक नहीं कहा है;

सामान्य बनाते हैं।

आपको एक तालिका मिली है जो मॉडल प्रतियोगिताओं को देती है? प्रतिस्पर्धाएँ प्रतियोगियों से बनती हैं? आपको एक या अधिक प्रतियोगिताओं में प्रतियोगियों की एक अलग सूची की आवश्यकता है ......

आपके पास निम्न तालिका होनी चाहिए .....

CREATE TABLE Competitor (
    [CompetitorID] INT IDENTITY(1,1) PRIMARY KEY
    , [CompetitorName] NVARCHAR(255)
    )

CREATE TABLE Competition (
    [CompetitionID] INT IDENTITY(1,1) PRIMARY KEY
    , [CompetitionName] NVARCHAR(255)
    )

CREATE TABLE CompetitionCompetitors (
    [CompetitionID] INT
    , [CompetitorID] INT
    , [Score] INT

    , PRIMARY KEY (
        [CompetitionID]
        , [CompetitorID]
        )
    )

CompetitionCompetitors.CompetitionID और अन्य तालिकाओं की ओर इशारा करते हुए CompetitorID पर बाधाओं के साथ।

इस तरह की तालिका संरचना के साथ - आपकी चाबियाँ सभी सरल हैं - इसमें एक अच्छा प्राकृतिक कुंजी नहीं लगता है जो मॉडल को फिट करेगा इसलिए मुझे लगता है कि यहां एक महत्वपूर्ण कुंजी एक अच्छा फिट है।

इसलिए यदि आपके पास यह था तो किसी विशेष प्रतियोगिता में प्रतियोगियों की विशिष्ट सूची प्राप्त करने के लिए आप इस तरह से एक क्वेरी जारी कर सकते हैं:

DECLARE @CompetitionName VARCHAR(50) SET @CompetitionName = 'London Marathon'

    SELECT
        p.[CompetitorName] AS [CompetitorName]
    FROM
        Competitor AS p
    WHERE
        EXISTS (
            SELECT 1
            FROM
                CompetitionCompetitor AS cc
                JOIN Competition AS c ON c.[ID] = cc.[CompetitionID]
            WHERE
                cc.[CompetitorID] = p.[CompetitorID]
                AND cc.[CompetitionName] = @CompetitionNAme
        )

और यदि आप चाहते हैं कि प्रत्येक प्रतियोगिता के लिए स्कोर एक प्रतियोगी में हो:

SELECT
    p.[CompetitorName]
    , c.[CompetitionName]
    , cc.[Score]
FROM
    Competitor AS p
    JOIN CompetitionCompetitor AS cc ON cc.[CompetitorID] = p.[CompetitorID]
    JOIN Competition AS c ON c.[ID] = cc.[CompetitionID]

और जब आपके पास नए प्रतियोगियों के साथ एक नई प्रतियोगिता होती है तो आप बस यह जाँचते हैं कि कौन से पहले से ही प्रतियोगी तालिका में मौजूद हैं। यदि वे पहले से मौजूद हैं, तो आप उन प्रतियोगियों के लिए प्रतियोगी में सम्मिलित नहीं होते हैं और नए के लिए सम्मिलित करते हैं।

फिर आप कॉम्पीटिशन में नया कॉम्पीटिशन डालते हैं और आखिर में आप सिर्फ कॉम्पिटिशन.कॉम में सभी लिंक बनाते हैं।


2
यह मानते हुए कि ओपी के पास एक कैश्ड परिणाम प्राप्त करने के लिए अपनी सभी तालिकाओं के पुनर्गठन के लिए इस समय उत्तोलन है। अपने डीबी और ऐप को फिर से लिखना, कुछ परिभाषित दायरे में समस्या को हल करने के बजाय, हर बार जब कुछ आसानी से घटता नहीं है, तो यह आपदा का एक नुस्खा है।
जेफरी वेस्ट

1
शायद मेरी तरह ओपी के मामले में, आपके पास डेटाबेस को संशोधित करने के लिए हमेशा पहुंच नहीं होती है .. और एक पुराने डेटाबेस को फिर से लिखना / सामान्य करना हमेशा बजट या आवंटित समय में नहीं होता है।
ईगलीन

10

आपको एक साथ तालिकाओं में शामिल होने और अद्वितीय प्रतियोगियों की एक सूची प्राप्त करने की आवश्यकता होगी जो पहले से मौजूद नहीं हैं Competitors

यह अनोखा रिकॉर्ड डालेगा।

INSERT Competitors (cName) 
SELECT DISTINCT Name
FROM CompResults cr LEFT JOIN Competitors c ON cr.Name = c.cName
WHERE c.Name IS NULL

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


4

ऊपर के उत्तर जो सामान्य करने की बात करते हैं, वे बहुत अच्छे हैं! लेकिन क्या होगा अगर आप खुद को मेरे जैसी स्थिति में पाते हैं जहाँ आपको डेटाबेस स्कीमा या संरचना को छूने की अनुमति नहीं है क्योंकि यह खड़ा है? जैसे, DBA के 'देवता' हैं और सभी सुझाए गए संशोधन / देव / अशक्त हैं?

उस संबंध में, मुझे ऐसा लगता है कि इस स्टैक ओवरफ्लो पोस्टिंग के साथ उत्तर दिया गया है, कोड के नमूने देने से ऊपर सभी उपयोगकर्ताओं के संबंध में।

मैं INSERT VALUES से कोड रीपोस्ट कर रहा हूँ, जहाँ से मैं किसी भी डेटाबेस डेटाबेस में कोई परिवर्तन नहीं कर सकता, जिसने मुझे सबसे अधिक मदद की है:

INSERT INTO #table1 (Id, guidd, TimeAdded, ExtraData)
SELECT Id, guidd, TimeAdded, ExtraData
FROM #table2
WHERE NOT EXISTS (Select Id, guidd From #table1 WHERE #table1.id = #table2.id)
-----------------------------------
MERGE #table1 as [Target]
USING  (select Id, guidd, TimeAdded, ExtraData from #table2) as [Source]
(id, guidd, TimeAdded, ExtraData)
    on [Target].id =[Source].id
WHEN NOT MATCHED THEN
    INSERT (id, guidd, TimeAdded, ExtraData)
    VALUES ([Source].id, [Source].guidd, [Source].TimeAdded, [Source].ExtraData);
------------------------------
INSERT INTO #table1 (id, guidd, TimeAdded, ExtraData)
SELECT id, guidd, TimeAdded, ExtraData from #table2
EXCEPT
SELECT id, guidd, TimeAdded, ExtraData from #table1
------------------------------
INSERT INTO #table1 (id, guidd, TimeAdded, ExtraData)
SELECT #table2.id, #table2.guidd, #table2.TimeAdded, #table2.ExtraData
FROM #table2
LEFT JOIN #table1 on #table1.id = #table2.id
WHERE #table1.id is null

उपरोक्त कोड आपके पास जो कुछ भी है उससे अलग क्षेत्रों का उपयोग करता है, लेकिन आपको विभिन्न तकनीकों के साथ सामान्य लाभ मिलता है।

ध्यान दें कि स्टैक ओवरफ्लो पर मूल उत्तर के अनुसार, इस कोड को यहां से कॉपी किया गया था ।

वैसे भी मेरी बात "बेस्ट प्रैक्टिस" है जो अक्सर आप क्या कर सकते हैं और सिद्धांत के रूप में अच्छी तरह से नहीं कर सकते हैं।

  • यदि आप सामान्य / अनुक्रमित / कुंजी उत्पन्न करने में सक्षम हैं - महान!
  • यदि नहीं और आप को मेरी तरह कोड हैक करने का सहारा है, तो उम्मीद है कि उपरोक्त मदद करता है।

सौभाग्य!


यदि यह स्पष्ट नहीं है, तो यह समस्या के लिए चार अलग-अलग दृष्टिकोण हैं, इसलिए एक को चुनें।
2

3

Transact चार्ली द्वारा सुझाए गए अनुसार अपने परिचालन तालिकाओं को सामान्य करना , एक अच्छा विचार है, और समय के साथ कई सिरदर्द और समस्याओं को बचाएगा - लेकिन इंटरफ़ेस तालिकाओं के रूप में ऐसी चीजें हैं , जो बाहरी प्रणालियों के साथ एकीकरण, और रिपोर्टिंग का समर्थन करती हैं। तालिकाएं, जो विश्लेषणात्मक जैसी चीजों का समर्थन करती हैं प्रसंस्करण; और उन प्रकार की तालिकाओं को सामान्य रूप से सामान्य नहीं किया जाना चाहिए - वास्तव में, बहुत बार यह बहुत अधिक सुविधाजनक होता है और उनके लिए नहीं होना चाहिए

इस मामले में, मुझे लगता है कि आपके ऑपरेशनल टेबल के लिए Transact चार्ली का प्रस्ताव एक अच्छा है।

लेकिन मैं एकीकरण तालिका (बाहरी स्रोतों से डेटा लोड करना) के प्रयोजनों के लिए CompetitorName पर कुशल जोड़ों का समर्थन करने के लिए प्रतियोगी तालिका में एक सूचकांक (जरूरी नहीं कि अद्वितीय) जोड़ दूंगा, और मैं मिश्रण में एक इंटरफ़ेस तालिका डालूँगा: CompetitionResults।

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

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

एक चीज जो मैं नोट करूंगा - वास्तव में, प्रतियोगी नाम, यह मुझे लगता है, आपके डेटा में अद्वितीय होने की बहुत संभावना नहीं है । उदाहरण के लिए, 200,000 प्रतियोगियों में, आपके पास 2 या अधिक डेविड स्मिथ हो सकते हैं। इसलिए मेरा सुझाव होगा कि आप प्रतियोगियों से अधिक जानकारी एकत्र करें, जैसे कि उनका फोन नंबर या ईमेल पता, या कुछ और जो अद्वितीय होने की अधिक संभावना है।

आपकी परिचालन तालिका, प्रतियोगी, प्रत्येक डेटा आइटम के लिए बस एक कॉलम होना चाहिए जो एक समग्र प्राकृतिक कुंजी में योगदान देता है; उदाहरण के लिए इसमें प्राथमिक ईमेल पते के लिए एक कॉलम होना चाहिए। लेकिन इंटरफ़ेस टेबल में एक प्राथमिक ईमेल पते के लिए पुराने और नए मूल्यों के लिए एक स्लॉट होना चाहिए , ताकि पुराने मूल्य का उपयोग प्रतियोगियों में रिकॉर्ड देखने के लिए किया जा सके और इसके नए हिस्से को नए मूल्य पर अपडेट किया जा सके।

तो CompetitionResults में कुछ "पुराने" और "नए" फ़ील्ड होने चाहिए - पुराने ईमेल, newEmail, oldPhone, newPhone, आदि। इस तरह आप CompetitorName, Email, और Phone से Competitors में एक कंपोजिट कुंजी बना सकते हैं।

फिर जब आपके पास कुछ प्रतियोगिता परिणाम होते हैं, तो आप अपनी एक्सेल शीट या जो कुछ भी आपके पास है, उससे अपने कॉम्पिटिशनResults टेबल को छोटा और फिर से लोड कर सकते हैं और सभी नए प्रतियोगियों को प्रतियोगी तालिका में सम्मिलित करने के लिए एक कुशल, सम्मिलित कर सकते हैं, और एकल, कुशल अद्यतन करने के लिए प्रतियोगिता प्रतियोगियों से मौजूदा प्रतियोगियों के बारे में सभी जानकारी। और आप CompetitionCompetitors तालिका में नई पंक्तियों को सम्मिलित करने के लिए एक एकल इंसर्ट कर सकते हैं। ये चीजें एक ProcessCompetitionResults संग्रहीत कार्यविधि में की जा सकती हैं, जिसे CompetitionResults तालिका लोड करने के बाद निष्पादित किया जा सकता है।

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

एक आखिरी टिप्पणी जो मैं करूंगा, वह एक है जो मैंने एसओ पर पहले की है: यदि आप एक विदेशी कुंजी बनाते हैं जो यह सुनिश्चित करती है कि प्रतियोगी तालिका में एक प्रतियोगी मौजूद है, तो इससे पहले कि आप उस प्रतियोगी के साथ एक पंक्ति जोड़ सकते हैं, प्रतिस्पर्धात्मकता में, सुनिश्चित करें कि विदेशी कुंजी को अपडेट और कैस्केड करने के लिए सेट किया गया है । इस तरह अगर आपको किसी प्रतियोगी को हटाने की आवश्यकता है, तो आप इसे कर सकते हैं और उस प्रतियोगी से जुड़ी सभी पंक्तियाँ अपने आप हट जाएँगी। अन्यथा, डिफ़ॉल्ट रूप से, विदेशी कुंजी के लिए आपको कॉम्पिटिशन.कॉम से सभी संबंधित पंक्तियों को हटाना होगा, इससे पहले कि वह आपको एक प्रतियोगी को हटा दे।

(कुछ लोगों को लगता है कि गैर-कैस्केडिंग विदेशी कुंजी एक अच्छा सुरक्षा एहतियात है, लेकिन मेरा अनुभव यह है कि वे बट में सिर्फ एक भयावह दर्द हैं जो एक ओवरसाइट के परिणामस्वरूप अधिक बार नहीं होते हैं और वे काम का एक गुच्छा बनाते हैं डीबीए के लिए। गलती से सामान हटाने वाले लोगों के साथ व्यवहार करना, इसीलिए आपके पास "आप सुनिश्चित हैं" जैसे संवाद और विभिन्न प्रकार के नियमित बैकअप और अनावश्यक डेटा स्रोत हैं। यह वास्तव में एक प्रतियोगी को नष्ट करना चाहता है, जिसका डेटा सभी चाहते हैं। उदाहरण के लिए गड़बड़, की तुलना में यह गलती से एक को हटाने के लिए है और फिर जाओ "ओह नहीं! मैं ऐसा करने का मतलब नहीं था! और अब मेरे पास उनकी प्रतियोगिता के परिणाम नहीं हैं! Aaaahh!" उत्तरार्द्ध निश्चित रूप से काफी आम है, इसलिए , आपको इसके लिए तैयार रहने की आवश्यकता है, लेकिन पूर्व कहीं अधिक सामान्य है,पूर्व, इमो के लिए तैयार करने का सबसे आसान और सबसे अच्छा तरीका है, बस विदेशी कुंजी कैस्केड अपडेट और डिलीट करना।)


1

ठीक है, यह 7 साल पहले पूछा गया था, लेकिन मुझे लगता है कि यहां सबसे अच्छा समाधान नई मेज को पूरी तरह से अलग करना है और बस एक कस्टम दृश्य के रूप में ऐसा करना है। इस तरह आप डेटा की नकल नहीं कर रहे हैं, अद्वितीय डेटा के बारे में कोई चिंता नहीं है, और यह वास्तविक डेटाबेस संरचना को नहीं छूता है। कुछ इस तरह:

CREATE VIEW vw_competitions
  AS
  SELECT
   Id int
   CompetitionName nvarchar(75)
   CompetitionType nvarchar(50)
   OtherField1 int
   OtherField2 nvarchar(64)  --add the fields you want viewed from the Competition table
  FROM Competitions
GO

अन्य वस्तुओं को यहां जोड़ा जा सकता है जैसे कि अन्य तालिकाओं पर जुड़ना, जहां क्लॉस, आदि। इस समस्या का सबसे सुरुचिपूर्ण समाधान होने की संभावना है, क्योंकि अब आप केवल दृश्य को क्वेरी कर सकते हैं:

SELECT *
FROM vw_competitions

... और किसी भी WHERE, IN या EXISTS क्लॉस को व्यू क्वेरी में जोड़ें।

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