AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 128433
Accepted
Hikari
Hikari
Asked: 2016-02-06 11:03:46 +0800 CST2016-02-06 11:03:46 +0800 CST 2016-02-06 11:03:46 +0800 CST

Adicionar autoincremento ao PK existente

  • 772

Criei uma tabela em um BD que já existe em outro BD. Ele foi inicialmente preenchido com os dados antigos do banco de dados. O PK da tabela tinha que receber os valores que já existem nesses registros, então não poderia ser autoincremento.

Agora preciso que a nova tabela tenha seu PK como autoincremento. Mas como posso fazer isso depois que o PK já existe e tem dados?

sql-server primary-key
  • 5 5 respostas
  • 75958 Views

5 respostas

  • Voted
  1. Best Answer
    Daniel Hutmacher
    2016-02-06T13:21:24+08:002016-02-06T13:21:24+08:00

    A maneira como eu entendo sua pergunta é que você tem uma tabela existente com uma coluna que até agora foi preenchida com valores manuais e agora você deseja (1) transformar essa coluna em uma IDENTITYcoluna e (2) certificar-se de que o IDENTITYinício do valor mais recente nas linhas existentes.

    Primeiro, alguns dados de teste para brincar:

    CREATE TABLE dbo.ident_test (
        id    int NOT NULL,
        xyz   varchar(10) NOT NULL,
        CONSTRAINT PK_ident_test PRIMARY KEY CLUSTERED (id)
    );
    
    INSERT INTO dbo.ident_test (id, xyz)
    VALUES (1, 'test'),
           (2, 'test'),
           (5, 'test'),
           (6, 'test'),
           (10, 'test'),
           (18, 'test'),
           (19, 'test'),
           (20, 'test');
    

    O objetivo é tornar a coluna de chave primária da tabela, id, uma IDENTITYcoluna que começará em 21 para o próximo registro que for inserido. Para este exemplo, a coluna xyzrepresenta todas as outras colunas da tabela.

    Antes de fazer qualquer coisa, por favor, leia os avisos na parte inferior deste post.

    Primeiro, caso algo dê errado:

    BEGIN TRANSACTION;
    

    Agora, vamos adicionar uma coluna de trabalho temporária id_tempe definir essa coluna para os idvalores da coluna existente:

    ALTER TABLE dbo.ident_test ADD id_temp int NULL;
    UPDATE dbo.ident_test SET id_temp=id;
    

    Em seguida, precisamos descartar a idcoluna existente (você não pode simplesmente "adicionar" IDENTITYa uma coluna existente, você precisa criar a coluna como um IDENTITY). A chave primária também tem que ir, porque a coluna depende dela.

    ALTER TABLE dbo.ident_test DROP CONSTRAINT PK_ident_test;
    ALTER TABLE dbo.ident_test DROP COLUMN id;
    

    ... e adicione a coluna novamente, desta vez como um IDENTITY, junto com a chave primária:

    ALTER TABLE dbo.ident_test ADD id int IDENTITY(1, 1) NOT NULL;
    ALTER TABLE dbo.ident_test ADD CONSTRAINT PK_ident_test PRIMARY KEY CLUSTERED (id);
    

    Aqui é onde fica interessante. Você pode habilitar IDENTITY_INSERTna tabela, o que significa que você pode definir manualmente os valores de uma IDENTITYcoluna ao inserir novas linhas (mas não atualizando as linhas existentes).

    SET IDENTITY_INSERT dbo.ident_test ON;
    

    Com esse conjunto, DELETEtodas as linhas da tabela, mas as linhas que você está excluindo, estão OUTPUTna mesma tabela - mas com valores específicos para a idcoluna (da coluna de backup).

    DELETE FROM dbo.ident_test
    OUTPUT deleted.id_temp AS id, deleted.xyz
    INTO dbo.ident_test (id, xyz);
    

    Feito isso, desligue IDENTITY_INSERTnovamente.

    SET IDENTITY_INSERT dbo.ident_test OFF;
    

    Solte a coluna temporária que adicionamos:

    ALTER TABLE dbo.ident_test DROP COLUMN id_temp;
    

    E, finalmente, resemente a IDENTITYcoluna, para que os próximos registros idsejam retomados após o maior número existente na idcoluna:

    DECLARE @maxid int;
    SELECT @maxid=MAX(id) FROM dbo.ident_test;
    DBCC CHECKIDENT ("dbo.ident_test", RESEED, @maxid)
    

    Verificando a tabela de exemplo, o idnúmero mais alto é 20.

    SELECT * FROM dbo.ident_test;
    

    Adicione outra linha e verifique seu novo IDENTITY:

    INSERT INTO dbo.ident_test (xyz) VALUES ('New row');
    SELECT * FROM dbo.ident_test;
    

    No exemplo, a nova linha terá id=21. Por fim, se estiver satisfeito, confirme a transação:

    COMMIT TRANSACTION;
    

    Importante

    Esta não é uma operação trivial e traz alguns riscos dos quais você deve estar ciente.

    • Faça isso em um ambiente de teste dedicado. Tenha backups. :)

    • Eu gosto de usar BEGIN/COMMIT TRANSACTIONporque evita que outros processos mexam com a tabela enquanto você está no meio de alterá-la, e dá a possibilidade de reverter tudo se algo der errado. No entanto, qualquer outro processo que tente acessar sua mesa antes de você confirmar sua transação acabará esperando. Isso pode ser muito ruim se você tiver uma tabela grande e/ou estiver em um ambiente de produção.

    • OUTPUT .. INTOnão funcionará se sua tabela de destino tiver restrições de chave estrangeira ou qualquer um dos vários outros recursos que não consigo lembrar de cabeça. Em vez disso, você pode descarregar os dados em uma tabela temporária e inseri-los novamente na tabela original. Você pode usar a alternância de partição (mesmo que não use partições).

    • Execute essas instruções uma a uma, não como um lote ou em um procedimento armazenado.

    • Tente pensar em outras coisas que podem depender da idcoluna que você está descartando e recriando. Quaisquer índices terão que ser descartados e recriados (como fizemos com a chave primária). Lembre-se de criar scripts de cada índice e restrição que você precisará recriar antecipadamente.

    • Desabilite qualquer INSERTe DELETEacionadores na mesa.

    Se recriar a tabela for uma opção:

    Se recriar a tabela é uma opção para você, tudo é muito mais simples:

    • Crie a tabela vazia, com a idcoluna como IDENTITY,
    • Colocado IDENTITY_INSERT ONpara a mesa,
    • Preencha a tabela,
    • Definir IDENTITY_INSERT OFF, e
    • Resemeie a identidade.
    • 17
  2. Julien Vavasseur
    2016-02-07T06:23:08+08:002016-02-07T06:23:08+08:00

    Usar UPDATE, DELETE ou INSERT para mover dados pode levar muito tempo e usar recursos (IO) em dados e arquivos/discos de log. É possível evitar o preenchimento do log de transações com potencialmente muitos registros ao trabalhar em uma grande tabela: Usando a troca de partição, apenas os metadados são alterados.

    Não há movimentação de dados envolvida e, portanto, isso é realizado muito rapidamente (quase instantâneo).

    Tabela de amostra

    A questão não mostra a tabela original DDL. O seguinte DDL será usado como exemplo nesta resposta:

    CREATE TABLE dbo.idT(
        id int not null
        , uid uniqueidentifier not null
        , name varchar(50)
    );
    ALTER TABLE dbo.idT ADD CONSTRAINT PK_idT PRIMARY KEY CLUSTERED(id);
    

    Meia dúzia de IDs aleatórios fictícios de 0 a 15 são adicionados com esta consulta:

    WITH ids(n) AS(
        SELECT x1.n+x2.n*4
        FROM (values(0), (3)) as x1(n)
        CROSS JOIN (values(0), (2), (3)) as x2(n)
    )
    INSERT INTO idt(id, uid, name)
    SELECT n, NEWID(), NEWID() 
    FROM ids
    

    Dados de exemplo emIdT

    id  uid                                     name
    0   65533096-5007-43EA-88AD-D6776B3B94FA    6A69D4F2-D682-4168-A92F-4CD2E2DBC21D
    3   CE87F1ED-BE1A-4F2D-8D62-E1ECA822D35B    AF0524D9-0DBB-41E1-883B-003CB4E4F012
    8   34A1DBFD-4F92-4F34-9F04-4CDC824AB15A    02B4BDA4-D515-4262-9031-0BE496AC24CE
    11  51606C95-9DE8-4C30-B23B-F915EEA41156    93258103-9C22-4F9C-85CF-712ED0FB3CE6
    12  CEC80431-0513-4751-A250-0EB3390DACAB    2DA6B8AF-3EBC-42B3-A76C-028716E24661
    15  5037EA83-286F-4EBC-AD7C-E237B570C1FF    095E51E9-8C38-4104-858F-D14AA810A550
    

    Nova mesa comIDENTITY(0, 1)

    O único problema idTé a falta do IDENTITY(0, 1)imóvel no id. Uma nova tabela com uma estrutura semelhante e IDENTITY(0, 1)é criada:

    CREATE TABLE dbo.idT_Switch(
        id int identity(0, 1) not null
        , uid uniqueidentifier not null
        , name varchar(50)
    );
    ALTER TABLE dbo.idT_Switch ADD CONSTRAINT PK_idT_Switch PRIMARY KEY CLUSTERED(id);
    

    Além de IDENTITY(0, 1), idT_Switché idêntico a idT.

    Chaves estrangeiras

    As chaves estrangeiras idTdevem ser removidas para permitir que essa técnica seja usada.

    Interruptor de partição

    As tabelas idTe têm uma estrutura compatível. Em vez de idT_Switchusar instruções e para mover linhas de ou para si , pode ser usado:DELETEUPDATEINSERTidTidT_SwitchidTALTER TABLE ... SWITCH

    ALTER TABLE dbo.idT
    SWITCH TO dbo.idT_Switch;
    

    A única 'partição' de PK_idT(toda a tabela) é movida para PK_idT_Switch(e vice-versa). idTagora contém 0 linhas e idT_Switchcontém 6 linhas.

    Você pode encontrar a lista completa de requisitos de compatibilidade de origem e destino aqui:

    Transferindo dados com eficiência usando a alternância de partição

    Observe que esse uso de SWITCHnão requer Enterprise Edition, pois não há particionamento explícito. Uma tabela não particionada é considerada uma tabela com uma única partição do SQL Server 2005 em diante.

    SubstituiridT

    idTagora está vazio e inútil e pode ser descartado:

    DROP TABLE idT;
    

    idT_Switchpode ser renomeado e substituirá a idTtabela antiga:

    EXECUTE sys.sp_rename
        @objname = N'dbo.idT_Switch',
        @newname = N'idT', -- note lack of schema prefix
        @objtype = 'OBJECT';
    

    Chaves estrangeiras

    As chaves estrangeiras podem ser adicionadas novamente à nova idTtabela. Qualquer outra coisa removida anteriormente idTpara tornar as tabelas compatíveis para comutação também precisará ser refeita.

    Resemente

    SELECT IDENT_CURRENT( 'dbo.idT');
    

    Este comando retorna 0. A tabela idT contém 6 linhas com MAX(id) = 15. DBCC CHECKIDENT ( table_name ) pode ser usado:

    DBCC CHECKIDENT ('dbo.idT');
    

    Como 15 é maior que 0, ele será propagado automaticamente sem procurar por MAX(id):

    Se o valor de identidade atual de uma tabela for menor que o valor máximo de identidade armazenado na coluna de identidade, ele será redefinido usando o valor máximo na coluna de identidade. Consulte a seção 'Exceções' a seguir.

    IDENT_CURRENT agora retorna 15 .

    Testar e adicionar dados

    Uma declaração simples INSERT:

    INSERT INTO idT(uid, name) SELECT NEWID(), NEWID();
    

    Adiciona esta linha:

    id  uid                                     name
    16  B395D692-5D7B-4DFA-9971-A1497B8357A1    FF210D9E-4027-479C-B5D8-057E77FAF378
    

    A idcoluna agora está usando a identidade e o valor recém-inserido é de fato 16 (15+1).

    Mais Informações

    Há uma pergunta e resposta relacionadas com mais informações sobre a SWITCHtécnica aqui:

    Por que a remoção da propriedade Identity em uma coluna não é compatível

    • 5
  3. Tom V
    2016-02-06T11:09:39+08:002016-02-06T11:09:39+08:00

    Se você quiser começar com um novo valor de identidade, precisará propagar novamente sua identidade. Dê uma olhada na documentação paraCHECKIDENT

    DBCC CHECKIDENT (yourtable, reseed, starting point)
    
    • 4
  4. user4321
    2019-04-24T12:53:08+08:002019-04-24T12:53:08+08:00

    ATIVAR e DESATIVAR IDENTITY_INSERT

    Se sua tabela for TABLE_A então

    1. CREATE TABLE TABLE_B semelhante a TABLE_A com a coluna de identidade
    2. SET IDENTITY_INSERT TABLE_B ON
    3. INSERIR em TABLE_B de TABLE_A
    4. SET IDENTITY_INSERT TABLE_B OFF
    5. DROP TABLE TABLE_A e renomeie a tabela B Exec sp_rename 'TABLE_B', 'TABLE_A'
    • 0
  5. CA Martin
    2021-03-04T18:53:12+08:002021-03-04T18:53:12+08:00

    Se você tem uma situação como eu, então você pode fazer isso muito mais fácil

    Etapa 1: obter um bom conjunto de dados, incluindo os valores do campo de chave (no meu caso, o problema foi que a chave primária não pôde ser sincronizada corretamente com duas outras tabelas)

    Etapa 2: use a capacidade do SSMS para criar uma tabela de descarte e criação da que você precisa criar. Além disso, certifique-se de que a tabela NOVA tenha uma chave de identidade primária adicionada

    Nota:
    Isso foi necessário para mim, pois usei os dados bons com uma chave primária semeada, que era a chave primária correta incrementada, mas não consegui mais incrementar automaticamente esse campo

    Etapa 3: use a seguinte consulta para inserir o incremento idêntico do PK

    • INSERT INTO [dbo].[fact_table2]
          ([dim_order_id]
          ,[dim_inventory_id]
          ,[primary_location_velocity_code]
          ,[profit_ordered]
          ,[fill_rate]
          ,[profit_unfilled]
          ,[dollar_weighted_fill_rate]
          ,[missed_sales])
      
       SELECT [dim_order_id]
          ,[dim_inventory_id]
          ,[primary_location_velocity_code]
          ,[profit_ordered]
          ,[fill_rate]
          ,[profit_unfilled]
          ,[dollar_weighted_fill_rate]
          ,[missed_sales]
          from
          [dbo].[fact_table1]
          order by
          [fact_table1_id] asc
      

    Etapa 4: testar se as tabelas são idênticas

    • select top 10 *
      from [dbo].[fact_table1]
      where
      profit_unfilled > 0
         order by
         [fact_table1_id] desc
      
    • select top 10 *
      from [dbo].[fact_table2]
      where
      profit_unfilled > 0
         order by
         [fact_table1_id] desc
      

    Último passo: Observe que a instrução insert não estava usando o campo PK.... Ele foi usado apenas para classificar os dados corretamente para a inserção :)

    • 0

relate perguntas

  • Chaves primárias de caractere x número inteiro

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve