Armazenamento eficiente de conjuntos de pares de valores-chave com chaves totalmente diferentes


9

Eu herdei um aplicativo que associa muitos tipos diferentes de atividades a um site. Existem aproximadamente 100 tipos de atividades diferentes e cada um possui um conjunto diferente de 3 a 10 campos. No entanto, todas as atividades têm pelo menos um campo de data (pode ser qualquer combinação de data, data de início, data de término, data de início agendada etc.) e um campo de pessoa responsável. Todos os outros campos variam amplamente e um campo de data de início não será necessariamente chamado de "Data de início".

Criar uma tabela de subtipos para cada tipo de atividade resultaria em um esquema com 100 tabelas de subtipos diferentes, o que seria muito difícil de lidar. A solução atual para esse problema é armazenar os valores da atividade como pares de valores-chave. Este é um esquema bastante simplificado do sistema atual para entender o ponto.

insira a descrição da imagem aqui

Cada atividade possui vários ActivityFields; cada site possui várias atividades e a tabela SiteActivityData armazena os KVPs de cada SiteActivity.

Isso torna o aplicativo (baseado na Web) muito fácil de codificar, porque tudo o que você realmente precisa fazer é percorrer os registros no SiteActivityData para uma determinada atividade e adicionar um controle de rótulo e entrada para cada linha a um formulário. Mas há muitos problemas:

  • Integridade é ruim; é possível inserir um campo no SiteActivityData que não pertença ao tipo de atividade e o DataValue é um campo varchar, portanto, números e datas precisam ser constantemente convertidos.
  • Os relatórios e consultas ad-hoc desses dados são difíceis, propensos a erros e lentos. Por exemplo, obter uma lista de todas as atividades de um determinado tipo que tenham uma Data de término dentro de um intervalo especificado requer pivôs e varchars de conversão para datas. Os redatores de relatórios odeiam esse esquema e eu não os culpo.

Então, o que estou procurando é uma maneira de armazenar um grande número de atividades que quase não têm campos em comum, de maneira a facilitar a geração de relatórios. O que eu vim até agora é usar XML para armazenar os dados da atividade em um formato pseudo-noSQL:

insira a descrição da imagem aqui

A tabela Atividade conteria o XSD para cada atividade, eliminando a necessidade da tabela ActivityField. SiteActivity conteria o XML de valor-chave, para que cada atividade de um site agora estivesse em uma única linha.

Uma atividade seria algo parecido com isto (mas eu não a desenvolvi completamente):

<SomeActivityType>
  <SomeDateField type="StartDate">2000-01-01</SomeDateField>
  <AnotherDateField type="EndDate">2011-01-01</AnotherDateField>
  <EmployeeId type="ResponsiblePerson">1234</EmployeeId>
  <SomeTextField>blah blah</SomeTextField>
  ...

Vantagens:

  • O XSD validaria o XML, detectando erros como colocar uma string em um campo numérico no nível do banco de dados, algo que era impossível com o esquema antigo que armazenava tudo no varchar.
  • O conjunto de registros de KVPs usado para criar formulários da Web pode ser facilmente reproduzido usando select ... from ActivityXML.nodes('/SomeActivityType/*') as T(r)
  • Uma subconsulta xpath do XML pode ser usada para produzir um conjunto de resultados que possui colunas para data de início, data de término etc. sem usar um pivô, algo como select ActivityXML.value('.[@type=StartDate]', 'datetime') as StartDate, ActivityXML.value('.[@type=EndDate]', 'datetime') as EndDate from SiteActivity where...

Parece uma boa ideia? Não consigo pensar em outras maneiras de armazenar um número tão grande de diferentes conjuntos de propriedades. Outro pensamento que tive foi manter o esquema existente e convertê-lo em algo mais facilmente consultável em um data warehouse, mas nunca projetei um esquema em estrela antes e não fazia ideia de por onde começar.

Pergunta adicional: Se eu definir uma marca como tendo um tipo de dados de data no XSD xs:date, o SQL Server o indexará como um valor de data? Estou preocupado se, se eu consultar por data, ele precisará converter a string de data em um valor de data e aumentar a chance de usar um índice.


Qual é a atualização dos dados dos relatórios? Os relatórios chegarão à produção?
James Anderson

A maioria dos relatórios chega agora a um data warehouse (o que não é realmente um DW, é essencialmente uma cópia do esquema transacional de produção, com um conjunto de visualizações e tabelas de outros bancos de dados). Ter relatórios com um dia desatualizado é aceitável, mas seria um bônus se pudesse ser publicado.
Paul Abbott

Quanta sobreposição existe nos campos? Dez campos abrangem todos os 100 subtipos ou existem ~ 500 campos totalmente distintos?
Jon de Todos os Negócios

Existem 72 campos e 75 tipos de atividades. 30 campos são usados ​​apenas por uma atividade e a maioria do restante é usada por 5 a 10 atividades. Existem vários campos usados ​​por ~ 30 atividades diferentes. Na maioria das vezes, não há muita semelhança entre as atividades.
Paul Abbott

Respostas:


7

Então, o que estou procurando é uma maneira de armazenar um grande número de atividades que quase não têm campos em comum, de maneira a facilitar a geração de relatórios.

Representante insuficiente para comentar primeiro, então aqui vamos nós!

Se o objetivo principal for o relatório e você tiver um DW (mesmo que não seja um esquema em estrela), recomendo tentar inseri-lo em um esquema em estrela. Os benefícios são consultas rápidas e simples. A desvantagem é o ETL, mas você já está pensando em mover os dados para um novo design e o ETL para o esquema em estrela é provavelmente mais simples de criar e manter do que uma solução de wrapper XML (e o SSIS está incluído no seu licenciamento do SQL Server). Além disso, inicia o processo de um design reconhecido de relatórios / análises.

Então, como fazer isso ... Parece que você tem o que é conhecido como um fato sem fatos . Essa é uma interseção de atributos que definem um evento sem medida associada (como um preço de venda). Você tem datas disponíveis para algumas ou todas as suas atividades? É provável que você realmente tenha uma interseção de uma Atividade, Site e Data (s).

DimActivity- Acho que existe um padrão, algo que pode permitir que você os decomponha em pelo menos colunas relativamente compartilhadas. Se sim, você pode ter três? cinco? dimensões para classes de atividades. Na pior das hipóteses, você tem algumas colunas consistentes, como o nome da atividade, pode filtrar e deixar títulos gerais como "Atributo1" etc. para os demais detalhes aleatórios.

Você não precisa de tudo na dimensão - provavelmente não deve haver nenhuma data na dimensão Atividade - todos devem estar presentes, pois a Chave de Substituição faz referência à dimensão Data. Como exemplo, uma Data que permaneceria na dimensão de uma pessoa seria uma data de nascimento porque é um atributo de uma pessoa. Uma data de visita ao hospital residiria de fato, pois é um evento pontual associado a uma pessoa, entre outras coisas, mas não é um atributo da pessoa que visita o hospital. Mais discussão sobre o fato.

DimSite- parece simples, então descreveremos as chaves substitutas aqui. Essencialmente, este é apenas um ID único e incremental. A coluna Identidade Inteira é comum. Isso permite a separação dos sistemas DW e de origem e garante junções ideais no data warehouse. Sua Chave Natural ou Chave Comercial geralmente é mantida, mas para manutenção / design, não para análise e junções. Esquema de exemplo:

CREATE TABLE [DIM].[Site]
(
 SiteSK INT NOT NULL IDENTITY PRIMARY KEY
,SiteNK INT NOT NULL --source system key
,SiteName VARCHAR(500) NOT NULL
)

DimDate- atributos de data. Faça uma "chave inteligente" em vez de uma identidade. Isso significa que você pode digitar um número inteiro significativo relacionado a uma data para consultas como WHERE DateSK = 20150708. Existem muitos scripts gratuitos para carregar o DimDate e a maioria inclui essa chave inteligente. ( uma opção )

DimEmployee - seu XML incluiu isso, se for uma alteração mais geral para DimPerson, e preencha com atributos de pessoa relevantes, pois eles estão disponíveis e são pertinentes aos relatórios.

E seu fato é:

FactActivitySite
DimSiteSK - FK to DimSite
DimActivitySK - FK to DimActivity
DimEmployee - FK to DimEmployee
DimDateSK - FK to DimDate

Você pode renomeá-los no fato e pode ter várias chaves de data por evento. Os fatos são geralmente muito grandes, portanto, evitar atualizações geralmente é bom ... se você tiver várias atualizações de datas em um único evento, poderá experimentar um design de Excluir / Inserir adicionando um SK ao fato que permite a seleção de linhas de "atualização" para ser excluído e depois inserir os dados mais recentes.

Expandir as datas de fatos para o que você precisa: StartDateSK, EndDateSK, ScheduledStartDateSK.

Todas as dimensões devem ter uma linha desconhecida, normalmente com um -1 SK codificado. Quando você carrega o fato e uma atividade não possui nenhuma das Datas incluídas, ela deve simplesmente carregar um -1.

O fato é uma coleção de referências inteiras aos seus atributos armazenados nas dimensões, une-os e você obtém todos os seus detalhes, em um padrão de associação muito limpo, e o fato, devido aos seus tipos de dados, é excepcionalmente pequeno e rápido. Como você está no SQL Server, adicione um índice columnstore para aumentar ainda mais o desempenho. Você pode simplesmente descartá-lo e reconstruir durante o ETL. Depois de acessar o SQL 2014+, você pode gravar nos índices columnstore.

insira a descrição da imagem aqui

Se você seguir esta rota, pesquise Modelagem Dimensional. Eu recomendaria a metodologia Kimball . Existem muitos guias gratuitos por aí também, mas se isso for algo a não ser uma solução única, o investimento provavelmente valerá a pena.


(pergunta do wesdev): @Dave, que ferramenta ERD você usou?
usar o seguinte comando

Isso foi feito no Microsoft Visio 2013
Dave
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.