Consultar um data warehouse

Concluído

Quando as tabelas de dimensões e de fatos em um data warehouse tiverem sido carregadas com dados, você poderá usar o SQL para consultar as tabelas e analisar os dados que elas contêm. A sintaxe Transact-SQL usada para consultar tabelas em um pool de SQL dedicado do Synapse é semelhante ao SQL usado no SQL Server ou no Banco de Dados SQL do Azure.

Agregação de medidas por atributos de dimensão

A maioria das análises de dados com um data warehouse envolve agregar as medidas numéricas em tabelas de fatos segundo os atributos em tabelas de dimensões. Devido à maneira como um esquema em estrela ou floco de neve é implementado, as consultas para executar esse tipo de agregação dependem de cláusulas JOIN para conectar tabelas de fatos a tabelas de dimensões e de uma combinação de funções de agregação e cláusulas GROUP BY para definir as hierarquias de agregação.

Por exemplo, o seguinte SQL consulta as tabelas FactSales e DimDate em um data warehouse hipotético para agregar valores de vendas por ano e trimestre:

SELECT  dates.CalendarYear,
        dates.CalendarQuarter,
        SUM(sales.SalesAmount) AS TotalSales
FROM dbo.FactSales AS sales
JOIN dbo.DimDate AS dates ON sales.OrderDateKey = dates.DateKey
GROUP BY dates.CalendarYear, dates.CalendarQuarter
ORDER BY dates.CalendarYear, dates.CalendarQuarter;

Os resultados dessa consulta seriam semelhantes à seguinte tabela:

CalendarYear CalendarQuarter TotalSales
2020 1 25980.16
2020 2 27453.87
2020 3 28527.15
2020 4 31083.45
2021 1 34562.96
2021 2 36162.27
... ... ...

Você pode unir quantas tabelas de dimensões forem necessárias para calcular as agregações de que precisa. Por exemplo, o seguinte código estende o exemplo anterior para dividir os totais de vendas trimestrais por cidade com base nos detalhes do endereço do cliente na tabela DimCustomer:

SELECT  dates.CalendarYear,
        dates.CalendarQuarter,
        custs.City,
        SUM(sales.SalesAmount) AS TotalSales
FROM dbo.FactSales AS sales
JOIN dbo.DimDate AS dates ON sales.OrderDateKey = dates.DateKey
JOIN dbo.DimCustomer AS custs ON sales.CustomerKey = custs.CustomerKey
GROUP BY dates.CalendarYear, dates.CalendarQuarter, custs.City
ORDER BY dates.CalendarYear, dates.CalendarQuarter, custs.City;

Desta vez, os resultados incluem um total de vendas trimestrais para cada cidade:

CalendarYear CalendarQuarter City TotalSales
2020 1 Amsterdã 5982.53
2020 1 Berlim 2826.98
2020 1 Chicago 5372.72
... ... ... ..
2020 2 Amsterdã 7163.93
2020 2 Berlim 8191.12
2020 2 Chicago 2428.72
... ... ... ..
2020 3 Amsterdã 7261.92
2020 3 Berlim 4202.65
2020 3 Chicago 2287.87
... ... ... ..
2020 4 Amsterdã 8262.73
2020 4 Berlim 5373.61
2020 4 Chicago 7726.23
... ... ... ..
2021 1 Amsterdã 7261.28
2021 1 Berlim 3648.28
2021 1 Chicago 1027.27
... ... ... ..

Junções em um esquema floco de neve

Ao usar um esquema floco de neve, as dimensões podem estar parcialmente normalizadas, exigindo várias junções para relacionar tabelas de fatos a dimensões de floco de neve. Por exemplo, suponha que o data warehouse inclui uma tabela de dimensões DimProduct da qual as categorias de produto foram normalizadas em uma tabela DimCategory separada. Uma consulta para agregar itens vendidos por categoria de produto pode ser semelhante ao seguinte exemplo:

SELECT  cat.ProductCategory,
        SUM(sales.OrderQuantity) AS ItemsSold
FROM dbo.FactSales AS sales
JOIN dbo.DimProduct AS prod ON sales.ProductKey = prod.ProductKey
JOIN dbo.DimCategory AS cat ON prod.CategoryKey = cat.CategoryKey
GROUP BY cat.ProductCategory
ORDER BY cat.ProductCategory;

Os resultados dessa consulta incluem o número de itens vendidos para cada categoria de produto:

ProductCategory ItemsSold
Acessórios 28271
Peças e partes 5368
... ...

Observação

Cláusulas JOIN para FactSales e DimProduct e para DimProduct e DimCategory são necessárias, embora nenhum campo de DimProduct seja retornado pela consulta.

Usando funções de classificação

Outro tipo comum de consulta analítica é particionar os resultados com base em um atributo de dimensão e classificar os resultados dentro de cada partição. Por exemplo, talvez você queira classificar as lojas a cada ano pela receita de vendas. Para atingir essa meta, você pode usar funções de classificação Transact-SQL, como ROW_NUMBER, RANK, DENSE_RANK e NTILE. Essas funções permitem particionar os dados em categorias, cada uma retornando um valor específico que indica a posição relativa de cada linha dentro da partição:

  • ROW_NUMBER retorna a posição ordinal da linha dentro da partição. Por exemplo, a primeira linha é numerada como 1, a segunda como 2 e assim por diante.
  • RANK retorna a posição classificada de cada linha nos resultados ordenados. Por exemplo, em uma partição de lojas ordenadas pelo volume de vendas, a loja com o maior volume de vendas é classificada como 1. Se várias lojas tiverem os mesmos volumes de vendas, elas serão classificadas da mesma forma, e a classificação atribuída às lojas seguintes refletirá o número de lojas que têm volumes de vendas mais altos – incluindo empates.
  • DENSE_RANK classifica as linhas em uma partição da mesma forma que RANK, mas quando várias linhas têm a mesma classificação, as linhas seguintes nas posições de classificação ignoram empates.
  • NTILE retorna o percentil especificado em que a linha se enquadra. Por exemplo, em uma partição de lojas ordenadas pelo volume de vendas, NTILE(4) retorna o quartil em que o volume de vendas de uma loja a coloca.

Por exemplo, considere a consulta abaixo:

SELECT  ProductCategory,
        ProductName,
        ListPrice,
        ROW_NUMBER() OVER
            (PARTITION BY ProductCategory ORDER BY ListPrice DESC) AS RowNumber,
        RANK() OVER
            (PARTITION BY ProductCategory ORDER BY ListPrice DESC) AS Rank,
        DENSE_RANK() OVER
            (PARTITION BY ProductCategory ORDER BY ListPrice DESC) AS DenseRank,
        NTILE(4) OVER
            (PARTITION BY ProductCategory ORDER BY ListPrice DESC) AS Quartile
FROM dbo.DimProduct
ORDER BY ProductCategory;

A consulta particiona os produtos em agrupamentos com base em suas categorias e, dentro de cada partição de categoria, a posição relativa de cada produto é determinada com base em seu preço listado. Os resultados dessa consulta poderiam ser semelhantes à seguinte tabela:

ProductCategory ProductName ListPrice RowNumber Rank DenseRank Quartile
Acessórios Widget 8,99 1 1 1 1
Acessórios Knicknak 8.49 2 2 2 1
Acessórios Roda dentada 5,99 3 3 3 2
Acessórios Doodah 5,99 4 3 3 2
Acessórios Spangle 2,99 5 5 4 3
Acessórios Badabing 0,25 6 6 5 4
Peças e partes Flimflam 7,49 1 1 1 1
Peças e partes Snickity wotsit 6,99 2 2 2 1
Peças e partes Flange 4.25 3 3 3 2
... ... ... ... ... ... ...

Observação

Os resultados de exemplo demonstram a diferença entre RANK e DENSE_RANK. Observe que, na categoria Acessórios, os produtos Roda dentada e Doodah têm o mesmo preço listado; e ambos são classificados como o 3º produto com preço mais alto. O próximo produto com preço mais alto tem um RANK de 5 (há quatro produtos mais caros do que ele) e um DENSE_RANK de 4 (há três preços mais altos).

Para saber mais sobre as funções de classificação, consulte Funções de classificação (Transact-SQL) na documentação do Azure Synapse Analytics.

Recuperando uma contagem aproximada

Embora a finalidade de um data warehouse seja principalmente dar suporte a modelos de dados analíticos e relatórios para a empresa, analistas e cientistas de dados geralmente precisam fazer alguma exploração inicial dos dados apenas para determinar a escala básica e a distribuição deles.

Por exemplo, a seguinte consulta usa a função COUNT para recuperar o número de vendas para cada ano em um data warehouse hipotético:

SELECT dates.CalendarYear AS CalendarYear,
    COUNT(DISTINCT sales.OrderNumber) AS Orders
FROM FactSales AS sales
JOIN DimDate AS dates ON sales.OrderDateKey = dates.DateKey
GROUP BY dates.CalendarYear
ORDER BY CalendarYear;

Os resultados dessa consulta podem ser semelhantes à seguinte tabela:

CalendarYear Pedidos
2019 239870
2020 284741
2021 309272
... ...

O volume de dados em um data warehouse pode significar que até mesmo consultas simples para contar o número de registros que atendem aos critérios especificados podem levar um tempo considerável para serem executadas. Em muitos casos, uma contagem precisa não é necessária – uma estimativa aproximada será suficiente. Nesses casos, use a função APPROX_COUNT_DISTINCT, conforme mostrado no seguinte exemplo:

SELECT dates.CalendarYear AS CalendarYear,
    APPROX_COUNT_DISTINCT(sales.OrderNumber) AS ApproxOrders
FROM FactSales AS sales
JOIN DimDate AS dates ON sales.OrderDateKey = dates.DateKey
GROUP BY dates.CalendarYear
ORDER BY CalendarYear;

A função APPROX_COUNT_DISTINCT usa um algoritmo HyperLogLog para recuperar uma contagem aproximada. É garantido que o resultado tenha uma taxa de erro máxima de 2% com probabilidade de 97%, portanto, os resultados dessa consulta com os mesmos dados hipotéticos de antes podem ser semelhantes à seguinte tabela:

CalendarYear ApproxOrders
2019 235552
2020 290436
2021 304633
... ...

As contagens são menos exatas, mas ainda são suficientes para uma comparação aproximada das vendas anuais. Com um grande volume de dados, a consulta que usa a função APPROX_COUNT_DISTINCT é concluída mais rapidamente e a exatidão reduzida pode ser uma compensação aceitável durante a exploração básica de dados.

Observação

Consulte a documentação da função APPROX_COUNT_DISTINCT para obter mais detalhes.