The best solution would be not to replicate the information to the table ItemContrato, after all this would be redundancy. The best way would be to fetch the information directly from the source table:
SELECT ic.id,
ic.contratoid,
ic.nome,
c.ativo
FROM ItemContrato ic WITH(NOLOCK)
INNER JOIN Contrato c WITH(NOLOCK) ON c.id = ic.contratoid;
But if you really want to replicate the information, I suggest using a trigger to make the change:
IF OBJECT_ID('tgr_contrato_aiu', 'TR') IS NULL
BEGIN
EXEC('CREATE TRIGGER tgr_contrato_aiu ON Contrato FOR INSERT, UPDATE AS BEGIN SELECT 1 END');
END;
GO
ALTER TRIGGER tgr_contrato_aiu
ON Contrato
FOR INSERT, UPDATE
AS
BEGIN
DECLARE @nome_tabela VARCHAR(100),
@query VARCHAR(MAX);
SET NOCOUNT ON;
-- Pega o nome da tabela para a qual a TRIGGER é executada
SELECT @nome_tabela = OBJECT_NAME(o.parent_object_id)
FROM sys.objects o WITH(NOLOCK)
WHERE o.name = OBJECT_NAME(@@PROCID);
SELECT @query = isnull(@query + CHAR(10), '') + 'UPDATE f' + CHAR(10) +
' SET f.ativo = ' + + CAST(i.ativo AS VARCHAR) + CHAR(10) +
' FROM ' + tf.name + ' f' + CHAR(10) +
' WHERE f.' + c.name + ' = ' + CAST(i.id AS VARCHAR) + ';' + CHAR(10)
FROM sys.tables t WITH(NOLOCK)
INNER JOIN sys.foreign_keys fk WITH(NOLOCK) ON fk.referenced_object_id = t.object_id
INNER JOIN sys.tables tf WITH(NOLOCK) ON tf.object_id = fk.parent_object_id
INNER JOIN sys.foreign_key_columns fkc WITH(NOLOCK) ON fkc.constraint_object_id = fk.object_id
INNER JOIN sys.columns c WITH(NOLOCK) ON c.object_id = fkc.parent_object_id
AND c.column_id = fkc.parent_column_id
CROSS JOIN inserted i
INNER JOIN deleted d ON d.id = i.id
WHERE t.name = @nome_tabela
-- Somente se mudar algo na coluna "Ativo"
AND i.ativo <> d.ativo
-- Garante que exista a coluna "Ativo" na tabela filha
AND EXISTS(SELECT 1
FROM sys.columns cf WITH(NOLOCK)
WHERE cf.object_id = fkc.parent_object_id
AND cf.name = 'ATIVO');
IF @query IS NOT NULL
BEGIN
-- PRINT @query;
EXEC(@query);
END;
END;
GO
To trigger above searches all foreign keys related to table Contrato, together with your columns and dynamically updates the column Ativo according to the value that was entered. Note that the trigger is executed afterward of each INSERT and UPDATE.
I also suggest the creation of a trigger to the table ItemContrato which will ensure that when a new record is entered, it is already with column information Ativo in the way that is expected:
IF OBJECT_ID('tgr_itemcontrato_ai', 'TR') IS NULL
BEGIN
EXEC('CREATE TRIGGER tgr_itemcontrato_ai ON ItemContrato FOR INSERT AS BEGIN SELECT 1 END');
END;
GO
ALTER TRIGGER tgr_itemcontrato_ai
ON ItemContrato
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
UPDATE ic
SET ic.ativo = c.ativo
FROM ItemContrato ic
INNER JOIN inserted i ON i.id = ic.id
INNER JOIN Contrato c ON c.id = ic.contratoid;
END;
GO
For testing I used the following inclusions in the tables:
SET NOCOUNT ON;
DECLARE @ultimo_codigo INT;
-- Inserção dos dados para teste
-- Contrato "X"
INSERT INTO Contrato(nome, ativo)
VALUES('X', 1);
SET @ultimo_codigo = SCOPE_IDENTITY();
INSERT INTO ItemContrato(contratoid, nome)
VALUES(@ultimo_codigo, 'A'),
(@ultimo_codigo, 'B'),
(@ultimo_codigo, 'C');
-- Contrato "Y"
INSERT INTO Contrato(nome, ativo)
VALUES('Y', 1);
SET @ultimo_codigo = SCOPE_IDENTITY();
INSERT INTO ItemContrato(contratoid, nome)
VALUES(@ultimo_codigo, 'D'),
(@ultimo_codigo, 'E'),
(@ultimo_codigo, 'F'),
(@ultimo_codigo, 'G');
-- Contrato "Z"
INSERT INTO Contrato(nome, ativo)
VALUES('Z', 0);
SET @ultimo_codigo = SCOPE_IDENTITY();
INSERT INTO ItemContrato(contratoid, nome)
VALUES(@ultimo_codigo, 'H'),
(@ultimo_codigo, 'I'),
(@ultimo_codigo, 'J');
Getting the results right after insertion:
╔════╦══════╦═══════╗
║ Id ║ Nome ║ Ativo ║
╠════╬══════╬═══════╣
║ 1 ║ X ║ 1 ║
║ 2 ║ Y ║ 1 ║
║ 3 ║ Z ║ 0 ║
╚════╩══════╩═══════╝
╔════╦════════════╦══════╦═══════╗
║ Id ║ ContratoId ║ Nome ║ Ativo ║
╠════╬════════════╬══════╬═══════╣
║ 1 ║ 1 ║ A ║ 1 ║
║ 2 ║ 1 ║ B ║ 1 ║
║ 3 ║ 1 ║ C ║ 1 ║
║ 4 ║ 2 ║ D ║ 1 ║
║ 5 ║ 2 ║ E ║ 1 ║
║ 6 ║ 2 ║ F ║ 1 ║
║ 7 ║ 2 ║ G ║ 1 ║
║ 8 ║ 3 ║ H ║ 0 ║
║ 9 ║ 3 ║ I ║ 0 ║
║10 ║ 3 ║ J ║ 0 ║
╚════╩════════════╩══════╩═══════╝
Soon after I performed the following UPDATE:
-- Atualização dos contratos "X" e "Z"
UPDATE c
SET c.ativo = 0
FROM Contrato c
WHERE c.nome = 'X';
UPDATE c
SET c.ativo = 1
FROM Contrato c
WHERE c.nome = 'Z';
And the records after the UPDATE sane:
╔════╦══════╦═══════╗
║ Id ║ Nome ║ Ativo ║
╠════╬══════╬═══════╣
║ 1 ║ X ║ 0 ║
║ 2 ║ Y ║ 1 ║
║ 3 ║ Z ║ 1 ║
╚════╩══════╩═══════╝
╔════╦════════════╦══════╦═══════╗
║ Id ║ ContratoId ║ Nome ║ Ativo ║
╠════╬════════════╬══════╬═══════╣
║ 1 ║ 1 ║ A ║ 0 ║
║ 2 ║ 1 ║ B ║ 0 ║
║ 3 ║ 1 ║ C ║ 0 ║
║ 4 ║ 2 ║ D ║ 1 ║
║ 5 ║ 2 ║ E ║ 1 ║
║ 6 ║ 2 ║ F ║ 1 ║
║ 7 ║ 2 ║ G ║ 1 ║
║ 8 ║ 3 ║ H ║ 1 ║
║ 9 ║ 3 ║ I ║ 1 ║
║10 ║ 3 ║ J ║ 1 ║
╚════╩════════════╩══════╩═══════╝
In the case of the first query the results before the UPDATE to the table ItemContrato were:
╔════╦════════════╦══════╦═══════╗
║ Id ║ ContratoId ║ Nome ║ Ativo ║
╠════╬════════════╬══════╬═══════╣
║ 1 ║ 1 ║ A ║ 1 ║
║ 2 ║ 1 ║ B ║ 1 ║
║ 3 ║ 1 ║ C ║ 1 ║
║ 4 ║ 2 ║ D ║ 1 ║
║ 5 ║ 2 ║ E ║ 1 ║
║ 6 ║ 2 ║ F ║ 1 ║
║ 7 ║ 2 ║ G ║ 1 ║
║ 8 ║ 3 ║ H ║ 0 ║
║ 9 ║ 3 ║ I ║ 0 ║
║10 ║ 3 ║ J ║ 0 ║
╚════╩════════════╩══════╩═══════╝
And after the UPDATE:
╔════╦════════════╦══════╦═══════╗
║ Id ║ ContratoId ║ Nome ║ Ativo ║
╠════╬════════════╬══════╬═══════╣
║ 1 ║ 1 ║ A ║ 0 ║
║ 2 ║ 1 ║ B ║ 0 ║
║ 3 ║ 1 ║ C ║ 0 ║
║ 4 ║ 2 ║ D ║ 1 ║
║ 5 ║ 2 ║ E ║ 1 ║
║ 6 ║ 2 ║ F ║ 1 ║
║ 7 ║ 2 ║ G ║ 1 ║
║ 8 ║ 3 ║ H ║ 1 ║
║ 9 ║ 3 ║ I ║ 1 ║
║10 ║ 3 ║ J ║ 1 ║
╚════╩════════════╩══════╩═══════╝
Note that the results are the same, thus proving that for this case the use of JOIN to avoid redundancy is the best option.
I don’t understand exactly where I should do this implementation, I have little experience with this structure I’m working on. But yes, I have a Context
– Evandro Silva
In the DDD this goes inside your persistence layer, it can be a Repository Pattern that you are using, or another pattern.
– Gabriel Coletta
In my case I have a repository with the specialized methods of the Contract entity. I must override my Update method?
– Evandro Silva
No, because this implementation is a specific scenario where your update will add a zero Asset if you want to overwrite your base. Update(), you need to change my example.
– Gabriel Coletta
In fact it is a scenario that must occur in all situations. My intention is, when making an inactive record, all "children" of this record also become inactive. I was told I could do with Trigger too, but I still don’t know what the best option would be
– Evandro Silva
Trigger could also be used for a similar result.
– Gabriel Coletta