Share via


Criar tabelas com o Synapse SQL no Azure Synapse Analytics

Este documento inclui conceitos-chave para estruturar tabelas com o conjunto de SQL dedicado e o conjunto de SQL sem servidor.

O conjunto de SQL sem servidor é um serviço de consulta sobre os dados no data lake. Não tem armazenamento local para ingestão de dados. O Conjunto de SQL dedicado representa uma coleção de recursos analíticos que estão a ser aprovisionados ao utilizar o Synapse SQL. O tamanho de um conjunto de SQL dedicado é determinado pelas Unidades de Armazenamento de Dados (DWU).

A tabela seguinte lista os tópicos relevantes para o conjunto de SQL dedicado vs. conjunto de SQL sem servidor:

Tópico conjunto de SQL dedicado conjunto de SQL sem servidor
Determinar a categoria da tabela Yes No
Nomes de esquema Yes Yes
Nomes de tabelas Yes No
Persistência da tabela Yes No
Tabela normal Yes No
Tabela temporária Yes Yes
Tabela externa Yes Yes
Tipos de dados Yes Yes
Tabelas distribuídas Yes No
Tabelas distribuídas com hash Yes No
Tabelas replicadas Yes No
Tabelas round robin Yes No
Métodos de distribuição comuns para tabelas Yes No
Partições Yes Yes
Índices Columnstore Yes No
Estatísticas Yes Yes
Chave primária e chave exclusiva Yes No
Comandos para criar tabelas Yes No
Alinhar os dados de origem com o armazém de dados Yes No
Funcionalidades de tabela não suportadas Yes No
Consultas de tamanho da tabela Yes No

Determinar a categoria da tabela

Um esquema de estrela organiza os dados em tabelas de factos e dimensões. Algumas tabelas são utilizadas para integração ou teste de dados antes de passar para uma tabela de factos ou dimensões. À medida que cria uma tabela, decida se os dados da tabela pertencem a uma tabela de factos, dimensão ou integração. Esta decisão informa a estrutura e a distribuição de tabelas adequadas.

  • As tabelas de factos contêm dados quantitativos que são normalmente gerados num sistema transacional e, em seguida, carregados para o armazém de dados. Por exemplo, uma empresa de retalho gera transações de vendas todos os dias e, em seguida, carrega os dados para uma tabela de factos do armazém de dados para análise.

  • As tabelas de dimensões contêm dados de atributos que podem ser alterados, mas que normalmente são alterados com pouca frequência. Por exemplo, o nome e endereço de um cliente são armazenados numa tabela de dimensões e atualizados apenas quando o perfil do cliente é alterado. Para minimizar o tamanho de uma tabela de factos grande, o nome e o endereço do cliente não precisam de estar em todas as linhas de uma tabela de factos. Em vez disso, a tabela de factos e a tabela de dimensões podem partilhar um ID de cliente. Uma consulta pode associar as duas tabelas para associar o perfil e as transações de um cliente.

  • As tabelas de integração fornecem um local para integrar ou testar dados. Pode criar uma tabela de integração como uma tabela normal, uma tabela externa ou uma tabela temporária. Por exemplo, pode carregar dados para uma tabela de teste, realizar transformações nos dados em teste e, em seguida, inserir os dados numa tabela de produção.

Nomes de esquema

Os esquemas são uma boa forma de agrupar objetos que são utilizados de forma semelhante. O código seguinte cria um esquema definido pelo utilizador denominado wwi.

CREATE SCHEMA wwi;

Nomes de tabelas

Se estiver a migrar várias bases de dados de uma solução no local para um conjunto de SQL dedicado, a melhor prática é migrar todas as tabelas de factos, dimensões e integração para um esquema de conjunto de SQL. Por exemplo, pode armazenar todas as tabelas no armazém de dados de exemplo WideWorldImportersDW num esquema chamado wwi.

Para mostrar a organização das tabelas no conjunto de SQL dedicado, pode utilizar factos, dim e int como prefixos para os nomes das tabelas. A tabela abaixo mostra alguns dos nomes de esquema e tabela para WideWorldImportersDW.

Tabela WideWorldImportersDW Tipo de tabela conjunto de SQL dedicado
City Dimensão wwi. DimCity
Encomenda Fact wwi. FactOrder

Persistência da tabela

As tabelas armazenam dados permanentemente no Armazenamento do Azure, temporariamente no Armazenamento do Azure ou num arquivo de dados externo ao armazém de dados.

Tabela normal

Uma tabela normal armazena dados no Armazenamento do Azure como parte do armazém de dados. A tabela e os dados persistem se uma sessão está ou não aberta. O exemplo abaixo cria uma tabela normal com duas colunas.

CREATE TABLE MyTable (col1 int, col2 int );  

Tabela temporária

Existe apenas uma tabela temporária durante a sessão. Pode utilizar uma tabela temporária para impedir que outros utilizadores vejam resultados temporários. A utilização de tabelas temporárias também reduz a necessidade de limpeza. As tabelas temporárias utilizam o armazenamento local e, em conjuntos de SQL dedicados, podem oferecer um desempenho mais rápido.

O conjunto de SQL sem servidor suporta tabelas temporárias. No entanto, a sua utilização é limitada, uma vez que pode selecionar a partir de uma tabela temporária, mas não pode aderi-la a ficheiros no armazenamento.

Para obter mais informações, veja Tabelas temporárias.

Tabela externa

As tabelas externas apontam para dados localizados no blob do Armazenamento do Azure ou Azure Data Lake Storage.

Importe dados de tabelas externas para conjuntos de SQL dedicados com a instrução CREATE TABLE AS SELECT . Para obter um tutorial de carregamento, veja Utilizar o PolyBase para carregar dados do armazenamento de blobs do Azure.

Para o conjunto de SQL sem servidor, pode utilizar o CETAS para guardar o resultado da consulta numa tabela externa no Armazenamento do Azure.

Tipos de dados

O conjunto de SQL dedicado suporta os tipos de dados mais utilizados. Para obter uma lista dos tipos de dados suportados, veja tipos de dados na referência CREATE TABLE na instrução CREATE TABLE. Para obter mais informações sobre como utilizar tipos de dados, veja Tipos de dados.

Tabelas distribuídas

Uma funcionalidade fundamental do conjunto de SQL dedicado é a forma como pode armazenar e operar em tabelas entre distribuições. O conjunto de SQL dedicado suporta três métodos de distribuição de dados:

  • Round robin (predefinição)
  • Hash
  • Replicada

Tabelas distribuídas com hash

Uma tabela distribuída com hash distribui as linhas com base no valor da coluna de distribuição. Uma tabela distribuída com hash é criada para alcançar um elevado desempenho das consultas em tabelas grandes. Existem vários fatores a considerar ao escolher uma coluna de distribuição.

Para obter mais informações, veja Orientações de estrutura para tabelas distribuídas.

Tabelas replicadas

Uma tabela replicada tem uma cópia completa da tabela disponível em todos os nós de Computação. As consultas são executadas rapidamente em tabelas replicadas porque as associações em tabelas replicadas não requerem movimento de dados. No entanto, a replicação requer armazenamento extra e não é prática para tabelas grandes.

Para obter mais informações, veja Orientações de estrutura para tabelas replicadas.

Tabelas round robin

Uma tabela round robin distribui linhas de tabela uniformemente em todas as distribuições. As linhas são distribuídas aleatoriamente. Carregar dados para uma tabela round robin é rápido. Contudo, as consultas podem exigir mais movimento de dados do que os outros métodos de distribuição.

Para obter mais informações, veja Orientações de estrutura para tabelas distribuídas.

Métodos de distribuição comuns para tabelas

A categoria de tabela determina frequentemente a opção ideal para a distribuição de tabelas.

Categoria de tabela Opção de distribuição recomendada
Fact Utilize a distribuição com hash com o índice columnstore em cluster. O desempenho melhora quando duas tabelas hash são associadas na mesma coluna de distribuição.
Dimensão Utilize replicado para tabelas mais pequenas. Se as tabelas forem demasiado grandes para serem armazenadas em cada Nó de computação, utilize a distribuição com hash.
Processo de teste Utilize round robin para a tabela de teste. A carga com CTAS é rápida. Assim que os dados estiverem na tabela de teste, utilize INSERT... SELECIONE para mover os dados para tabelas de produção.

Partições

Nos conjuntos de SQL dedicados, uma tabela particionada armazena e executa operações nas linhas da tabela de acordo com os intervalos de dados. Por exemplo, uma tabela pode ser particionada por dia, mês ou ano. Pode melhorar o desempenho das consultas através da eliminação de partições, o que limita a análise de consultas aos dados numa partição.

Também pode manter os dados através da mudança de partições. Uma vez que os dados num conjunto de SQL dedicado já estão distribuídos, demasiadas partições podem abrandar o desempenho das consultas. Para obter mais informações, veja Orientações de criação de partições.

Dica

Quando a partição mudar para partições de tabela que não estão vazias, considere utilizar a opção TRUNCATE_TARGET na instrução ALTER TABLE se os dados existentes forem truncados.

O código abaixo muda os dados diários transformados para uma partição SalesFact e substitui quaisquer dados existentes.

ALTER TABLE SalesFact_DailyFinalLoad SWITCH PARTITION 256 TO SalesFact PARTITION 256 WITH (TRUNCATE_TARGET = ON);  

No conjunto de SQL sem servidor, pode limitar os ficheiros/pastas (partições) que serão lidos pela consulta. A criação de partições por caminho é suportada através das funções filepath e fileinfo descritas em Consultar ficheiros de armazenamento. O exemplo seguinte lê uma pasta com dados para o ano 2017:

SELECT
    nyc.filepath(1) AS [year],
    payment_type,
    SUM(fare_amount) AS fare_total
FROM  
    OPENROWSET(
        BULK 'https://sqlondemandstorage.blob.core.windows.net/parquet/taxi/year=*/month=*/*.parquet',
        FORMAT='PARQUET'
    ) AS nyc
WHERE
    nyc.filepath(1) = 2017
GROUP BY
    nyc.filepath(1),
    payment_type
ORDER BY
    nyc.filepath(1),
    payment_type

Índices Columnstore

Por predefinição, o conjunto de SQL dedicado armazena uma tabela como um índice columnstore em cluster. Esta forma de armazenamento de dados obtém um elevado desempenho de consultas e compressão de dados em tabelas grandes. Normalmente, o índice columnstore em cluster é a melhor opção, mas, em alguns casos, um índice em cluster ou uma área dinâmica é a estrutura de armazenamento adequada.

Dica

Uma tabela de área dinâmica pode ser especialmente útil para carregar dados transitórios, como uma tabela de teste, que é transformada numa tabela final.

Para obter uma lista de funcionalidades columnstore, veja Novidades para índices columnstore. Para melhorar o desempenho do índice columnstore, veja Maximizar a qualidade do grupo de linhas para índices columnstore.

Estatísticas

O otimizador de consultas utiliza estatísticas ao nível da coluna quando cria o plano para executar uma consulta. Para melhorar o desempenho das consultas, é importante ter estatísticas em colunas individuais, especialmente colunas utilizadas em associações de consultas. O Synapse SQL suporta a criação automática de estatísticas.

A atualização estatística não ocorre automaticamente. Atualize as estatísticas após a adição ou alteração de um número significativo de linhas. Por exemplo, atualize as estatísticas após uma carga. São fornecidas informações adicionais no artigo Orientação de estatísticas .

Chave primária e chave exclusiva

Para o conjunto de SQL dedicado, a CHAVE PRIMÁRIA só é suportada quando são utilizados NÃO AGRUPADOS e NÃO IMPOSTOS. A restrição EXCLUSIVA só é suportada quando não é imposta. Para obter mais informações, veja o artigo Dedicated SQL pool table constraints (Restrições de tabelas de conjuntos de SQL dedicados ).

Comandos para criar tabelas

Para o conjunto de SQL dedicado, pode criar uma tabela como uma nova tabela vazia. Também pode criar e preencher uma tabela com os resultados de uma instrução select. Seguem-se os comandos T-SQL para criar uma tabela.

Instrução T-SQL Description
CREATE TABLE Cria uma tabela vazia ao definir todas as colunas e opções da tabela.
CRIAR TABELA EXTERNA Cria uma tabela externa. A definição da tabela é armazenada no conjunto de SQL dedicado. Os dados da tabela são armazenados no armazenamento de Blobs do Azure ou Azure Data Lake Storage.
CREATE TABLE AS SELECT Preenche uma nova tabela com os resultados de uma instrução select. As colunas de tabela e os tipos de dados baseiam-se nos resultados da instrução select. Para importar dados, esta instrução pode selecionar a partir de uma tabela externa.
CRIAR TABELA EXTERNA COMO SELECIONAR Cria uma nova tabela externa ao exportar os resultados de uma instrução select para uma localização externa. A localização é armazenamento de Blobs do Azure ou Azure Data Lake Storage.

Alinhar os dados de origem com o armazém de dados

As tabelas do conjunto de SQL dedicado são preenchidas ao carregar dados de outra origem de dados. Para obter uma carga bem-sucedida, o número e os tipos de dados das colunas nos dados de origem têm de estar alinhados com a definição da tabela no armazém de dados.

Nota

Conseguir alinhar os dados pode ser a parte mais difícil de estruturar as suas tabelas.

Se os dados forem provenientes de vários arquivos de dados, pode migrar os dados para o armazém de dados e armazená-lo numa tabela de integração. Assim que os dados estiverem na tabela de integração, pode utilizar o poder do conjunto de SQL dedicado para implementar operações de transformação. Assim que os dados estiverem preparados, pode inseri-los em tabelas de produção.

Funcionalidades de tabela não suportadas

O Conjunto de SQL dedicado suporta muitas, mas não todas, das funcionalidades de tabela oferecidas por outras bases de dados. A lista seguinte mostra algumas das funcionalidades de tabela que não são suportadas no conjunto de SQL dedicado.

Consultas de tamanho da tabela

No conjunto de SQL dedicado, uma forma simples de identificar o espaço e as linhas consumidas por uma tabela em cada uma das 60 distribuições é utilizar o DBCC PDW_SHOWSPACEUSED.

DBCC PDW_SHOWSPACEUSED('dbo.FactInternetSales');

Tenha em atenção que utilizar comandos DBCC pode ser bastante limitador. As vistas de gestão dinâmica (DMVs) mostram mais detalhes do que os comandos DBCC. Comece por criar a vista abaixo.

CREATE VIEW dbo.vTableSizes
AS
WITH base
AS
(
SELECT
 GETDATE()                                                             AS  [execution_time]
, DB_NAME()                                                            AS  [database_name]
, s.name                                                               AS  [schema_name]
, t.name                                                               AS  [table_name]
, QUOTENAME(s.name)+'.'+QUOTENAME(t.name)                              AS  [two_part_name]
, nt.[name]                                                            AS  [node_table_name]
, ROW_NUMBER() OVER(PARTITION BY nt.[name] ORDER BY (SELECT NULL))     AS  [node_table_name_seq]
, tp.[distribution_policy_desc]                                        AS  [distribution_policy_name]
, c.[name]                                                             AS  [distribution_column]
, nt.[distribution_id]                                                 AS  [distribution_id]
, i.[type]                                                             AS  [index_type]
, i.[type_desc]                                                        AS  [index_type_desc]
, nt.[pdw_node_id]                                                     AS  [pdw_node_id]
, pn.[type]                                                            AS  [pdw_node_type]
, pn.[name]                                                            AS  [pdw_node_name]
, di.name                                                              AS  [dist_name]
, di.position                                                          AS  [dist_position]
, nps.[partition_number]                                               AS  [partition_nmbr]
, nps.[reserved_page_count]                                            AS  [reserved_space_page_count]
, nps.[reserved_page_count] - nps.[used_page_count]                    AS  [unused_space_page_count]
, nps.[in_row_data_page_count]
    + nps.[row_overflow_used_page_count]
    + nps.[lob_used_page_count]                                        AS  [data_space_page_count]
, nps.[reserved_page_count]
 - (nps.[reserved_page_count] - nps.[used_page_count])
 - ([in_row_data_page_count]
         + [row_overflow_used_page_count]+[lob_used_page_count])       AS  [index_space_page_count]
, nps.[row_count]                                                      AS  [row_count]
from
    sys.schemas s
INNER JOIN sys.tables t
    ON s.[schema_id] = t.[schema_id]
INNER JOIN sys.indexes i
    ON  t.[object_id] = i.[object_id]
    AND i.[index_id] <= 1
INNER JOIN sys.pdw_table_distribution_properties tp
    ON t.[object_id] = tp.[object_id]
INNER JOIN sys.pdw_table_mappings tm
    ON t.[object_id] = tm.[object_id]
INNER JOIN sys.pdw_nodes_tables nt
    ON tm.[physical_name] = nt.[name]
INNER JOIN sys.dm_pdw_nodes pn
    ON  nt.[pdw_node_id] = pn.[pdw_node_id]
INNER JOIN sys.pdw_distributions di
    ON  nt.[distribution_id] = di.[distribution_id]
INNER JOIN sys.dm_pdw_nodes_db_partition_stats nps
    ON nt.[object_id] = nps.[object_id]
    AND nt.[pdw_node_id] = nps.[pdw_node_id]
    AND nt.[distribution_id] = nps.[distribution_id]
LEFT OUTER JOIN (select * from sys.pdw_column_distribution_properties where distribution_ordinal = 1) cdp
    ON t.[object_id] = cdp.[object_id]
LEFT OUTER JOIN sys.columns c
    ON cdp.[object_id] = c.[object_id]
    AND cdp.[column_id] = c.[column_id]
WHERE pn.[type] = 'COMPUTE'
)
, size
AS
(
SELECT
   [execution_time]
,  [database_name]
,  [schema_name]
,  [table_name]
,  [two_part_name]
,  [node_table_name]
,  [node_table_name_seq]
,  [distribution_policy_name]
,  [distribution_column]
,  [distribution_id]
,  [index_type]
,  [index_type_desc]
,  [pdw_node_id]
,  [pdw_node_type]
,  [pdw_node_name]
,  [dist_name]
,  [dist_position]
,  [partition_nmbr]
,  [reserved_space_page_count]
,  [unused_space_page_count]
,  [data_space_page_count]
,  [index_space_page_count]
,  [row_count]
,  ([reserved_space_page_count] * 8.0)                                 AS [reserved_space_KB]
,  ([reserved_space_page_count] * 8.0)/1000                            AS [reserved_space_MB]
,  ([reserved_space_page_count] * 8.0)/1000000                         AS [reserved_space_GB]
,  ([reserved_space_page_count] * 8.0)/1000000000                      AS [reserved_space_TB]
,  ([unused_space_page_count]   * 8.0)                                 AS [unused_space_KB]
,  ([unused_space_page_count]   * 8.0)/1000                            AS [unused_space_MB]
,  ([unused_space_page_count]   * 8.0)/1000000                         AS [unused_space_GB]
,  ([unused_space_page_count]   * 8.0)/1000000000                      AS [unused_space_TB]
,  ([data_space_page_count]     * 8.0)                                 AS [data_space_KB]
,  ([data_space_page_count]     * 8.0)/1000                            AS [data_space_MB]
,  ([data_space_page_count]     * 8.0)/1000000                         AS [data_space_GB]
,  ([data_space_page_count]     * 8.0)/1000000000                      AS [data_space_TB]
,  ([index_space_page_count]  * 8.0)                                   AS [index_space_KB]
,  ([index_space_page_count]  * 8.0)/1000                              AS [index_space_MB]
,  ([index_space_page_count]  * 8.0)/1000000                           AS [index_space_GB]
,  ([index_space_page_count]  * 8.0)/1000000000                        AS [index_space_TB]
FROM base
)
SELECT *
FROM size
;

Resumo do espaço da tabela

Esta consulta devolve as linhas e o espaço por tabela. O resumo do espaço da tabela permite-lhe ver que tabelas são as suas maiores tabelas. Também verá se a distribuição é round robin, replicada ou com hash. Para tabelas distribuídas com hash, a consulta mostra a coluna de distribuição.

SELECT
     database_name
,    schema_name
,    table_name
,    distribution_policy_name
,      distribution_column
,    index_type_desc
,    COUNT(distinct partition_nmbr) as nbr_partitions
,    SUM(row_count)                 as table_row_count
,    SUM(reserved_space_GB)         as table_reserved_space_GB
,    SUM(data_space_GB)             as table_data_space_GB
,    SUM(index_space_GB)            as table_index_space_GB
,    SUM(unused_space_GB)           as table_unused_space_GB
FROM
    dbo.vTableSizes
GROUP BY
     database_name
,    schema_name
,    table_name
,    distribution_policy_name
,      distribution_column
,    index_type_desc
ORDER BY
    table_reserved_space_GB desc
;

Espaço de tabela por tipo de distribuição

SELECT
     distribution_policy_name
,    SUM(row_count)                as table_type_row_count
,    SUM(reserved_space_GB)        as table_type_reserved_space_GB
,    SUM(data_space_GB)            as table_type_data_space_GB
,    SUM(index_space_GB)           as table_type_index_space_GB
,    SUM(unused_space_GB)          as table_type_unused_space_GB
FROM dbo.vTableSizes
GROUP BY distribution_policy_name
;

Espaço da tabela por tipo de índice

SELECT
     index_type_desc
,    SUM(row_count)                as table_type_row_count
,    SUM(reserved_space_GB)        as table_type_reserved_space_GB
,    SUM(data_space_GB)            as table_type_data_space_GB
,    SUM(index_space_GB)           as table_type_index_space_GB
,    SUM(unused_space_GB)          as table_type_unused_space_GB
FROM dbo.vTableSizes
GROUP BY index_type_desc
;

Resumo do espaço de distribuição

SELECT
    distribution_id
,    SUM(row_count)                as total_node_distribution_row_count
,    SUM(reserved_space_MB)        as total_node_distribution_reserved_space_MB
,    SUM(data_space_MB)            as total_node_distribution_data_space_MB
,    SUM(index_space_MB)           as total_node_distribution_index_space_MB
,    SUM(unused_space_MB)          as total_node_distribution_unused_space_MB
FROM dbo.vTableSizes
GROUP BY     distribution_id
ORDER BY    distribution_id
;

Passos seguintes

Depois de criar as tabelas para o armazém de dados, o próximo passo é carregar dados para a tabela. Para obter um tutorial de carregamento, veja Carregar dados para o conjunto de SQL dedicado.