Possíveis benefícios de armazenar vários valores em um campo de uma linha em vez de como linhas separadas

11

Durante nossa última reunião semanal, uma pessoa que não tem experiência em administração de banco de dados levantou esta questão:

"Haveria um cenário que justificasse o armazenamento de dados em linha (string) em vez de várias linhas?"

Vamos assumir uma tabela chamada countryStatesonde queremos armazenar os estados de um país; Usarei os EUA neste exemplo e não listarei todos os Estados por preguiça.

Lá teríamos duas colunas; um chamado Countrye o outro chamado States. Conforme discutido aqui e proposto pela resposta de @ srutzky , PKserá o código definido pela ISO 3166-1 alfa-3 .

Nossa tabela ficaria assim:

+---------+-----------------------+-------------------------------------------------------+
| Country | States                | StateName                                             |
+---------+-----------------------+-------------------------------------------------------+
| USA     | AL, CA, FL,OH, NY, WY | Alabama, California, Florida, Ohio, New York, Wyoming |
+---------+-----------------------+-------------------------------------------------------+

Ao fazer a mesma pergunta a um desenvolvedor amigo, ele disse que, do ponto de vista do tamanho do tráfego de dados, isso pode ser útil, mas não se precisarmos manipular esses dados. Nesse caso, teria que haver uma inteligência no código do aplicativo que poderia transformar essa string em uma lista (digamos que o software que tem acesso a esta tabela precise criar uma caixa de combinação).

Concluímos que esse modelo não é muito útil, mas suspeitei que pudesse haver uma maneira de torná-lo útil.

O que eu gostaria de perguntar é se algum de vocês já viu, ouviu ou fez algo assim de uma maneira que realmente funciona .

Humano Apesar de tudo
fonte
Agora imagine que você tenha uma segunda tabela, "vendas", que possui dados para todas as vendas que ocorreram junto com o código do estado em que a venda ocorreu. Como você escreveria uma consulta que gera um relatório com colunas (StateName, TotalSalesAmount)? Difícil, certo?
zgguy
Exatamente. Eu também não concordo com este modelo. Ficamos presos a qualquer momento em que precisamos recuperar qualquer tipo de dados (ou dados úteis, se você desejar).
Human_AfterAll
Um cenário possível pode ser o de armazenar variáveis. Armazene a;b;c, use o front end para analisar sua string e a, em seguida b, cexecute a execução fazendo algo com eles, talvez? Sinta que pode atender a algum tipo de necessidade específica dessa maneira ... Pensando bem, não. Você sempre pode armazenar IDs, Junte-se a suas tabelas e criar uma string concatenada que pode enviar conteúdo para o FE ...
Nelz
Para ser justo (para mim, pelo menos ;-), propus usar os códigos de país com dois caracteres :-) nessa outra resposta .
Solomon Rutzky
2
Observe que ninguém tem escrúpulos em armazenar o valor "Alabama" em uma coluna, em vez de ter uma tabela separada com as colunas STATE, N & C para "state STATE name is Nth character C". Como 1. não pretendemos consultar caracteres de nomes ou 2. não nos importamos em chamar uma função NTH_CHAR (N, S) retornando "o enésimo caractere da string S" em todas as linhas com um nome, se o fizermos. . (Vs JOIN e outros operadores relacionais eliminando algumas dessas linhas por meio da tabela extra.) O mesmo vale para números inteiros e NTH_DIGIT (N, I). É sempre uma decisão sobre o que em um banco de dados específico é relacionalmente atômico.
philipxy

Respostas:

13

Para começar, o título da pergunta atual referente a "armazenamento de dados como string em vez de colunas" é um pouco confuso. Quando se fala em armazenar dados como cadeias de caracteres em vez de outra coisa, isso geralmente se refere à serialização de tudo para um formato de cadeia de caracteres, em vez de um tipo de dados adequado / forte (por exemplo, INTou DATETIME). Mas se perguntar sobre o armazenamento de dados como vários valores em um único campo, em vez de separar linhas, isso é um pouco diferente. E para ser justo, embora a concatenação de valores seja mais facilmente feita com strings, também pode ser feita com INTe BINARYtipos, mascarando bits ou reservando da mesma forma determinadas posições para ter significados diferentes. Como a segunda interpretação é sobre o que realmente está sendo perguntado, com base no texto da pergunta, vamos abordar isso.

Em uma palavra: Não. Se você estiver armazenando pontos de dados reais, isso trará apenas dor (em termos de código e desempenho), pois é uma complicação desnecessária. Se for um valor que só será armazenado como uma única unidade, atualizado como uma única unidade e nunca desmontado no banco de dados, isso pode ser aceitável, pois é aproximadamente análogo ao armazenamento de uma imagem ou PDF. Caso contrário, qualquer tentativa de analisar os dados invalidará o uso de quaisquer índices (por exemplo LIKE '%something%', usando , ou CHARINDEX, ou PATINDEX, ou SUBSTRING, etc).

Se você precisar armazenar valores separados em um único campo de uma única linha, haverá meios mais apropriados de fazer isso: XML ou JSON. Esses são formatos analisáveis ​​( XML / JSON ) e o XML pode até ser indexado . Mas, idealmente, esses dados seriam armazenados em campos digitados corretamente, para que possam ser realmente úteis.

E não se esqueça de que o objetivo de um RDBMS é armazenar dados para que possam ser recuperados e manipulados da maneira mais eficiente possível, dentro das restrições impostas por ser compatível com ACID . A recuperação de valores concatenados é ruim o suficiente devido à necessidade de analisar os valores primeiro e isso não é indexável. Mas manipular geralmente significa substituir o blob inteiro apenas para atualizar uma parte dele (assumindo que não existe um padrão para usar com uma REPLACEfunção). O tipo de dados XML pelo menos permite XML DML para atualizações simplistas, embora elas ainda não sejam tão rápidas quanto uma simples atualização de dados modelados corretamente.

Além disso, dado um cenário como o que é mostrado na pergunta acima, concatenando todos os códigos de estado juntos, você não conseguiria Chave estrangeira (em qualquer direção) desses valores.

E se os requisitos de negócios mudarem com o tempo e você precisar rastrear propriedades adicionais desses itens? Em termos de "estados", o que dizer das capitais, população ou ordem de classificação ou qualquer outra coisa? Armazenado corretamente como linhas, você pode adicionar mais colunas para propriedades adicionais. Claro, você pode ter vários níveis de dados analisáveis, como, |StateCode,Capital,Population |StateCode,Capital,Populate|...espero, que qualquer pessoa possa ver o problema ficando exponencialmente fora de controle. Obviamente, esse problema em particular é facilmente tratado com os formatos XML e JSON, e esse é o valor deles, como mencionado acima. Mas você ainda precisa de uma muito boa razão para usar qualquer um desses como meios iniciais de modelagem como nem nunca vai ser tão eficiente quanto usar campos discretos em linhas separadas.

Solomon Rutzky
fonte
9

Na verdade, usei algo assim para um propósito muito limitado. Criamos uma tabela de cabeçalhos para arquivos de saída. Eles foram construídos especificamente e eram principalmente apenas os títulos das colunas, mas não exatamente. Então os dados pareciam algo como

OutputType   OutputHeader
PersonalData Name|Address|City|State|Zip
JobInfo      Name|JobName|JobTitle

Essencialmente, parecia que era uma lista delimitada. E de uma maneira que era. Mas, para nossos propósitos, era uma única corda longa.

Esse é o truque aqui. Se você nunca planeja analisar a lista, vale a pena salvá-la. Se, no entanto, você precisar ou precisar analisar a lista, vale a pena o espaço e o tempo extra para dividi-la e salvá-la em linhas separadas.

Kenneth Fisher
fonte
1

Eu usei uma vez com uma tabela bastante pequena, por exemplo:

CREATE TABLE t1 (
  ID number,
  some_feature   varchar2(100),
  valid_channels  varchar2(100));

CREATE TABLE channel_def (
  channel varchar2(100));

E então armazene valores CRM,SMS,SELF-CAREem valid_channel.

A tabela inteira tem algo como 10 registros. valid_channelcontém valores que realmente devem estar em uma tabela de vínculo que representa o relacionamento muitos-para-muitos. A mesa t1não será usada intensivamente, por isso decidimos seguir esse caminho. Algumas políticas estavam envolvidas nessa decisão, no entanto (veja abaixo).

Mas, em geral, eu evito, não é 3NF.

O local em que trabalho atualmente tem dezenas dessas colunas por todo o lugar. A justificativa deles é que isso facilita as consultas: em vez de juntar três tabelas usando a tabela de vinculação, elas podem ir direto para a tabela de definição usando LIKE. Por exemplo

SELECT * 
  FROM t1 
 INNER JOIN channel_def cd
    ON ','||t1.valid_channels||',' LIKE '%,'||cd.channel||',%';

Horrível + no Oracle, desativa o uso do índice por causa do início '%,'.

Robotron
fonte
O que seria mais lento: LIKEou uma junção simples?
Human_AfterAll
É melhor ter uma junção em uma coluna indexada ou, pelo menos, ter uma restrição referencial (FK). Além disso, as junções geralmente são feitas em uma PK da outra tabela, que é indexada por padrão (pelo menos no Oracle). Se você está perguntando sobre o caso específico em questão (veja acima), o plano de execução provavelmente diria que era o mesmo, já que era uma mesa pequena.
Robotron
@Human_AfterAll LIKEseria mais lento, especialmente se os dados forem modelados corretamente para usar um TINYINTcampo PK em channel_def. Então, ele só precisa comparar um único byte entre as duas tabelas. Aqui, ele deve analisar a sequência, caractere por caractere (pelo menos até que a condição seja satisfeita), e está fazendo uma pesquisa que não diferencia maiúsculas de minúsculas (com base na definição de tabela especificada que não mostra um _BIN2agrupamento sendo usado). Isso invalida índices no SQL Server também. Eu resolvi isso na minha resposta dizendo que a análise não pode usar índices. Acabei de atualizar minha resposta para torná-la mais clara.
Solomon Rutzky
11
@Human_AfterAll Eu diria que essa decisão de modelagem foi sustentada por falta de experiência e conhecimento (e às vezes preguiça). Um JOIN adicional é tudo o que é salvo, mas o que é sacrificado é a capacidade de Chave Estrangeira, que impediria a entrada de dados totalmente falsos (mesmo que não correspondam à LIKEcláusula e produzam resultados estranhos, ainda podem causar outros problemas ou pelo menos dificultar / depurar mais a depuração). Isso também torna a atualização do valid_channelscampo mais complicada. Isso não quer dizer que isso não funcione, simplesmente não há uma boa razão para fazê-lo.
Solomon Rutzky
"falta de experiência" - o que é pior é que esta decisão de design particular foi imposta por um membro sênior da equipe ...
Robotron
1

Isso foi feito aqui no SE. Como Marc Gravell escreve :

... Após algumas considerações e considerações, decidimos por uma representação natural delimitada por um tubo (barra), com tubos à esquerda / à direita, para que ".net c #" se torne simplesmente "| .net | c # |". Isso tem virtudes:

  • muito simples de analisar
  • a atualização e a remoção em massa de tags podem ser feitas com uma simples substituição (incluindo os pipes, para evitar a substituição de correspondências no meio da tag)
  • ...

Esse "novo formato" foi o próximo passo do "formato antigo", que era um pouco diferente e foi escolhido para usar o recurso de Pesquisa de Texto Completo do SQL Server, portanto, alguns dos benefícios não são relevantes se você o fizer do zero.

Presumivelmente, eles não normalizaram completamente a coisa por razões de quantidade de trabalho e desempenho.

Eugene Ryabtsev
fonte
0

Bem, um possível benefício principal do uso de seqüências de caracteres e outros tipos de dados é enviá-los do SQL Server para C #, C, C ++ (etc) usando o SQLCLR quando é necessário um desempenho absoluto. Você pode até criar uma exibição ou procedimento armazenado para representar dados relacionais de maneira não relacional - como no exemplo acima, com esse objetivo.

Veja este exemplo:

http://aboutsqlserver.com/2013/07/22/clr-vs-t-sql-performance-considerations/

por Wikipedia: SQL CLR ou SQLCLR (SQL Common Language Runtime) é uma tecnologia para hospedagem do mecanismo de tempo de execução de linguagem comum do Microsoft .NET no SQL Server. O SQLCLR permite que o código gerenciado seja hospedado e executado no ambiente do Microsoft SQL Server.

Picada
fonte
2
Olá. Você pode dar mais detalhes aqui. Não tenho certeza de como isso é um benefício de armazenar dados de maneiras não tradicionais. Se alguma coisa, é um benefício do SQLCLR poder lidar melhor com formatos de dados alternativos, se houver. Mas esse não é um motivo para preferir um formato de dados alternativo. Como tal, acho que isso não responde à pergunta.
Solomon Rutzky
O link do artigo explica os benefícios dos prós e contras. Além disso, mencionei o armazenamento de dados de forma relacional e, para fins do CLR, convertê-los em não-relacionais com uma exibição ou procedimento armazenado. Sua pergunta foi "Existe um cenário que justifique o armazenamento de dados em linha (string) em vez de várias linhas?" E minha resposta foi sim, embora eu prefira uma visualização ou procedimento armazenado para fins de interação com o CLR.
Sting
0

Na minha opinião, a resposta seria não. Eu não usei essa abordagem e a evitaria - não consigo pensar em uma razão para seguir esse caminho. Você está se inclinando para o mundo do JSON / NoSQL com uma matriz.

Tínhamos opções de design semelhantes em uma função anterior, na qual a equipe de arquitetos queria ter um campo "Dados" que fosse delimitado e depois convertido em binário. No final, não seguimos esse caminho por alguns motivos.

Se você tivesse que ingressar nesse tipo de dados, seria uma experiência feia. A atualização de elementos únicos da sequência também seria desagradável.

Clive Strong
fonte