फास्ट परिवर्तन स्तंभ NVARCHAR (4000) से NVARCHAR (260)


12

मुझे एक बहुत बड़ी मेमोरी ग्रांट के साथ प्रदर्शन की समस्या है, इस तालिका को कुछ NVARCHAR(4000)स्तंभों के साथ संभालना है । बात ये है कि ये कॉलम कभी बड़े नहीं होते हैं NVARCHAR(260)

का उपयोग करते हुए

ALTER TABLE [table] ALTER COLUMN [col] NVARCHAR(260) NULL

SQL सर्वर में परिणाम संपूर्ण तालिका (और लॉग स्पेस में 2x तालिका आकार का उपयोग करके) को फिर से लिखना है, जो अरबों पंक्तियों का है, केवल कुछ भी नहीं बदलने के लिए, एक विकल्प नहीं है। कॉलम की चौड़ाई बढ़ाने से यह समस्या नहीं होती है, लेकिन यह घटती है।

मैंने एक बाधा बनाने की कोशिश की है CHECK (DATALENGTH([col]) <= 520)या CHECK (LEN([col]) <= 260)SQL सर्वर अभी भी पूरी तालिका को फिर से लिखने का फैसला करता है।

क्या मेटाडेटा-ओनली ऑपरेशन के रूप में कॉलम डेटा प्रकार को बदलने का कोई तरीका है? पूरी तालिका को फिर से लिखने के खर्च के बिना? मैं SQL सर्वर 2017 (14.0.2027.2 और 14.0.3192.2) का उपयोग कर रहा हूं।

पुन: पेश करने के लिए उपयोग करने के लिए यहां एक नमूना DDL तालिका है:

CREATE TABLE [table](
    id INT IDENTITY(1,1) NOT NULL,
    [col] NVARCHAR(4000) NULL,
    CONSTRAINT [PK_test] PRIMARY KEY CLUSTERED (id ASC)
);

और फिर चला ALTER

जवाबों:


16

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

  1. सभी कोडों में NVARCHAR (260) के रूप में कॉलम का उपयोग करें। क्वेरी ऑप्टिमाइज़र कच्चे के बजाय कास्टेड डेटा प्रकार का उपयोग करके मेमोरी अनुदान की गणना करेगा।
  2. तालिका का नाम बदलें और एक दृश्य बनाएं जो कलाकारों के बजाय करता है। यह विकल्प 1 जैसी ही चीज को पूरा करता है, लेकिन आपके द्वारा अपडेट किए जाने वाले कोड की मात्रा को सीमित कर सकता है।
  3. सही डेटा प्रकार के साथ एक गैर-स्थायी कंप्यूटेड कॉलम बनाएं और मूल कॉलम के बजाय उस कॉलम से अपने सभी प्रश्नों का चयन करें।
  4. मौजूदा कॉलम का नाम बदलें और मूल नाम के साथ कंप्यूटेड कॉलम जोड़ें। इसके बजाय नए कॉलम नाम का उपयोग करने के लिए मूल कॉलम में अपडेट या आवेषण करने वाले अपने सभी प्रश्नों को समायोजित करें।

15

क्या मेटाडेटा-ओनली ऑपरेशन के रूप में कॉलम डेटा प्रकार को बदलने का कोई तरीका है?

मुझे ऐसा नहीं लगता, यह उत्पाद अभी कैसे काम करता है। जोई के जवाब में प्रस्तावित इस सीमा के लिए वास्तव में कुछ महान वर्कअराउंड हैं ।

... SQL सर्वर में संपूर्ण तालिका को फिर से लिखना (और लॉग स्पेस में 2x तालिका आकार का उपयोग करके)

मैं उस कथन के दो भागों का अलग-अलग जवाब देने जा रहा हूं।

तालिका को फिर से लिखना

जैसा कि मैंने पहले उल्लेख किया है, वास्तव में इससे बचने का कोई तरीका नहीं है। यह स्थिति की वास्तविकता प्रतीत होती है, भले ही यह ग्राहकों के रूप में हमारे दृष्टिकोण से पूरी तरह से समझ में न आए।

DBCC PAGE4000 से 260 तक कॉलम को बदलने से पहले और बाद में देखने से पता चलता है कि डेटा पृष्ठ पर सभी डेटा डुप्लिकेट है (मेरी परीक्षण तालिका 'A'में पंक्ति में 260 बार थी ):

पहले और बाद में dbcc पृष्ठ के डेटा भाग का स्क्रीनशॉट

इस बिंदु पर, पृष्ठ पर सटीक समान डेटा की दो प्रतियां हैं। "पुराना" कॉलम अनिवार्य रूप से हटा दिया गया है (आईडी आईडी = 2 से आईडी = 67108865 पर बदल दिया गया है), और पृष्ठ पर डेटा की नई ऑफसेट को इंगित करने के लिए कॉलम का "नया" संस्करण अपडेट किया गया है:

पहले और बाद में dbcc पृष्ठ के कॉलम मेटाडेटा भागों का स्क्रीनशॉट

लॉग स्पेस में 2x टेबल साइज का उपयोग करना

बयान WITH (ONLINE = ON)के अंत में जोड़ने से लॉगिंग गतिविधि लगभग आधे से कम हो जाती है, इसलिए यह एक ऐसा सुधार है जिसे आप लिखने की मात्रा को डिस्क / डिस्क स्थान की आवश्यकता को कम कर सकते हैं।ALTER

मैंने इसे आज़माने के लिए इस टेस्ट हार्नेस का उपयोग किया:

USE [master];
GO
DROP DATABASE IF EXISTS [248749];
GO
CREATE DATABASE [248749] 
ON PRIMARY 
(
    NAME = N'248749', 
    FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\248749.mdf', 
    SIZE = 2048000KB, 
    FILEGROWTH = 65536KB
)
LOG ON 
(
    NAME = N'248749_log', 
    FILENAME = N'C:\Program Files\Microsoft SQL Server\MSSQL14.SQL2017\MSSQL\DATA\248749_log.ldf', 
    SIZE = 2048000KB, 
    FILEGROWTH = 65536KB
);
GO
USE [248749];
GO

CREATE TABLE dbo.[table]
(
    id int IDENTITY(1,1) NOT NULL,
    [col] nvarchar (4000) NULL,

    CONSTRAINT [PK_test] PRIMARY KEY CLUSTERED (id ASC)
);

INSERT INTO dbo.[table]
SELECT TOP (1000000)
    REPLICATE(N'A', 260)
FROM master.dbo.spt_values v1
    CROSS JOIN master.dbo.spt_values v2
    CROSS JOIN master.dbo.spt_values v3;
GO

मैंने बयान sys.dm_io_virtual_file_stats(DB_ID(N'248749'), DEFAULT)चलाने से पहले और बाद में जाँच की ALTER, और यहाँ अंतर हैं:

डिफ़ॉल्ट (ऑफ़लाइन) ALTER

  • डेटा फ़ाइल लिखता है / बाइट्स: 34,809 / 2,193,801,216
  • लॉग फाइल लिखता / लिखता है: 40,953 / 1,484,910,080

ऑनलाइन ALTER

  • डेटा फ़ाइल लिखती है / बाइट्स: 36,874 / 1,693,745,152 (22.8% गिरावट)
  • लॉग फ़ाइल लिखता है / बाइट्स लिखा है: 24,680 / 866,166,272 (41% ड्रॉप)

जैसा कि आप देख सकते हैं, डेटा फ़ाइल राइट्स में थोड़ी सी गिरावट थी, और लॉग फ़ाइल में एक बड़ी गिरावट लिखती है।


2

मैं कई बार ऐसी ही स्थिति में रहा हूं।

कदम :

इच्छित चौड़ाई का एक नया कोल जोड़ें

पुराने कॉलम से नए कॉलम में डेटा कॉपी करने के लिए प्रति हजार हज़ार पुनरावृत्तियों (शायद दस या बीस हज़ार) के साथ एक कर्सर का उपयोग करें

पुराना स्तंभ गिराओ

पुराने कॉलम के नाम से नए कॉलम का नाम बदलें

टाडा!


3
क्या होगा यदि आपके द्वारा पहले ही कॉपी किए गए कुछ रिकॉर्ड अपडेट किए जा रहे हैं, या हटा दिए गए हैं?
जॉर्ज 15।

1
update table set new_col = old_col where new_col <> old_col;ड्रॉप करने से पहले एक फाइनल करना बहुत आसान है old_col
कॉलिन टी हार्ट हार्ट

1
@ Colin'tHart कि एप्रोच लाखों पंक्तियों के साथ काम नहीं करेगी ... लेन-देन बहुत बड़ा हो जाता है, और यह ब्लॉक हो जाता है ....
जोन्सोम पुनः स्थापित करें मोनिका

@samsmith सबसे पहले आप वही करें जो आप ऊपर वर्णित हैं। फिर, मूल कॉलम को छोड़ने से पहले, यदि इस बीच मूल डेटा में कोई अपडेट हुआ है, तो उस अपडेट स्टेटमेंट को चलाएं। इसे केवल उन कुछ पंक्तियों को प्रभावित करना चाहिए जिन्हें संशोधित किया गया है। या क्या मैं कुछ न कुछ भूल रहा हूं?
कॉलिन टी हार्ट हार्ट

प्रक्रिया के दौरान अपडेट की गई पंक्तियों को कवर करने के लिए, पूर्ण स्कैन से बचने की कोशिश करना, where new_col <> old_colजिसमें कोई अन्य फ़िल्टरिंग क्लॉज़ नहीं होगा, आप इन परिवर्तनों को पूरा करने के लिए ट्रिगर जोड़ सकते हैं जैसा कि वे होते हैं और प्रक्रिया के अंत में इसे हटा देते हैं। फिर भी एक संभावित प्रदर्शन हिट हुआ, लेकिन अंत में एक बड़ी हिट के बजाय प्रक्रिया की लंबाई पर कई छोटी मात्राएं, शायद (तालिका के लिए आपके ऐप के अपडेट पैटर्न के आधार पर) कुल मिलाकर अब तक कम से कम एक बड़ी हिट की तुलना में ।
डेविड स्पिललेट

1

वैसे आपके डेटाबेस में उपलब्ध स्थान के आधार पर एक विकल्प है।

  1. अपनी तालिका की एक सटीक प्रतिलिपि बनाएँ (जैसे new_table), उस स्तंभ को छोड़कर, जहाँ से आप इसे छोटा कर NVARCHAR(4000)रहे हैं NVARCHAR(260):

    CREATE TABLE [new_table](
        id INT IDENTITY(1,1) NOT NULL,
        [col] NVARCHAR(260) NULL,
        CONSTRAINT [PK_test_new] PRIMARY KEY CLUSTERED (id ASC)
    );
  2. एक रखरखाव विंडो में "टूटी हुई" तालिका ( table) से "निश्चित" तालिका ( new_table) तक डेटा को एक साधारण से कॉपी करें INSERT ... INTO ... SELECT ....:

    SET IDENTITY_INSERT [new_table] ON
    GO
    INSERT id, col INTO [new_table] SELECT id, col from [table]
    GO
    SET IDENTITY_INSERT [new_table] OFF
    GO
  3. tableकुछ और करने के लिए "टूटी" तालिका का नाम बदलें :

    EXEC sp_rename 'table', 'old_table';  
  4. "निर्धारित" तालिका new_tableका नाम बदलें table:

    EXEC sp_rename 'new_table', 'table';  
  5. यदि सब कुछ ठीक है, तो "टूटी" नामांकित तालिका को छोड़ दें:

     DROP TABLE [old_table]
     GO

तुम वहाँ जाओ।

आपके सवालों का जवाब दे रहा हूं

क्या मेटाडेटा-ओनली ऑपरेशन के रूप में कॉलम डेटा प्रकार को बदलने का कोई तरीका है?

वर्तमान में संभव नहीं है

पूरी तालिका को फिर से लिखने के खर्च के बिना?

नहीं।
( मेरा समाधान और अन्य देखें। )


आपका "इनसेक्ट इन सेलेक्ट" एक परिणाम, एक बड़ी तालिका (लाखों या अरबों पंक्तियों) पर एक आम लेनदेन में होगा, जो डीबी को दसियों या सैकड़ों मिनटों के लिए रोक सकता है। (साथ ही साथ Ldf को बहुत बड़ा और संभवत: लॉग शिपिंग को तोड़ते हुए, यदि उपयोग में हो)
जोन्सोम पुनः स्थापित करें मोनिका
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.