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 / 198860
Accepted
PicoDeGallo
PicoDeGallo
Asked: 2018-02-27 15:20:07 +0800 CST2018-02-27 15:20:07 +0800 CST 2018-02-27 15:20:07 +0800 CST

Alterando funções de partição dinamicamente usando gatilhos

  • 772

Estou querendo utilizar o particionamento com base em um [TenantId](e posteriormente em conjunto com intervalos de datas). Em vez de precisar inserir manualmente o valor mais recente dentro do PARTITION FUNCTION, pensei em criar um TRIGGER AFTER INSERTpara puxar o [TenantId]valor e ALTER PARTITION FUNCTIONadicioná-lo ao SPLIT RANGE. No entanto, estou me deparando com um erro inesperado:

Não é possível executar ALTER PARTITION FUNCTION na/usando a tabela 'Tenant', pois a tabela é a tabela de destino ou parte das ações em cascata de um gatilho atualmente em execução.

Primeiro, estou criando o PARTITION FUNCTION [PF_Tenant_Isolation]and PARTITION SCHEME [PS_Tenant_Isolation]para particionamento no [TenantId].

CREATE PARTITION FUNCTION [PF_Tenant_Isolation] ([int])
    AS RANGE LEFT FOR VALUES (1);
GO

CREATE PARTITION SCHEME [PS_Tenant_Isolation]
    AS PARTITION [PF_Tenant_Isolation]
    ALL TO ([Auth]);
GO

Depois disso, estou criando a [Tenant]tabela no esquema de partição recém-criado.

IF OBJECT_ID('[Auth].[Tenant]', 'U') IS NULL
BEGIN
    CREATE TABLE [Auth].[Tenant] (
        [TenantId] [int] IDENTITY(1,1)
        ,[TenantActive] [bit] NOT NULL CONSTRAINT [DF_Tenant_TenantActive] DEFAULT 1
        ,[TenantName] [varchar](256) NOT NULL
        ,CONSTRAINT [PK_Tenant_TenantId] PRIMARY KEY CLUSTERED ([TenantId] ASC)
    ) ON [PS_Tenant_Isolation]([TenantId]);
END

Eu semeio o primeiro valor antes de criar o gatilho.

INSERT INTO [Auth].[Tenant]
VALUES (1,'Partition Trigger Test A');

Eu crio o gatilho na tabela [Tenant].

CREATE TRIGGER [TR_Tenant_Isolation] ON [Auth].[Tenant]
AFTER INSERT
AS
BEGIN
    DECLARE @MaxInsertedId int
    SET @MaxInsertedId = (SELECT MAX([TenantId]) FROM inserted)

    ALTER PARTITION SCHEME [PS_Tenant_Isolation]
        NEXT USED [Auth];

    ALTER PARTITION FUNCTION [PF_Tenant_Isolation]()
        SPLIT RANGE (@MaxInsertedId);
END

Eu sigo isso com a tentativa de inserir o segundo [Tenant]valor.

INSERT INTO [Auth].[Tenant]
VALUES (1,'Partition Trigger Test B');

É quando o erro descrito acima aparece. Com base no erro em si, lendo os argumentos do Technet , entendo que o problema está na utilização do AFTER INSERT. Como a ação de partição da transação depende da utilização do valor de intervalo dentro da função de partição, a ALTER PARTITION SCHEMEfalha e, portanto, toda a transação também.

AFTER especifica que o gatilho DML é acionado somente quando todas as operações especificadas na instrução SQL de gatilho forem executadas com êxito. Todas as ações referenciais em cascata e verificações de restrição também devem ser bem-sucedidas antes que esse gatilho seja acionado.

Eu olhei em INSTEAD OF INSERT , mas não tive nenhum sucesso. O gatilho é acionado uma vez e atualiza o SPLIT RANGEcom um valor de 0 (convertido implicitamente de NULL). Acredito que isso se deva ao fato de IDENTITYnão estar devidamente capturado no escopo da transação.

CREATE TRIGGER [TR_Tenant_Isolation] ON [Auth].[Tenant]
INSTEAD OF INSERT
AS
BEGIN
    DECLARE @MaxInsertedId int
    SET @MaxInsertedId =  (SELECT [TenantId] FROM inserted)

    ALTER PARTITION SCHEME [PS_Tenant_Isolation]
        NEXT USED [Auth];

    ALTER PARTITION FUNCTION [PF_Tenant_Isolation]()
        SPLIT RANGE (@MaxInsertedId);

    INSERT INTO [Auth].[Tenant] ([TenantActive], [TenantName])
    SELECT [TenantActive], [TenantName]
    FROM inserted;
END

As inserções de linhas subsequentes [Tenant]produzem um erro adicional devido à tentativa de inserir 0 (NULL).

Valores de limite de intervalo duplicados não são permitidos na lista de valores de limite de função de partição. O valor de limite que está sendo adicionado já está presente no ordinal 1 da lista de valores de limite.

Como posso contornar isso? Preciso definir explicitamente o IDENTITYvalor de [TenantId]em conjunto com INSTEAD OF INSERT? Novas inserções em [Tenant]serão bastante esporádicas e mínimas, mas [TenantId]serão uma chave restritiva em outras tabelas. É por isso que decidi investigar esse método de implementação para alterar dinamicamente a função de partição.

sql-server database-design
  • 1 1 respostas
  • 2377 Views

1 respostas

  • Voted
  1. Best Answer
    Dan Guzman
    2018-02-28T05:11:22+08:002018-02-28T05:11:22+08:00

    O erro enigmático no AFTERgatilho é devido à execução de um DDL na tabela de destino do gatilho. Com o INSTEAD OFgatilho, você precisaria executar o INSERTpara obter o valor atribuído IDENTITYe, em seguida, dividir a função de partição. No entanto, você provavelmente não deseja usar IDENTITY aqui de qualquer maneira, pois eles podem ter lacunas que às vezes são grandes e resultam em uma lista de limites de partição desordenada.

    Abaixo está um exemplo que abandona a IDENTITY e usa uma função RANGE RIGHT, que acredito ser mais natural para limites de partição incrementais. Esta versão valida exatamente uma linha inserida, mas pode ser estendida para lidar com inserções de várias linhas, se necessário. Seu caso de uso, como eu o entendo, sugere apenas inserções singleton raras.

    --start with no partition boundaries
    CREATE PARTITION FUNCTION [PF_Tenant_Isolation] ([int])
        AS RANGE RIGHT FOR VALUES ();
    GO
    
    CREATE PARTITION SCHEME [PS_Tenant_Isolation]
        AS PARTITION [PF_Tenant_Isolation]
        ALL TO ([Auth]);
    GO
    
    CREATE TABLE [Auth].[Tenant] (
         [TenantId] [int] NOT NULL
        ,[TenantActive] [bit] NOT NULL CONSTRAINT [DF_Tenant_TenantActive] DEFAULT 1
        ,[TenantName] [varchar](256) NOT NULL
        ,CONSTRAINT [PK_Tenant_TenantId] PRIMARY KEY CLUSTERED ([TenantId] ASC)
    ) ON [PS_Tenant_Isolation]([TenantId]);
    GO
    
    CREATE TRIGGER [TR_Tenant_Isolation] ON [Auth].[Tenant]
    INSTEAD OF INSERT
    AS
    DECLARE @TenantId int;
    BEGIN TRY
    
        --Get next TenantId and exclusively lock table to prevent deadlocking during DDL.
        --If other tables are partitoned via this function, add code to get exclusive locks on those too.
        SELECT TOP(1) @TenantId = COALESCE(MAX(TenantId),0) + 1 FROM [Auth].[Tenant] WITH(TABLOCKX);
    
        INSERT INTO [Auth].[Tenant] ([TenantId], [TenantActive], [TenantName])
            SELECT @TenantId, [TenantActive], [TenantName]
            FROM inserted;
    
        IF @@ROWCOUNT <> 1
        BEGIN
            RAISERROR('Exactly one row must be inserted into Auth.Tenant at a time',16,1);
        END;
    
        ALTER PARTITION SCHEME [PS_Tenant_Isolation]
            NEXT USED [Auth];
    
        ALTER PARTITION FUNCTION [PF_Tenant_Isolation]()
            SPLIT RANGE (@TenantId);
    
    END TRY
    BEGIN CATCH;
        THROW;
    END CATCH;
    GO
    
    INSERT INTO [Auth].[Tenant]([TenantActive], [TenantName])
    VALUES (1,'Partition Trigger Test A');
    GO
    

    EDITAR:

    Eu vejo sua notação, mas dado que as consultas serão lidas de [inquilino], o oposto não aconteceria onde isso realmente causaria impasses?

    O bloqueio X granular do curso na tabela Tenant aguardará (ser bloqueado por) outra atividade simultânea na tabela para ser concluído e, uma vez concedido, bloqueará outra atividade na tabela. Esse bloqueio evitará deadlocks na tabela Tenant durante a operação DDL na transação do acionador. A duração do SPLIT em si será rápida, pois as linhas não são movidas entre as partições. A duração do bloqueio antes que o bloqueio inicial do bloco X seja concedido dependerá de quanto tempo as outras consultas são executadas.

    No caso de várias tabelas (ou seja, tabelas relacionadas particionadas por esquemas baseados na mesma função), os deadlocks ainda podem ocorrer se a ordem de bloqueio no gatilho for diferente daquela de outra atividade. Um bloqueio exclusivo nessas tabelas também no gatilho pode apenas mitigar a probabilidade de deadlocks nesse caso. Por exemplo, se você tiver uma consulta SELECT que une Tenant e TenantDetails, ambos particionados de forma semelhante, pode ocorrer um deadlock se a consulta adquirir bloqueios dessas tabelas na ordem inversa do gatilho.

    Além disso, entendo que com esquemas de partição você normalmente deseja deixar partições nos limites esquerdo e direito que estão "vazios" para a comutação adequada.

    As partições vazias são uma consideração para SPLITe MERGEmas não para SWITCH. Com o SPLITno acionador, a partição dividida está sempre vazia, portanto, nenhuma movimentação de dados cara é necessária para estar em conformidade com a nova especificação de limite.

    A melhor prática geral é MERGEdelimitar quando ambas as partições adjacentes estiverem vazias. Dito isso, você pode sill MERGEsem movimento de linha, desde que a partição que contém o limite (uma à direita com uma RANGE RIGHTfunção) esteja vazia.

    • 3

relate perguntas

  • 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?

  • Quais são algumas maneiras de implementar um relacionamento muitos-para-muitos em um data warehouse?

  • 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