Estou no processo de definição de filtros em uma publicação de replicação de mesclagem para nosso banco de dados.
O problema que estou enfrentando é que a replicação de mesclagem tem essas regras, não gosto de chamá-las de limitações porque posso entender o propósito.
1) Ao criar um filtro de artigo, você não pode incluir uma subconsulta, ou pelo menos não deveria. Se fizer isso, parecerá funcionar na primeira vez que você sincronizar, mas se algo mudar na tabela de subconsulta, o filtro não será reavaliado.
2) Ao usar um filtro de junção, você só pode unir duas tabelas.
O problema que estou encontrando é que os relacionamentos em nosso banco de dados são mais complicados do que isso. Por exemplo, aqui está um de nossos relacionamentos,
user <- regions selected <- STREET LIGHTS -> settings (are we syncing street lights) -> user
Aqui está um exemplo de estrutura de tabela para explicar o que foi dito acima com mais detalhes,
Tabela de usuários,
Id Username
1 petermc
UserRegion Table, (pense nisso como uma tabela de segurança, qual região um determinado usuário pode ver)
Id UserId RegionId
1 1 2
1 2 4
Tabela de configuração de sincronização,
Id UserId Table
1 1 StreetLight
Tabela de regiões (esta é apenas uma tabela de pesquisa para mostrar alguns exemplos de regiões)
Id Region Name
1 North Auckland
2 South Auckland
3 Central Auckland
4 Great Barrier Island
Mesa Rodoviária,
Id RegionId Name
1 1 Rosedale Rd
2 1 North Shore Rd
Mesa Street Light
Id RoadId Last Replaced
1 1 2012-05-01
2 1 2009-06-03
3 2 2001-06-08
Portanto, neste caso, se eu escrevesse uma instrução select para aplicar minha filtragem para petermc, ficaria assim,
select * from StreetLight where
roadId in (select Id from Road where RegionId in (select regionId from UserRegion where userid = 1))
and exists (select 1 from syncsetting where userid = 1 and [table] = 'StreetLight')
Então eu estou fazendo duas coisas lá. Primeira filtragem com base na região para reduzir uma tabela muito grande em um subconjunto menor e mais gerenciável.
A segunda é especificar se o assinante está interessado na tabela StreetLight. Caso contrário, o assinante deve ter uma tabela StreetLight vazia. Esta parte é importante porque a publicação terá um grande número de tabelas nela, então não faz sentido incluir coisas que o assinante não vai usar.
Nosso maior banco de dados possui milhões de registros em algumas tabelas, e haverá uma quantidade moderada de atualizações desses registros também. Devemos obter esta filtragem correta. A opção de não filtrar essas tabelas não é viável.
Esta é uma situação complicada, mas acho que a melhor solução seria criar uma tabela adicional (que chamarei de UserFilters), apenas para armazenar os dados consolidados das tabelas Road, UserRegion e SyncSetting em um único formato de linha combinado e em seguida, junte seu filtro de replicação a essa tabela. Essa tabela UserFilters precisaria ser mantida por gatilhos nas tabelas constituintes para que, quando as linhas em Road, UserRegion ou SyncSetting fossem alteradas, as linhas em UserFilters fossem alteradas automaticamente para corresponder. Vou demonstrar como fazer tudo isso abaixo.
Primeiro uma nota; Esta tabela (UserFilters) é uma tabela desnormalizada que é adjunta ao seu esquema de dados de aplicativo normal. Ou seja, é um artefato operacional usado apenas para manipular a tecnologia de replicação para fazer o que você deseja e não faz parte do design/esquema de dados do seu aplicativo. Esse uso de desnormalização é considerado aceitável, pois é apenas para abordar limitações de tecnologia e não é usado diretamente por seus aplicativos.
OK, aqui está como fazer isso:
1. Escreva sua consulta de filtro desejada no formulário JOIN:
Aqui está sua consulta de filtro original (desenrolada, para facilitar a decomposição):
Queremos reescrever isso em um formato com apenas JOINs e WHEREs, mas sem subConsultas. (Isso sempre pode ser feito, mas não vou explicar como neste artigo.) Assim:
2. Crie uma exibição consolidando todas as condições de filtro
Usamos as cláusulas JOIN acima para fazer uma exibição que contém apenas as colunas relevantes para o filtro de replicação e que projeta todas as linhas necessárias para descrever completamente todas as condições do filtro. Isso é mais fácil do que parece, deve ficar assim:
Observe que também generalizamos a expressão da View para abranger todos os usuários. Além disso, comentamos o SyncSetting "[table] = 'StreetLight'", a fim de generalizar isso para todas as tabelas cobertas por SyncSetting e Road (que podem ou não ser válidos, você terá que decidir isso).
3. Crie a tabela UserFilters
Crie a tabela UserFilters usando a exibição como guia para as colunas. No entanto, queremos adicionar uma coluna IDENTITY por motivos de desempenho de chave/índice:
Em seguida, preencha-o usando a Visualização:
Por motivos de desempenho e bloqueio, você desejará muitos caminhos de índice. Não posso dizer com certeza quais deveriam ser, mas aqui está o que eu começaria:
4. Adicionar acionadores de manutenção às tabelas constituintes
Você precisará adicionar gatilhos às tabelas Road, UserRegion e SyncSetting para manter o conteúdo de [adjUserFilters] sincronizado com essas tabelas quando elas forem modificadas. Eles devem ficar assim: Primeiro, a tabela Road:
A tabela UserRegion:
E, finalmente, a tabela SyncSetting:
5. Crie sua consulta de filtro de replicação
Agora você pode criar seu filtro de consulta de replicação para "user1" da seguinte forma:
Conforme solicitado, este é o script que você usaria para sincronizar os adjUserFilters periodicamente, em vez de Triggers:
De imediato, não tenho certeza de como você garantiria que isso fosse executado logo antes ou como parte do trabalho de sincronização de mesclagem, mas provavelmente pode ser feito.
Devo lançar um aviso. Agora nos afastamos da replicação de mesclagem e devo sugerir que o esquema acima pode ser um grande problema de desempenho.
A exceção a isso exigiria que você tivesse uma pequena publicação com pequenas quantidades de filtragem e/ou filtros que não tivessem vários níveis de profundidade. Por exemplo, 10 a 100 artigos podem funcionar bem. Se você forçar demais o esquema acima, poderá ter problemas de desempenho/travamento.
Toda vez que você insere um registro na tabela de filtragem superior, o gatilho de replicação de mesclagem deve processar todas as tabelas filhas. Ele também adiciona registros a MSMerge_contents e MSMerge_genhistory para todas as tabelas filhas. Quanto mais tabelas filhas você tiver, e se elas tiverem uma grande quantidade de registros nelas, mais poder de processamento será necessário.
Tivemos um problema com sp_MSsetupbelongs sendo muito lento e expirando. No final, chegamos à conclusão de que estávamos pressionando demais a replicação de mesclagem e que essa tecnologia não funcionaria para nós.
Isso me leva a sugerir que, se o esquema de filtragem na replicação de mesclagem pronta para uso não for flexível o suficiente para sua situação, não filtre ou não use a replicação de mesclagem. Teste Teste Teste embora, é claro, cada situação seja diferente.