Существует ли способ преобразовать некластерный индекс на Первичном ключе к кластеризованному? (SQL Server 2008)

title = u"Klüft skräms inför på fédéral électoral große"
import unicodedata
unicodedata.normalize('NFKD', title).encode('ascii','ignore')
'Kluft skrams infor pa federal electoral groe'
13
задан marc_s 12 March 2010 в 22:13
поделиться

4 ответа

Если на ваш существующий PK ссылается множество других таблиц, вы потратите много утомительных и подверженных ошибкам минут на написание сценария, удаляющего все ссылки FK и воссоздающего их.

SQL Server Management Studio может сделать это за вас. Возможно, вы не осознавали, что в таблице может быть только один кластеризованный индекс, поскольку кластеризованный индекс представляет собой физический порядок строк; это означает, что сначала необходимо вызвать кластеризованный индекс и отключить кластеризацию . Тогда и только тогда вы можете открыть другой индекс и включить кластеризацию .

Это необходимо сделать в конструкторе таблиц, затем щелкнуть правой кнопкой мыши и выбрать Индексы / ключи ... . Сначала найдите существующий кластерный индекс (возможно, первичный ключ) и измените Create as Clustered на No . Затем перейдите к другому индексу и измените Create as Clustered на Yes для этого. Если таблица большая, операция может прерваться, пока вы сохраняете; вы можете обойти это, если SSMS сгенерирует сценарий изменения (щелкните правой кнопкой мыши конструктор после изменения индексов, и вы увидите эту опцию). Затем вы можете запустить этот сценарий в окне запроса без тайм-аута.

Если вы посмотрите на этот сценарий изменения, вы увидите всю работу, которую он выполняет, создавая промежуточные таблицы и переключая ключи; это больно писать вручную. Позвольте SSMS сделать это за вас.

20
ответ дан 1 December 2019 в 19:39
поделиться

Из-за установки средства моделирования данных контролируют несколько сделанных годы назад давно ушедшим разработчиком, у меня была целая база данных, полная таблиц с некластерными индексами на PK и никакими кластерными индексами. Я нашел статью StackOverflow с кодом, который сделал отбрасывания FK, отбросил ограничение PK; повторно добавленный ограничение PK как кластерный индекс и затем повторно добавил FKs: Изменение Первичный ключ от Некластеризованного до Кластеризованного я пересмотрел тот код для слияния, несколько из больших StackOverflow комментирует его; сделанный этим цикл через все таблицы в моем DB (за исключением два, что я заставил код исключить); найденный им создал внешние ключи, которым не доверяют (на sp_Blitz); зафиксированный это и вуаля! код ниже этого, кажется, работает вполне хорошо для добавления кластерных индексов ко всем таблицам "кучи" в дб - если все таблицы в DB имеют поле PKs идентификационных данных кроме исключенных.

/* Script to take tables with a primary key but not a clustered index to being tables
with a primary key and a clustered index on the PK field.  Foreign keys are dropped and recreated.
Much borrowing from a great piece of code from StackOverflow

What if you have a table or two you don't want a clustered index on?
Lines 38 and 39 are for tables you want excluded from the process.  Input your own table names here,
if any.

After it finishes, test the altered db against an unaltered version of the db and check
that it recreated all constraints/FKs.  The only diff should be the clustered index vs non-clustered.

Revisions made by Ed Z 10/2019:
Original script processes one table only - I wrapped it in a loop that processes all heap tables in a database.
Original script prints the commands - this script both prints them and runs them.  If you want to be safe - 
just comment out the 5 exec statements and inspect the printed commands; then run them if you wish.  
Original script creates untrusted foreign keys (per sp_Blitz) - this script creates trusted foreign keys.
*/

SET NOCOUNT ON;

DECLARE @PKTableName VARCHAR(100), 
        @PKName varchar(100),
        @FKName varchar(100),
        @sql varchar(max),
        @PKcolumnName varchar(30),
        @table VARCHAR(100),
        @FKColumnName VARCHAR(100)

SELECT @PKTableName = ''

-- loop thru all the Heap tables; assumes they have a primary key identity field.
WHILE 1 = 1
BEGIN
    select @PKTableName = MIN(so.name) 
    from sys.indexes si inner join sys.objects so on si.object_id = so.object_id
    where so.is_ms_shipped = 0  
    and si.type_desc = 'Heap'
    and so.name not like '<a table name you want excluded>'  -- exclude a couple of tables that lack a unique key
    and so.name not like '<a table name you want excluded>'
    and so.name > @PKTableName

    IF @PKTableName is null
        BREAK

    --initialize
    select @PKName = '', @FKName = '', @PKcolumnName = '', @sql = '', @table = '', @FKColumnName = ''

    IF  EXISTS (SELECT * FROM sys.tables WHERE object_id = OBJECT_ID(N'[dbo].[FKAgainstTableList]'))
    BEGIN 
        DROP TABLE FKAgainstTableList
    END
    --CREATE TABLE FKAgainstTableList (ForeignKey VARCHAR(30),[Table] VARCHAR(30))
    --SET @PKTableName = 'MYTABLE'
    set @PKName = (SELECT name FROM sys.indexes WHERE OBJECT_NAME(object_id) = @PKTableName AND is_primary_key = 1)
    set @PKcolumnName = (SELECT name FROM sys.columns WHERE OBJECT_NAME(object_id) = @PKTableName AND is_identity =1)
    /* OR use, if you are not sure there is only one column in the primary key or for primary keys that are not identity fields:
    SELECT @PKcolumnName=column_name FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE OBJECTPROPERTY(OBJECT_ID(constraint_name), 'IsPrimaryKey') = 1 AND table_name = @PKTableName
    */
    -- PRINT @PKcolumnName  -- debug

     SELECT  OBJECT_NAME(sys.foreign_key_columns.parent_object_id) [Table],sys.columns.name [FKColumnName],sys.foreign_keys.name [FKName] 
        INTO FKAgainstTableList
        FROM sys.foreign_keys INNER JOIN sys.foreign_key_columns 
        ON sys.foreign_keys.object_id = sys.foreign_key_columns.constraint_object_id
        INNER JOIN sys.columns ON sys.columns.object_id = sys.foreign_keys.parent_object_id AND sys.columns.column_id = sys.foreign_key_columns.parent_column_id
        WHERE OBJECT_NAME(sys.foreign_keys.referenced_object_id) = @PKTableName

    DECLARE table_cur1 CURSOR  FOR
        SELECT  * FROM FKAgainstTableList

        PRINT @sql

    -------------------------------Disable constraint on FK Tables
    OPEN table_cur1
    FETCH NEXT FROM table_cur1 INTO @table,@FKColumnName,@FKName
    WHILE   @@FETCH_STATUS = 0
        BEGIN
            SET @sql ='ALTER TABLE '+@table+' DROP CONSTRAINT '+ @FKName
            PRINT @sql
            EXEC(@sql)
            FETCH NEXT FROM table_cur1 INTO @table,@FKColumnName,@FKName
        END
    CLOSE table_cur1
    DEALLOCATE table_cur1
    --------------------------------DROP AND recreate CLUSTERED pk
    IF  EXISTS (SELECT 1 FROM sys.indexes WHERE object_id = OBJECT_ID(@PKTableName) AND name = @PKName)
    BEGIN
        SET @sql = 'ALTER TABLE '+@PKTableName+' DROP CONSTRAINT '+ @PKName
        PRINT @sql
        EXEC(@sql)
    END
    SET @sql = 'ALTER TABLE '+@PKTableName +' ADD  CONSTRAINT '+@PKName+' PRIMARY KEY CLUSTERED ('+@PKcolumnName+' ASC)
    WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]'
    PRINT(@sql)
    EXEC(@sql)
    --------------------------------Enable FK constraints on FK tables.
    DECLARE table_cur2 CURSOR  FOR
        SELECT  * FROM FKAgainstTableList
    OPEN table_cur2
    FETCH NEXT FROM table_cur2 INTO @table,@FKColumnName,@FKName
    WHILE   @@FETCH_STATUS = 0
        BEGIN
            SET @sql = 'ALTER TABLE '+@table+' WITH NOCHECK ADD  CONSTRAINT  '+ @FKName+' FOREIGN KEY(['+@FKColumnName+'])
            REFERENCES ['+@PKTableName+'] (['+@PKcolumnName+'])'
            PRINT(@sql)
            EXEC(@sql)

--          SET @sql = 'ALTER TABLE '+@table+' CHECK CONSTRAINT  '+@FKName  -- this created untrusted foreign keys
            SET @sql = 'ALTER TABLE '+@table+' WITH CHECK CHECK CONSTRAINT  '+@FKName  -- assumes all FK relations are in order
            PRINT(@sql)
            EXEC(@sql)

            FETCH NEXT FROM table_cur2 INTO @table,@FKColumnName,@FKName

             END
    CLOSE table_cur2
    DEALLOCATE table_cur2
    DROP TABLE FKAgainstTableList
END  -- end while loop

SET NOCOUNT OFF

0
ответ дан 1 December 2019 в 19:39
поделиться

Вам также придется отбросить ограничения FK, изменить PK, а затем воссоздать ограничения FK после того, как новый PK будет установлен. Ограничения внешнего ключа требуют, чтобы указанный ключ был уникальным, первичный ключ по определению уникален, поэтому удаление PK не допускается, пока существуют ограничения FK, ссылающиеся на PK.

При переходе на кластерный индекс таблица перезаписывается. Каждый раз, когда я рассматриваю возможность перехода в / из кластерного индекса или изменения кластерного индекса, я бы пересмотрел его необходимость и выбор ключей для кластерного индекса, особенно если он не будет уникальным или увеличивающимся (например, datetime для метки времени) . Помните, что кластеризованный индекс на самом деле не является в первую очередь индексом - это порядок данных на страницах. Вот почему кластеризация по возрастающему ключу помогает с разделением страниц при добавлении данных в таблицу.

1
ответ дан 1 December 2019 в 19:39
поделиться

Вы не можете преобразовать его на месте - вам нужно сначала отбросить ограничение первичного ключа (которое также автоматически отбросит некластеризованный индекс "за" ограничением первичного ключа), а затем воссоздать его как кластерный индекс:

ALTER TABLE dbo.YourTable
DROP CONSTRAINT PK_YourTable

, а затем воссоздайте его как кластеризованный:

ALTER TABLE dbo.YourTable
ADD CONSTRAINT PK_YourTable
PRIMARY KEY CLUSTERED (YourPKField)
9
ответ дан 1 December 2019 в 19:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: