Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Juntando 100 mesas


O otimizador do SQL Server contém lógica para remover junções redundantes, mas há restrições, e as junções devem ser provavelmente redundante . Para resumir, uma junção pode ter quatro efeitos:
  1. Pode adicionar colunas extras (da tabela unida)
  2. Pode adicionar linhas extras (a tabela unida pode corresponder a uma linha de origem mais de uma vez)
  3. Ele pode remover linhas (a tabela unida pode não ter uma correspondência)
  4. Pode introduzir NULL s (para um RIGHT ou FULL JOIN )

Para remover com sucesso uma junção redundante, a consulta (ou exibição) deve levar em conta todas as quatro possibilidades. Quando isso é feito, corretamente, o efeito pode ser surpreendente. Por exemplo:
USE AdventureWorks2012;
GO
CREATE VIEW dbo.ComplexView
AS
    SELECT
        pc.ProductCategoryID, pc.Name AS CatName,
        ps.ProductSubcategoryID, ps.Name AS SubCatName,
        p.ProductID, p.Name AS ProductName,
        p.Color, p.ListPrice, p.ReorderPoint,
        pm.Name AS ModelName, pm.ModifiedDate
    FROM Production.ProductCategory AS pc
    FULL JOIN Production.ProductSubcategory AS ps ON
        ps.ProductCategoryID = pc.ProductCategoryID
    FULL JOIN Production.Product AS p ON
        p.ProductSubcategoryID = ps.ProductSubcategoryID
    FULL JOIN Production.ProductModel AS pm ON
        pm.ProductModelID = p.ProductModelID

O otimizador pode simplificar com sucesso a seguinte consulta:
SELECT
    c.ProductID,
    c.ProductName
FROM dbo.ComplexView AS c
WHERE
    c.ProductName LIKE N'G%';

Para:



Rob Farley escreveu sobre essas ideias em profundidade no livro original MVP Deep Dives , e há uma gravação dele apresentando sobre o tema em SQLBits.

As principais restrições são que os relacionamentos de chave estrangeira deve ser baseado em uma única chave para contribuir para o processo de simplificação, e o tempo de compilação para as consultas em tal visão pode se tornar bastante longo, especialmente à medida que o número de junções aumenta. Pode ser um grande desafio escrever uma visão de 100 tabelas que tenha toda a semântica exatamente correta. Eu estaria inclinado a encontrar uma solução alternativa, talvez usando SQL dinâmico .

Dito isso, as qualidades particulares de sua tabela desnormalizada podem significar que a visualização é bastante simples de montar, exigindo apenas FOREIGN KEYs obrigatórias não-NULL colunas referenciadas capazes e UNIQUE apropriados restrições para fazer esta solução funcionar como você espera, sem a sobrecarga de 100 operadores de junção física no plano.

Exemplo


Usando dez tabelas em vez de cem:
-- Referenced tables
CREATE TABLE dbo.Ref01 (col01 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref02 (col02 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref03 (col03 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref04 (col04 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref05 (col05 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref06 (col06 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref07 (col07 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref08 (col08 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref09 (col09 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);
CREATE TABLE dbo.Ref10 (col10 tinyint PRIMARY KEY, item varchar(50) NOT NULL UNIQUE);

A definição da tabela pai (com compactação de página):
CREATE TABLE dbo.Normalized
(
    pk      integer IDENTITY NOT NULL,
    col01   tinyint NOT NULL REFERENCES dbo.Ref01,
    col02   tinyint NOT NULL REFERENCES dbo.Ref02,
    col03   tinyint NOT NULL REFERENCES dbo.Ref03,
    col04   tinyint NOT NULL REFERENCES dbo.Ref04,
    col05   tinyint NOT NULL REFERENCES dbo.Ref05,
    col06   tinyint NOT NULL REFERENCES dbo.Ref06,
    col07   tinyint NOT NULL REFERENCES dbo.Ref07,
    col08   tinyint NOT NULL REFERENCES dbo.Ref08,
    col09   tinyint NOT NULL REFERENCES dbo.Ref09,
    col10   tinyint NOT NULL REFERENCES dbo.Ref10,

    CONSTRAINT PK_Normalized
        PRIMARY KEY CLUSTERED (pk)
        WITH (DATA_COMPRESSION = PAGE)
);

A vista:
CREATE VIEW dbo.Denormalized
WITH SCHEMABINDING AS
SELECT
    item01 = r01.item,
    item02 = r02.item,
    item03 = r03.item,
    item04 = r04.item,
    item05 = r05.item,
    item06 = r06.item,
    item07 = r07.item,
    item08 = r08.item,
    item09 = r09.item,
    item10 = r10.item
FROM dbo.Normalized AS n
JOIN dbo.Ref01 AS r01 ON r01.col01 = n.col01
JOIN dbo.Ref02 AS r02 ON r02.col02 = n.col02
JOIN dbo.Ref03 AS r03 ON r03.col03 = n.col03
JOIN dbo.Ref04 AS r04 ON r04.col04 = n.col04
JOIN dbo.Ref05 AS r05 ON r05.col05 = n.col05
JOIN dbo.Ref06 AS r06 ON r06.col06 = n.col06
JOIN dbo.Ref07 AS r07 ON r07.col07 = n.col07
JOIN dbo.Ref08 AS r08 ON r08.col08 = n.col08
JOIN dbo.Ref09 AS r09 ON r09.col09 = n.col09
JOIN dbo.Ref10 AS r10 ON r10.col10 = n.col10;

Hackeie as estatísticas para fazer o otimizador pensar que a tabela é muito grande:
UPDATE STATISTICS dbo.Normalized WITH ROWCOUNT = 100000000, PAGECOUNT = 5000000;

Exemplo de consulta do usuário:
SELECT
    d.item06,
    d.item07
FROM dbo.Denormalized AS d
WHERE
    d.item08 = 'Banana'
    AND d.item01 = 'Green';

Nos dá este plano de execução:



A verificação da tabela Normalizada parece ruim, mas ambos os bitmaps do filtro Bloom são aplicados durante a verificação pelo mecanismo de armazenamento (assim, as linhas que não podem corresponder nem chegam ao processador de consulta). Isso pode ser suficiente para fornecer um desempenho aceitável no seu caso e certamente melhor do que digitalizar a tabela original com suas colunas transbordantes.

Se você conseguir atualizar para o SQL Server 2012 Enterprise em algum momento, terá outra opção:criar um índice de armazenamento de colunas na tabela Normalizada:
CREATE NONCLUSTERED COLUMNSTORE INDEX cs 
ON dbo.Normalized (col01,col02,col03,col04,col05,col06,col07,col08,col09,col10);

O plano de execução é:



Isso provavelmente parece pior para você, mas o armazenamento de colunas fornece compactação excepcional e todo o plano de execução é executado no modo de lote com filtros para todas as colunas de contribuição. Se o servidor tiver threads e memória adequados disponíveis, essa alternativa pode realmente voar.

Por fim, não tenho certeza se essa normalização é a abordagem correta, considerando o número de tabelas e as chances de obter um plano de execução ruim ou exigir tempo de compilação excessivo. Eu provavelmente corrigiria primeiro o esquema da tabela desnormalizada (tipos de dados adequados e assim por diante), possivelmente aplicaria a compactação de dados... as coisas usuais.

Se os dados realmente pertencem a um esquema em estrela, ele provavelmente precisa de mais trabalho de design do que apenas dividir elementos de dados repetidos em tabelas separadas.