2012-06-20 19 views
8

के साथ ऑल्टर कॉलम डेटाटाइप मेरे पास 80 से अधिक विभिन्न तालिकाओं में एक संदर्भ आईडी वर्कर (6) कॉलम है। सरकारी संगठन द्वारा लागू किए गए परिवर्तन के बाद मुझे आईडी को एक वर्चर (8) में विस्तारित करने की आवश्यकता है जो आईडी निर्दिष्ट करता है।प्राथमिक कुंजी

मैं इस प्रकार तालिका नाम पाने के लिए एक कर्सर घोषित करने के लिए उम्मीद कर रहा था:

DECLARE @TableName AS VARCHAR(200) 
DECLARE TableCursor CURSOR LOCAL READ_ONLY FOR 
SELECT t.name AS TableName 
    FROM sys.columns c 
     JOIN sys.tables t ON c.object_id = t.object_id 
    WHERE c.name = 'ReferenceID' 

OPEN TableCursor 
    FETCH NEXT FROM TableCursor 
    INTO @TableName 

और फिर प्रकार संपादित इस प्रकार है:

ALTER TABLE @TableName ALTER COLUMN ReferenceID VARCHAR(8) 

यह विफल रहता है क्योंकि स्तंभ का हिस्सा है कुछ तालिकाओं में प्राथमिक कुंजी (और पीके में शामिल कॉलम तालिका से तालिका में भिन्न होते हैं)।

मैं वास्तव में प्रत्येक तालिका के लिए प्रत्येक पीके को मैन्युअल रूप से ड्रॉप और पुन: बनाना नहीं चाहता हूं।

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

+1

आपको प्राथमिक कुंजी को छोड़ना और फिर से बनाना होगा। –

+0

@Damien_The_Unbeliever - नहीं आप नहीं करते हैं। यह केवल मेटाडाटा ही बदलता है। सभी पीके इंडेक्स को पुनर्निर्माण करने की कोई ज़रूरत नहीं है। –

+0

@ मार्टिन स्मिथ - हाँ, मैंने किया, नीचे मेरा समाधान देखें। – Neil

उत्तर

6

आपको NOT NULL स्पष्ट रूप से ALTER TABLE ... ALTER COLUMN में निर्दिष्ट करने की आवश्यकता है अन्यथा यह NULL को अनुमति देने के लिए डिफ़ॉल्ट है। पीके कॉलम में इसकी अनुमति नहीं है।

निम्नलिखित ठीक काम करता है।

CREATE TABLE p 
(
ReferenceID VARCHAR(6) NOT NULL PRIMARY KEY 
) 

INSERT INTO p VALUES ('AAAAAA') 

ALTER TABLE p ALTER COLUMN ReferenceID VARCHAR(8) NOT NULL 

जब NOT NULL छोड़ दिया जाता है यह निम्न त्रुटि

Msg 5074, Level 16, State 1, Line 1 
The object 'PK__p__E1A99A792180FB33' is dependent on column 'ReferenceID'. 
Msg 4922, Level 16, State 9, Line 1 
ALTER TABLE ALTER COLUMN ReferenceID failed because one or more objects access this column. 

बातें अपने कार्यक्रम संबंधी दृष्टिकोण में विचार करने के लिए की एक जोड़े है कि आप हैं किसी भी विदेशी कुंजी ReferenceID स्तंभों को संदर्भित ड्रॉप करने की जरूरत है देता है अस्थायी रूप से और यह भी सुनिश्चित करें कि आप NOT NULL (गैर पीके) ReferenceID कॉलम शामिल नहीं करते हैं जो वर्तमान में शून्य हैं।

0

आपको ALTER कथन को गतिशील एसक्यूएल के रूप में निष्पादित करने की आवश्यकता है: कथन को SQL स्ट्रिंग के रूप में बनाएं और इसे sp_executesql पर पास करें।

+0

Stil एल विफल रहता है क्योंकि कॉलम प्राथमिक कुंजी का हिस्सा है। – Neil

3

EDIT इस समाधान की आवश्यकता है यदि आपके पास वर्चर (6) और चार (6) कॉलम के मिश्रण के साथ एक गड़बड़ डेटाबेस है जो 10 वर्षों से अधिक विकास के कारण होता है (सरकारी नीति के पर्याप्त परिवर्तनों के साथ किसी भी प्रयास के कारण "अच्छा डेटाबेस डिजाइन" अंत में संक्षिप्त करने के लिए।) अंत संपादित

जो लोग कहा कि मैं ड्रॉप और पी पुन: करने के लिए होगा करने के लिए

, तुम सही थे। इंडेक्स और विदेशी कुंजी को भी छोड़ने और पुनर्निर्माण की आवश्यकता होती है।

सौभाग्य से, सूचकांक और एफके की एक प्रबंधनीय संख्या थी इसलिए मैंने इन्हें 'असाधारण' के रूप में संभाला और स्क्रिप्ट की शुरुआत में उन्हें एक-एक करके गिरा दिया, फिर उन्हें एक बार फिर से जोड़ा , स्क्रिप्ट के अंत में (/ * */नीचे के दो खंड देखें)।

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

एसक्यूएल तार जो इकट्ठे होते हैं उन्हें नीचे लिपि में मुद्रित किया जाता है। यदि आप इसका पुन: उपयोग करना चाहते हैं (प्रदान की गई कोई वारंटी नहीं, आदि, ब्ला ब्लाह), निष्पादन समय से 50% तक दस्तक देने के लिए इन्हें टिप्पणी करें।

SET NOCOUNT ON 

/* Handle exceptional tables here 
* Remove indexes and foreign keys 
* --Lots of "IF EXISTS ... ALTER TABLE <name> DROP CONSTRAINT <constraint name>, etc. 
*/ 

--Declare variables 
DECLARE @SQL     VARCHAR(8000) 
DECLARE @TableName    VARCHAR(512) 
DECLARE @ConstraintName   VARCHAR(512) 
DECLARE @tColumn    VARCHAR(512) 
DECLARE @Columns    VARCHAR(8000) 
DECLARE @IsDescending   BIT 

--Set up temporary table 
SELECT 
    tbl.[schema_id], 
    tbl.name AS TableName, 
    i.NAME AS IndexName, 
    i.type_desc, 
    c.[column], 
    c.key_ordinal, 
    c.is_desc, 
    i.[object_id], 
    s.no_recompute, 
    i.[ignore_dup_key], 
    i.[allow_row_locks], 
    i.[allow_page_locks], 
    i.[fill_factor], 
    dsi.type, 
    dsi.name AS DataSpaceName 
INTO #PKBackup 
FROM 
    sys.tables AS tbl 
    INNER JOIN sys.indexes AS i 
     ON (
      i.index_id > 0 
      AND i.is_hypothetical = 0 
     ) 
     AND (i.[object_id] = tbl.[object_id]) 
    INNER JOIN (
     SELECT 
      ic.[object_id] , 
      c.[name] [column] , 
      ic.is_descending_key [is_desc], 
      ic.key_ordinal 
     FROM 
      sys.index_columns ic 
      INNER JOIN 
       sys.indexes i 
       ON 
       i.[object_id] = ic.[object_id] 
       AND 
       i.index_id = 1 
       AND 
       ic.index_id = 1 
      INNER JOIN 
       sys.tables t 
       ON 
       t.[object_id] = ic.[object_id] 
      INNER JOIN 
       sys.columns c 
       ON 
       c.[object_id] = t.[object_id] 
       AND 
       c.column_id = ic.column_id 
     ) AS c 
     ON c.[object_id] = i.[object_id] 
    LEFT OUTER JOIN 
     sys.key_constraints AS k 
     ON 
     k.parent_object_id = i.[object_id] 
     AND 
     k.unique_index_id = i.index_id 
    LEFT OUTER JOIN 
     sys.data_spaces AS dsi 
     ON 
     dsi.data_space_id = i.data_space_id 
    LEFT OUTER JOIN 
     sys.xml_indexes AS xi 
     ON 
     xi.[object_id] = i.[object_id] 
     AND 
     xi.index_id = i.index_id 
    LEFT OUTER JOIN 
     sys.stats AS s 
     ON 
     s.stats_id = i.index_id 
     AND 
     s.[object_id] = i.[object_id] 
WHERE 
    k.TYPE = 'PK' 

DECLARE TableCursor CURSOR LOCAL READ_ONLY FOR 
    SELECT t.name AS TableName 
    FROM sys.columns c 
     JOIN sys.tables t ON c.object_id = t.object_id 
    WHERE 
     c.name = 'ReferenceID' 

OPEN TableCursor 
    FETCH NEXT FROM TableCursor 
    INTO @TableName 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    PRINT('--Updating ' + @TableName + '...') 

    SELECT @ConstraintName = PK.CONSTRAINT_NAME 
    FROM 
     INFORMATION_SCHEMA.TABLE_CONSTRAINTS PK 
    WHERE 
     PK.TABLE_NAME = @TableName 
     AND 
     PK.CONSTRAINT_TYPE = 'PRIMARY KEY' 

--drop the constraint 
    --Some tables don't have a PK defined, only do the next bit if they do 
    IF (SELECT COUNT(*) FROM #PKBackup PK WHERE PK.TableName = @TableName) > 0 
    BEGIN 
     SET @SQL = 'ALTER TABLE @TableName DROP CONSTRAINT @ConstraintName' 
     SET @SQL = REPLACE(@SQL, '@TableName', @TableName) 
     SET @SQL = REPLACE(@SQL, '@ConstraintName', @ConstraintName) 
     PRINT @SQL 
     EXEC (@SQL) 
    END 
--This is where we actually change the datatype of the column 
    SET @SQL = 'ALTER TABLE @TableName ALTER COLUMN ReferenceID VARCHAR(8)' + (SELECT CASE WHEN C.Is_Nullable = 'NO' THEN ' NOT NULL' ELSE '' END 
     FROM INFORMATION_SCHEMA.COLUMNS C 
     WHERE C.TABLE_NAME = @TableName AND C.COLUMN_NAME = 'ReferenceID') 
    SET @SQL = REPLACE(@SQL, '@TableName', @TableName) 

    PRINT(@SQL) 
    EXEC(@SQL) 

--Recreate the constraint 
    --Some tables don't have a PK defined, only do the next bit if they do 
    IF (SELECT COUNT(*) FROM #PKBackup PK WHERE PK.TableName = @TableName) > 0 
    BEGIN 
    --First set up @SQL template 
    SELECT @SQL = 'ALTER TABLE [' + SCHEMA_NAME(PK.schema_id) + '].[' + PK.TableName 
        + '] ADD CONSTRAINT [' + PK.IndexName 
        + '] PRIMARY KEY ' + Type_desc + ' (@Columns) WITH ' 
        + '(PAD_INDEX = ' + CASE WHEN CAST(INDEXPROPERTY(pk.[object_id], PK.IndexName, N'IsPadIndex') AS BIT) = 0 THEN 'OFF' 
               ELSE 'ON' 
              END + ', ' 
        + 'STATISTICS_NORECOMPUTE = ' + CASE WHEN pk.no_recompute = 0 THEN 'OFF' 
                  ELSE 'ON' 
                 END 
        + ', SORT_IN_TEMPDB = OFF, ' 
        + 'IGNORE_DUP_KEY = ' + CASE WHEN pk.[ignore_dup_key] = 0 THEN 'OFF' 
                ELSE 'ON' 
               END + ', ' 
        + 'ONLINE = OFF, ' 
        + 'ALLOW_ROW_LOCKS = ' + CASE WHEN pk.allow_row_locks = 0 THEN 'OFF' 
                ELSE 'ON' 
               END + ', ' 
        + 'ALLOW_PAGE_LOCKS = ' + CASE WHEN pk.allow_page_locks = 0 THEN 'OFF' 
                ELSE 'ON' 
               END + ', ' 
        + 'FILLFACTOR = ' + CASE WHEN pk.[fill_factor] = 0 THEN '100' 
               ELSE CONVERT(NVARCHAR, pk.[fill_factor]) 
              END + ' ' 
        + ') ON [' + CASE WHEN 'FG' = pk.[type] THEN pk.DataSpaceName 
             ELSE N'' 
            END + ']' 
    FROM 
    #PKBackup PK WHERE PK.TableName = @TableName 

    SET @SQL = REPLACE(@SQL, '@TableName', @TableName) 
    SET @SQL = REPLACE(@SQL, '@ConstraintName', @ConstraintName) 

    --Second, build up @Columns 
    SET @Columns = ' ' 
    DECLARE ColumnCursor CURSOR LOCAL READ_ONLY FOR 
     SELECT pk.[column], PK.is_desc 
      FROM #PKBackup PK 
      WHERE PK.TableName = @TableName 
      ORDER BY PK.key_ordinal ASC 

    OPEN ColumnCursor 
     FETCH NEXT FROM ColumnCursor 
     INTO @tColumn, @IsDescending 

    WHILE @@FETCH_STATUS = 0 
    BEGIN 
     SET @Columns = @Columns + @tColumn + CASE WHEN @IsDescending = 1 THEN ' DESC, ' ELSE ' ASC, ' END 

     --Get the next TableName 
     FETCH NEXT FROM ColumnCursor 
     INTO @tColumn, @IsDescending 
    END 

    --Tidy up 
    CLOSE ColumnCursor 
    DEALLOCATE ColumnCursor 

    --Delete the last comma 
    SET @Columns = LEFT(@Columns, LEN(@Columns) - 1) 
    END 
--Recreate the constraint 
    SET @SQL = REPLACE(@SQL, '@Columns', @Columns) 
    PRINT @SQL 
    EXEC (@SQL) 

    PRINT('--Done 
    ') 

    SET @SQL = '' 

--Get the next TableName 
    FETCH NEXT FROM TableCursor 
    INTO @TableName 
END 

--Tidy up 
CLOSE TableCursor 
DEALLOCATE TableCursor 

DROP TABLE #PKBackup 

/* Handle exceptional tables here 
* Replace indexes and foreign keys that were removed at the start 
*/ 

SET NOCOUNT OFF 
+0

[लिंक] से (http://msdn.microsoft.com/en-us/library/ms181043%28v=sql.105%29.aspx) "हालांकि, आप प्राथमिक कुंजी के साथ परिभाषित कॉलम की लंबाई नहीं बदल सकते बाधा। " इसलिए मुझे इंडेक्स को छोड़ने और फिर से बनाने की आवश्यकता थी। – Neil

+0

मेरा मानना ​​है कि यह SQL Server 2005 पर संभव हो सकता है, लेकिन 2008R2 – Neil

+0

पर यह संभव नहीं है जैसे प्रलेखन गलत है। क्या आपने मेरे उत्तर में कोड चलाने का प्रयास किया था? क्या यह 100% साबित नहीं करता है कि यह संभव है? (मैंने 2008 आर 2 पर इसका परीक्षण किया है)। यह सभी मामलों में संभव नहीं होगा उदा। 'वर्चर (6)' से 'वर्कर (8)' में जा रहा है, सिर्फ मेटाडाटा परिवर्तन है लेकिन दूसरी दिशा में जाना नहीं है। –

-1

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

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

संबंधित मुद्दे