DEFAULT CONSTRAINT, इसके लायक है?


19

मैं आमतौर पर अगले नियमों के बाद अपने डेटाबेस डिजाइन करता हूं:

  • Db_owner और sysadmin के अलावा किसी और के पास डेटाबेस टेबल तक पहुंच नहीं है।
  • उपयोगकर्ता की भूमिकाएं एप्लिकेशन लेयर पर नियंत्रित की जाती हैं। मैं आमतौर पर विचारों, संग्रहीत प्रक्रियाओं और कार्यों तक पहुंच प्रदान करने के लिए एक db भूमिका का उपयोग करता हूं, लेकिन कुछ मामलों में, मैं कुछ संग्रहीत प्रक्रियाओं की सुरक्षा के लिए एक दूसरा नियम जोड़ता हूं।
  • मैं शुरू में महत्वपूर्ण सूचनाओं को मान्य करने के लिए TRIGGERS का उपयोग करता हूं।

CREATE TRIGGER <TriggerName>
ON <MyTable>
[BEFORE | AFTER] INSERT
AS
    IF EXISTS (SELECT 1 
               FROM   inserted
               WHERE  Field1 <> <some_initial_value>
               OR     Field2 <> <other_initial_value>)
    BEGIN
        UPDATE MyTable
        SET    Field1 = <some_initial_value>,  
               Field2 = <other_initial_value>  
        ...  
    END
  • DML को संग्रहीत प्रक्रियाओं का उपयोग करके निष्पादित किया जाता है:

sp_MyTable_Insert(@Field1, @Field2, @Field3, ...);
sp_MyTable_Delete(@Key1, @Key2, ...);
sp_MyTable_Update(@Key1, @Key2, @Field3, ...);

क्या आपको लगता है कि इस परिदृश्य पर, DEFAULT CONSTRAINTs का उपयोग करने के लिए इसके लायक है, या मैं DB सर्वर में एक अतिरिक्त और अनावश्यक नौकरी जोड़ रहा हूं?

अपडेट करें

मैं समझता हूं कि DEFAULT बाधा का उपयोग करके मैं किसी और को अधिक जानकारी दे रहा हूं जो डेटाबेस को प्रशासित करना होगा। लेकिन मुझे ज्यादातर प्रदर्शन पर दिलचस्पी है।

मुझे लगता है कि डेटाबेस हमेशा डिफ़ॉल्ट मान की जाँच कर रहा है, भले ही मैं सही मूल्य की आपूर्ति करता हूं, इसलिए मैं एक ही काम दो बार कर रहा हूं।

उदाहरण के लिए, ट्रिगर निष्पादन के भीतर DEFAULT बाधा से बचने का एक तरीका है?


1
यदि आप अपने सभी डीएमएल के लिए प्रक्रियाओं का उपयोग कर रहे हैं, तो मैं वहां आपका सत्यापन तर्क करूंगा। आधार तालिका में ग़लतियाँ करने से किसी को रोकने के लिए बाधाओं का उपयोग करें, लेकिन ट्रिगर नाटकीय रूप से आवेषण को धीमा कर देगा।
जोनाथन फाइट

ट्रिगर्स में आप क्या मान्यता दे रहे हैं? क्या इसके बजाय चेक की कमी के साथ किया जा सकता है?
मार्टिन स्मिथ

@MartinSmith यह निर्भर करता है, लेकिन चेक बाधा हमेशा लागू होती है, मुझे प्रारंभिक मान सुनिश्चित करने की आवश्यकता है, जैसे उपयोगकर्ता, वर्तमान समय, आदि मेरे अन्य प्रश्नों पर एक नज़र है: dba.stackexchange.com/questions/1620678/…
McNets

1
यदि आप "सामान्य" उत्तर चाहते हैं, तो आपको "बाधा" भाग को हटा देना चाहिए। एसक्यूएल में, बाधाओं को मान्य इनपुट मानों। वे एक डिफ़ॉल्ट मान को परिभाषित नहीं करते हैं । SQL में "डिफ़ॉल्ट बाधा" जैसी कोई चीज नहीं है। मैंने टैग जोड़े sql-serverऔर tsqlआपके प्रश्न में कोड उस DBMS के लिए अत्यधिक विशिष्ट है
a_horse_with_no_name

जवाबों:


21

मुझे लगता है कि डेटाबेस हमेशा डिफ़ॉल्ट मान की जाँच कर रहा है, भले ही मैं सही मूल्य की आपूर्ति करता हूं, इसलिए मैं एक ही काम दो बार कर रहा हूं।

उम, आप ऐसा क्यों मानेंगे? ;-)। यह देखते हुए कि डिफॉल्ट्स एक मान प्रदान करने के लिए मौजूद हैं जब वे जिस कॉलम में संलग्न होते हैं वह INSERTकथन में मौजूद नहीं होता है, मैं इसके विपरीत मानूंगा: यदि वे संबंधित कॉलम INSERTस्टेटमेंट में मौजूद हैं तो उन्हें पूरी तरह से अनदेखा कर दिया जाता है ।

सौभाग्य से, हम में से किसी को भी इस कथन के कारण कुछ भी मानने की आवश्यकता नहीं है:

मुझे ज्यादातर प्रदर्शन पर दिलचस्पी है।

प्रदर्शन के बारे में प्रश्न लगभग हमेशा परीक्षण योग्य होते हैं। इसलिए हमें इस प्रश्न का उत्तर देने के लिए SQL सर्वर (यहाँ सही प्राधिकारी) की अनुमति देने के लिए एक परीक्षण की आवश्यकता है।

सेट अप

निम्नलिखित को एक बार चलाएं:

SET NOCOUNT ON;

-- DROP TABLE #HasDefault;
CREATE TABLE #HasDefault
(
  [HasDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  [SomeInt] INT NULL,
  [SomeDate] DATETIME NOT NULL DEFAULT (GETDATE())
);

-- DROP TABLE #NoDefault;
CREATE TABLE #NoDefault
(
  [NoDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  [SomeInt] INT NULL,
  [SomeDate] DATETIME NOT NULL
);

-- make sure that data file and Tran Log file are grown, if need be, ahead of time:
INSERT INTO #HasDefault ([SomeInt])
  SELECT TOP (2000000) NULL
  FROM   [master].sys.[all_columns] ac1
  CROSS JOIN [master].sys.[all_columns] ac2;

परीक्षा 1 ए और 1 बी को व्यक्तिगत रूप से परीक्षण करें, न कि एक साथ जो समय सीमा को पूरा करता है। हर एक के लिए औसत टाइमिंग की समझ पाने के लिए हर बार कई बार दौड़ें।

टेस्ट 1 ए

TRUNCATE TABLE #HasDefault;
GO

PRINT '#HasDefault:';
SET STATISTICS TIME ON;
INSERT INTO #HasDefault ([SomeDate])
  SELECT TOP (1000000) '2017-05-15 10:11:12.000'
  FROM   [master].sys.[all_columns] ac1
  CROSS JOIN [master].sys.[all_columns] ac2;
SET STATISTICS TIME OFF;
GO

टेस्ट 1 बी

TRUNCATE TABLE #NoDefault;
GO

PRINT '#NoDefault:';
SET STATISTICS TIME ON;
INSERT INTO #NoDefault ([SomeDate])
  SELECT TOP (1000000) '2017-05-15 10:11:12.000'
  FROM   [master].sys.[all_columns] ac1
  CROSS JOIN [master].sys.[all_columns] ac2;
SET STATISTICS TIME OFF;
GO

2A और 2B को अलग-अलग परीक्षण करें, न कि एक साथ जो समय को रोकते हैं। हर एक के लिए औसत टाइमिंग की समझ पाने के लिए हर बार कई बार दौड़ें।

टेस्ट 2 ए

TRUNCATE TABLE #HasDefault;
GO

DECLARE @Counter INT = 0,
        @StartTime DATETIME,
        @EndTime DATETIME;

BEGIN TRAN;
--SET STATISTICS TIME ON;
SET @StartTime = GETDATE();
WHILE (@Counter < 100000)
BEGIN
  INSERT INTO #HasDefault ([SomeDate]) VALUES ('2017-05-15 10:11:12.000');
  SET @Counter = @Counter + 1;
END;
SET @EndTime = GETDATE();
--SET STATISTICS TIME OFF;
COMMIT TRAN;
PRINT DATEDIFF(MILLISECOND, @StartTime, @EndTime);

टेस्ट 2 बी

TRUNCATE TABLE #NoDefault;
GO

DECLARE @Counter INT = 0,
        @StartTime DATETIME,
        @EndTime DATETIME;

BEGIN TRAN;
--SET STATISTICS TIME ON;
SET @StartTime = GETDATE();
WHILE (@Counter < 100000)
BEGIN
  INSERT INTO #NoDefault ([SomeDate]) VALUES ('2017-05-15 10:11:12.000');
  SET @Counter = @Counter + 1;
END;
SET @EndTime = GETDATE();
--SET STATISTICS TIME OFF;
COMMIT TRAN;
PRINT DATEDIFF(MILLISECOND, @StartTime, @EndTime);

आपको देखना चाहिए कि परीक्षण 1 ए और 1 बी के बीच, या परीक्षण 2 ए और 2 बी के बीच के समय में कोई वास्तविक अंतर नहीं है। तो, नहीं, कोई DEFAULTपरिभाषित नहीं है, लेकिन इसका उपयोग नहीं किया गया है।

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


और, यह सिर्फ मेरे लिए हुआ है कि यह उद्देश्यपूर्ण रूप से दिखाया जा सकता है या नहीं, DEFAULTजब संबंधित कॉलम INSERTस्टेटमेंट में मौजूद है या नहीं, इसकी जाँच की जाए : बस एक अमान्य मान प्रदान करें। निम्नलिखित परीक्षण बस यही करता है:

-- DROP TABLE #BadDefault;
CREATE TABLE #BadDefault
(
  [BadDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  [SomeInt] INT NOT NULL DEFAULT (1 / 0)
);


INSERT INTO #BadDefault ([SomeInt]) VALUES (1234); -- Success!!!
SELECT * FROM #BadDefault; -- just to be sure ;-)



INSERT INTO #BadDefault ([SomeInt]) VALUES (DEFAULT); -- Error:
/*
Msg 8134, Level 16, State 1, Line xxxxx
Divide by zero error encountered.
The statement has been terminated.
*/
SELECT * FROM #BadDefault; -- just to be sure ;-)
GO

जैसा कि आप देख सकते हैं, जब एक कॉलम (और मान, कीवर्ड नहीं DEFAULT) प्रदान किया जाता है, तो डिफ़ॉल्ट को 100% अनदेखा किया जाता है। हम यह जानते हैं क्योंकि INSERTसफल होता है। लेकिन अगर डिफ़ॉल्ट का उपयोग किया जाता है, तो एक त्रुटि है क्योंकि इसे अंततः निष्पादित किया जा रहा है।


ट्रिगर निष्पादन के भीतर DEFAULT बाधा से बचने का एक तरीका है?

डिफ़ॉल्ट बाधाओं से बचने की आवश्यकता है (कम से कम इस संदर्भ में) पूरी तरह से अनावश्यक है, पूर्णता के लिए यह ध्यान दिया जा सकता है कि यह केवल एक INSTEAD OFट्रिगर के भीतर एक डिफ़ॉल्ट बाधा से "बचने" के लिए संभव होगा , लेकिन एक ट्रिगर के भीतर नहींAFTER । के लिए दस्तावेज़ के अनुसार बनाएं TRIGGER :

यदि ट्रिगर लाइन पर बाधाएं मौजूद हैं, तो उन्हें ट्रिगर निष्पादन के INSTEAD और AFTER ट्रिगर निष्पादन से पहले चेक किया जाता है। यदि बाधाओं का उल्लंघन किया जाता है, तो ट्रिगर कार्रवाई के INSTEAD को वापस ले लिया जाता है और AFTER ट्रिगर को निकाल नहीं दिया जाता है।

बेशक, INSTEAD OFट्रिगर का उपयोग करने की आवश्यकता होगी:

  1. डिफ़ॉल्ट बाधा को अक्षम करना
  2. एक AFTERट्रिगर बनाना जो बाधा को सक्षम करता है

हालाँकि, मैं ऐसा करने की सलाह नहीं दूंगा।


1
@McNets कोई समस्या नहीं :-)। इस सवाल ने वास्तव में कुछ शोध करने का एक शानदार अवसर प्रदान किया। और ऐसा करने में, मैंने MySQL पर परीक्षण करने का भी प्रयास किया (क्योंकि आपने मूल रूप से RDBMS के बारे में सामान्य रूप से पूछा था) और पाया कि MySQL में Defaults को स्थिरांक होने की आवश्यकता है, केवल डेटाटाइम CURRENT_TIMESTAMPकॉलम के लिए एक अपवाद है । इसलिए, अमान्य अभिव्यक्ति का उपयोग करने का "आसान" परीक्षण वहां काम नहीं करेगा (और अमान्य मान का उपयोग तब नहीं किया जा सकता है CREATE TABLEक्योंकि यह मान्य है), और मेरे पास प्रदर्शन परीक्षण का निर्माण करने का समय नहीं है। लेकिन मुझे संदेह है कि अधिकांश RDBMS उसी तरह से व्यवहार करेंगे।
सोलोमन रटज़की

9

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

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