Tenho uma tabela do Access chamada Engagement
com quatro campos:
Emp_id, Year, Week, Act_id
Ele registra quando um funcionário está/estava envolvido em uma atividade. Cada campo é Number e cada um faz parte da chave primária composta. A semântica do aplicativo era que cada entrada (cada Emp_id
- Year
- Week
- Act_id
combinação) deveria ser única. Ou seja, embora um funcionário geralmente tenha diferentes semanas do ano e diferentes atividades, às vezes um funcionário pode se envolver na mesma atividade em diferentes semanas do ano ou até mesmo se envolver em atividades diferentes na mesma semana do ano. Nulos não eram permitidos. Tudo funcionou.
Agora preciso expandir/modificar a semântica para permitir valores ano-semana desconhecidos – ou mais apropriadamente, não divulgados – associados a qualquer atividade de funcionário. Obviamente, tentar inserir uma linha com um ano-semana vazio resulta em "Índice ou chave primária não pode conter um valor nulo". Então eu preciso de uma mudança no design da mesa.
Uma coisa que tentei foi converter o índice de chave primária em um índice não primário desativando Primário (e deixando Único ativado) na janela Índices. Isso evita corretamente registros duplicados onde os valores de Ano-Semana não estão vazios -- mas permite registros duplicados onde Ano-Semana estão vazios.
Por exemplo, usando o índice exclusivo não primário acima, os seguintes dados são permitidos:
Emp_id Year Week Act_id
7 2014 12 31 } Same activity,
7 2015 22 31 } different dates.
7 2015 33 32
7 2015 40 33 } Same dates,
7 2015 40 34 } different activities.
7 2016 2 36
7 38 } Different activities,
7 39 } undisclosed dates.
E nenhuma das seguintes adições são permitidas posteriormente:
Emp_id Year Week Act_id
7 2014 12 31 } Both records are
7 2015 33 32 } duplicates of above.
Até aqui tudo bem (o comportamento corresponde aos requisitos). No entanto, ambas as adições a seguir são permitidas posteriormente, mas não devem ser :
Emp_id Year Week Act_id
7 38 } Both records are
7 39 } duplicates of above.
Por que é que?
Qual é uma boa maneira de contornar esse problema de, por um lado, permitir algo equivalente a nulo para Ano e Semana e, por outro lado, restringir cada Emp_id
- Year
- Week
- Act_id
combinação a ser única?
Posso pensar em duas outras soluções (não testadas):
- Escolha um valor numérico equivalente a nulo, como zero ou -1 e de alguma forma explique isso aos usuários.
- Converta os tipos de campo Ano-Semana de Número para Texto e simplesmente use "" (string vazia) para nulo.
Na sua experiência, qual é uma boa solução para esse tipo de situação?
Estou ciente de O que há de errado com colunas anuláveis em chaves primárias compostas? e valor NULL na chave primária de várias colunas , que explicam certas coisas, mas não fornecem uma solução.
O conselho mais forte que posso dar é manter a chave primária imutável , a menos que seja absolutamente inviável para o seu caso de uso. Você parece estar descrevendo 2 conjuntos de dados diferentes.
User-Actions
tomada durante um determinadoPeriod
(definido pela chave composta de Ano-Semana)User-Actions
tirada durante um desconhecidoPeriod
Minha impressão inicial é que o período desconhecido deve ser um conjunto de dados diferente. Certamente, se você não sabe quando uma ação ocorreu, também não sabe o suficiente para movê-la de seu ambiente de teste para a tabela onde
User-Action-Period
está o identificador exclusivo. Não conheço o MS Access bem o suficiente para comentar sobre as especificidades da chave nula nesse ambiente, mas recomendo definir seu modelo de dados para você mesmo de forma que você saiba se precisa incluir o "equivalente a nulo " registros chaveados na mesma tabela .Pela sua descrição, parece que o problema ocorre ao tentar inserir várias instâncias de um
User-Action
onde oPeriod
ocorrido não é conhecido . Mais uma vez, se você não souber o suficiente para identificar exclusivamente o tempo de confirmação doUser-Action
, é apropriado testar as métricas que você está registrando sobre o mesmoUser-Action
em outro lugar até que você possa identificar adequadamente o relacionamento que issoUser-Action
tem com o outro semelhanteUser-Actions
que ocorreu em outra chave -tempo capazPeriod
.Vejo dois caminhos a seguir:
Se apenas uma ação do usuário com chave de tempo "desconhecida" for permitida
Exemplo: A ação está "pendente" ou "a ser concluída". O usuário estará sempre enviando dados para um período futuro/não concluído.
Solução: escolha seu método favorito de codificar o período de tempo como "equivalente a nulo" e torne as colunas-chave não anuláveis. Você pode continuar armazenando esses conjuntos de dados na mesma tabela
Pegadinha: se o usuário enviar uma ação com um período confirmado enquanto existe um período desconhecido para o mesmo
User-Action
, você pode se deparar com uma situação em que os dados são confirmados em que o período é conhecido, mas outros dados já confirmados por um período desconhecido (que por acaso é o mesmo ) é deixado no estado "período desconhecido" e perdido para esseUser-Action-Time
registro e o usuário não sabe imediatamente o motivo.Múltiplas ações do usuário com chaves de tempo "desconhecidas" são permitidas
Exemplo: os usuários podem identificar exclusivamente o mesmo tipo de ação com um identificador diferente para um período desconhecido .
Solução: Deve-se definir outra forma de chavear os dados (onde o horário em que a ação ocorre é uma métrica do outro registro multichaveado). Permita que os usuários enviem dados para este conjunto de dados armazenado separadamente e mescle-os em seu
User-Action-Time
conjunto de dados de período usando a lógica apropriada. Dá mais trabalho, mas não há como contornar isso se os conjuntos de dados estiverem separados de boa-fé.Você pode usar uma data padrão de algo fora dos limites. por exemplo, na definição da tabela
YEAR INTEGER DEFAULT 1776, WEEK INTEGER DEFAULT 99
Dessa forma, você sabe que tudo é inserido com alguma data. Ele continuará a não permitir que você insira dados duplicados.
Além disso, você pode criar um relatório de exceção com base nesses valores iniciados para alguém, informando que precisa atualizar essa data para a correta.
Esta solução funcionará apenas no Access 2010+ (por exemplo, 2013) e somente se o arquivo for salvo como um accdb (esta solução não funciona para MDBs, mesmo no Access 2010) . Você também pode ter que habilitar macros no documento salvo para que as verificações realmente... você sabe... funcionem.
Configure o índice exclusivo como antes, tendo em mente que ele não será realmente exclusivo se você tiver valores nulos até que o restante desta solução seja colocado em ação. O código SQL para isso seria
Na visualização da folha de dados da tabela, clique em
Table
abaixoTable Tools
na faixa de opções superior. CliqueBefore Change
naBefore Events
seção da faixa de opções.a primeira ação deve ser
A segunda ação deve ser
Isso deve criar um
If..End If
blocoNo meio do
If
bloco, crie outra instrução. Eu acredito que o número real do erro não importa.Em seguida, salve e feche a
Before Change
chamada de macro.Agora você precisará criar o código VBA. Clique na
Database Tools
fita. NaMacro
seção, cliqueVisual Basic
No painel superior esquerdo que se abre, você deve ver uma árvore de arquivos. Um desses objetos deve ser uma entrada para seu banco de dados. Clique com o botão direito do mouse no nome e selecione
Insert
->Module
. Em seguida, na janela principal que se abre, cole o seguinte.Salve este módulo e o código VBA em geral. Chamei meu módulo de "Validador", mas não importa como você o chama... talvez seja necessário também compilar o módulo no
Debug
menu.Consulte o histórico de edição deste tópico para uma tentativa anterior de solução. Bah.
A primeira parte da solução foi - como eu disse na pergunta - converter o índice de chave primária em um índice não primário desativando Primário (e deixando Único ativado) na janela Índices.
Um meio alternativo para criar esse índice, conforme sugerido por @mpag, é via SQL:
A segunda parte da solução foi perceber que eu já estava utilizando uma consulta SQL parametrizada,
get_engagement
, no código dos meus formulários onde o usuário insere ou apaga os dados do engajamento:E, em seguida, modifique-o para que ele possa lidar com os casos nulos:
Foi a ideia de @mpag para o código de validação que me fez pensar no caminho certo.
Você pode fazer muitas coisas, mas inteiro e texto não são classificados da mesma forma.
O texto interromperia uma pesquisa na semana > 6.
No texto 10, 11, 12 não são > 6.
Eu usaria 0 para nenhuma data
Não que o tamanho seja grande coisa, mas você usaria tinyint para semana e smallint para ano
um varchar é do tamanho de um smallint