एसक्यूएल सर्वर लेनदेन करने से पहले डीडीएल को लेनदेन करने की अनुमति देता है (दिखाई देता है)?


9

PostgreSQL में मैं कुछ परीक्षण डेटा के साथ एक तालिका बना सकता हूं, और फिर एक लेनदेन में इसे एक अलग प्रकार के एक नए कॉलम में माइग्रेट कर सकता हूं, जिसके परिणामस्वरूप एक टेबल-फिर से लिखना होगा COMMIT,

CREATE TABLE foo ( a int );
INSERT INTO foo VALUES (1),(2),(3);

के बाद,

BEGIN;
  ALTER TABLE foo ADD COLUMN b varchar;
  UPDATE foo SET b = CAST(a AS varchar);
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

हालाँकि, Microsoft की SQL सर्वर में एक ही बात एक त्रुटि उत्पन्न होती है। इस कार्यशील db फिडेल की तुलना करें , जहाँ ADD(कॉलम) कमांड लेनदेन के बाहर है,

-- txn1
BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
COMMIT;

-- txn2
BEGIN TRANSACTION;
  UPDATE foo SET b = CAST( a AS varchar );
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

इस डीबी फ़िडल जो काम नहीं करता है,

-- txn1
BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
  UPDATE foo SET b = CAST( a AS varchar );
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

लेकिन त्रुटियों के बजाय

Msg 207 Level 16 State 1 Line 2
Invalid column name 'b'.

क्या डीडीएल के संबंध में, इस सौदे को दिखाई देने के लिए कुछ भी है, पोस्टग्रेक्यूएल की तरह व्यवहार करें?

जवाबों:


17

सामान्यतया, नहीं। एसक्यूएल सर्वर निष्पादन से पहले पूरे बैच को वर्तमान दायरे में संकलित करता है ताकि संदर्भित संस्थाओं को अस्तित्व में होना पड़े (बयान-स्तर के पुनर्मूल्यांकन बाद में भी हो सकते हैं)। मुख्य अपवाद आस्थगित नाम रिज़ॉल्यूशन है, लेकिन यह तालिकाओं पर लागू होता है, स्तंभों पर नहीं:

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

सामान्य वर्कअराउंड में डायनेमिक कोड (जैसे जो के उत्तर में ) शामिल है, या डीएमएल और डीडीएल को अलग-अलग बैचों में अलग करता है।

इस विशिष्ट मामले के लिए आप भी लिख सकते हैं:

BEGIN TRANSACTION;

    ALTER TABLE dbo.foo
        ALTER COLUMN a varchar(11) NOT NULL
        WITH (ONLINE = ON);

    EXECUTE sys.sp_rename
        @objname = N'dbo.foo.a',
        @newname = N'b',
        @objtype = 'COLUMN';

COMMIT TRANSACTION;

आप अभी भी bएक ही बैच और दायरे में नामांकित कॉलम तक नहीं पहुंच पाएंगे , लेकिन यह काम पूरा कर लेता है।

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


12

यह वही है जो आप खोज रहे हैं?

BEGIN TRANSACTION;
  ALTER TABLE foo ADD b varchar;
  EXEC sp_executesql N'UPDATE foo SET b = CAST( a AS varchar )';
  ALTER TABLE foo DROP COLUMN a;
COMMIT;

2

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

यह कई बार उल्लेख किया जा सकता है कि जब आप डीएमएल बना रहे हैं उसी समय डीडीएल में बदलाव न करें। अच्छी प्रोग्रामिंग समर्थन को बनाए रखने और स्पेगेटी स्ट्रिंग परिवर्तन से बचने के लिए इन कार्यों को अलग करती है।

और जैसा कि पॉल ने स्पष्ट रूप से कहा है, SQL सर्वर बैचों में काम करता है ।

अब, उन लोगों के लिए जो इस काम पर संदेह करते हैं, यह संभवतः आपके उदाहरण पर नहीं है, लेकिन 2017 जैसे कुछ संस्करण वास्तव में काम कर सकते हैं! यहाँ सबूत है: यहां छवि विवरण दर्ज करें

[परीक्षण कोड - एसक्यूएल सर्वर के कई संस्करणों पर काम नहीं कर सकता]

USE master
GO
CREATE TABLE foo (a VARCHAR(11) )
GO
BEGIN TRANSACTION;
    INSERT INTO dbo.foo (a)
    VALUES ('entry')
/*****
[2] Check Values
*****/
    SELECT a FROM dbo.foo
/*****
[3] Add Column
*****/
    ALTER TABLE dbo.foo
        ADD b VARCHAR(11)
/*****
[3] Insert value into this new column in the same batch
-- Again, this is just an example. Please do not do this in production
*****/
    IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
            AND name = 'b')
        INSERT INTO dbo.foo (b)
        VALUES ('d')
COMMIT TRANSACTION;
/*****
[4] SELECT outside transaction
-- this will fail
*****/
    --IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
    --      AND name = 'b')
    --  SELECT b FROM dbo.foo
-- this will work...but a SELECT * ???
IF EXISTS (SELECT * FROM sys.columns WHERE object_ID('foo') = object_id
            AND name = 'b')
        SELECT * FROM dbo.foo

DROP TABLE dbo.foo

[निष्कर्ष]

तो हाँ आप DDL और DML को एक ही बैच में SQL Server 2017 के कुछ संस्करणों या पैच के लिए @AndriyM के रूप में प्रदर्शित कर सकते हैं - SQL 2017 पर dbfiddle बताते हैं, लेकिन सभी DML समर्थित नहीं हैं और कोई गारंटी नहीं है कि यह हमेशा ही रहेगा। यदि यह काम करता है, तो यह SQL सर्वर के आपके संस्करण का एक विचलन हो सकता है और यह नाटकीय समस्याओं का कारण बन सकता है क्योंकि आप नए संस्करणों को पैच या माइग्रेट करते हैं।

  • साथ ही, सामान्य तौर पर आपके डिजाइन में बदलाव का अनुमान होना चाहिए। मैं समझता हूं कि स्तंभों को संशोधित / जोड़ने की चिंता एक मेज पर हो सकती है, लेकिन आप बैचों में इसके चारों ओर ठीक से डिजाइन कर सकते हैं।

[अतिरिक्त श्रेय]

EXISTS स्टेटमेंट के लिए, जैसे पॉल ने कहा, आपके कोड में अगले चरण पर जाने से पहले कोड को मान्य करने के लिए बहुत सारे अन्य साधन हैं।

  • EXISTS स्टेटमेंट आपको SQL सर्वर के सभी संस्करणों पर काम करने वाले कोड बनाने में मदद कर सकता है
  • यह एक बूलियन फ़ंक्शन है जो एक बयान में जटिल जांच की अनुमति देता है

नहीं, आप नए कॉलम में नहीं डाल सकते हैं यदि आप उसी बैच में कर रहे हैं जहां आप कॉलम बना रहे हैं। अधिक आम तौर पर, आप नए कॉलम (एस) को उसी बैच में संकलित नहीं कर सकते हैं जब आपने इसे बनाया था। IF EXISTS ट्रिक इस मामले में काम नहीं करता है। या तो डीएमएल को गतिशील रूप से लागू करें या दूसरे बैच में करें।
एंड्री एम

@AndriyM माफ करना, गलत तरीके से dbfiddle के बारे में एक बयान दिया। लेकिन क्या आपने अपने इंस्टा पर यह कोशिश की? यह 2017 SP1 पर काम करता है। मैं एक जिफ़ अपलोड करूँगा, लेकिन क्या आपने अपने सिस्टम पर यह परीक्षण किया है?
क्लिफ्टन_ह

i.imgur.com/fhAC7lB.png आप वास्तव में बता सकते हैं कि यह bसम्मिलित कथन के तहत लहरदार रेखा के आधार पर संकलन नहीं करेगा । मैं एसक्यूएल सर्वर 2014
एंड्री एम

@AndriyM दिलचस्प मैंने इस कार्य को पहले देखा था, और यह SQL सर्वर के कुछ संस्करणों पर काम करता प्रतीत होता है, जैसे कि SQL Server 2017 में मैंने उल्लेख किया है।
क्लिफ्टन_ह

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