Mais estatísticas do site em seu pequeno armazenamento

Analisando as estatísticas do site, obtemos uma visão do que está acontecendo com ele. Comparamos os resultados com outros conhecimentos sobre o produto ou serviço e assim melhoramos nossa experiência.

Quando a análise dos primeiros resultados é concluída, refletimos sobre as informações e tiramos conclusões, iniciando a próxima etapa. Surge a pergunta: o que aconteceria se olhássemos os dados de outra perspectiva?

Nesta etapa, há limitações nas ferramentas de análise. Esta é uma das razões pelas quais a ferramenta Google Analytics não era suficiente para mim, devido à sua capacidade limitada de visualizar e manipular os dados.

Sempre quis carregar rapidamente os dados básicos (dados mestres), adicionar um novo nível de agregação ou interpretar os valores existentes de outra forma.

É fácil fazer isso em seu pequeno armazém com base no arquivo access.log e para isso, basta usar a linguagem SQL.

Então, a quais perguntas eu gostaria de encontrar respostas?

O que e quando mudou no site

A história das mudanças nos dados básicos (dados mestres) sempre é interessante.

Mais estatísticas do site em seu pequeno armazenamento

SQL consulta de relatório

SELECT
	1 as 'SideStackedBar: Atualizações de Conteúdo por Mês',
	strftime('%m/%Y', datetime(UPDATE_DT, 'unixepoch')) AS 'Dia',
	COUNT(CASE WHEN PAGE_TITLE != 'n.a.' THEN DIM_REQUEST_ID END) AS 'Atualizações de páginas da web',
	COUNT(CASE WHEN PAGE_DESCR = 'IMAGES' THEN DIM_REQUEST_ID END) AS 'Uploads de imagens',
	COUNT(CASE WHEN PAGE_DESCR = 'VIDEO' THEN DIM_REQUEST_ID END) AS 'Uploads de vídeo',
	COUNT(CASE WHEN PAGE_DESCR = 'AUDIO' THEN DIM_REQUEST_ID END) AS 'Uploads de áudio'
FROM DIM_REQUEST
WHERE PAGE_TITLE != 'n.a.' OR PAGE_DESCR != 'n.a.'
GROUP BY strftime('%m/%Y', datetime(UPDATE_DT, 'unixepoch'))
ORDER BY UPDATE_DT

Por exemplo, em algum momento, foi feita uma otimização para motores de busca ou novo conteúdo foi adicionado ao site, e por isso espera-se um aumento no tráfego.

Grupos de usuários

O exemplo mais simples de um grupo pode ser o agente do usuário ou o nome do sistema operacional.

A medição dos agentes de usuário acumulou cerca de mil registros e eu estava interessado em ver a dinâmica da distribuição dos agentes dentro do grupo.

Mais estatísticas do site em seu pequeno armazenamento

SQL consulta de relatório

SELECT
	1 AS 'SideStackedBar: Agentes de Usuário',
	AGENT_OS AS 'SO',
	SUM(CASE WHEN AGENT_BOT = 'n.a.' THEN 1 ELSE 0 END ) AS 'Agente de Usuário de Usuários',
	SUM(CASE WHEN AGENT_BOT != 'n.a.' THEN 1 ELSE 0 END ) AS 'Agente de Usuário de Bots'
FROM DIM_USER_AGENT
WHERE DIM_USER_AGENT_ID != -1
GROUP BY AGENT_OS
ORDER BY 3 DESC

A maioria das combinações diferentes de agentes que acessam o site vêm do mundo Windows. Entre os agentes indefinidos estão, por exemplo, WhatsApp, PocketImageCache, PlayStation, SmartTV, etc.

Atividade dos grupos de usuários por semanas

Combinando alguns grupos, podemos observar a distribuição de sua atividade.

Por exemplo, os usuários do cluster Linux consomem mais tráfego no site do que todos os outros.

Mais estatísticas do site em seu pequeno armazenamento

SQL consulta de relatório

SELECT
1 AS 'StackedBar: Volume de Tráfego por Sistema Operacional do Usuário e por Semana',
strftime('%W semana', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Semana',
SUM(CASE WHEN USG.AGENT_OS IN ('Android', 'Linux') THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Usuários Android/Linux',
SUM(CASE WHEN USG.AGENT_OS IN ('Windows') THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Usuários Windows',
SUM(CASE WHEN USG.AGENT_OS IN ('Macintosh', 'iOS') THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Usuários Mac/iOS',
SUM(CASE WHEN USG.AGENT_OS IN ('n.a.', 'BlackBerry') THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Outro'
FROM
  FCT_ACCESS_USER_AGENT_DD FCT,
  DIM_USER_AGENT USG,
  DIM_HTTP_STATUS HST
WHERE FCT.DIM_USER_AGENT_ID = USG.DIM_USER_AGENT_ID
  AND FCT.DIM_HTTP_STATUS_ID = HST.DIM_HTTP_STATUS_ID
  AND USG.AGENT_BOT = 'n.a.' /* apenas usuários */
  AND HST.STATUS_GROUP IN ('Bem-sucedido') /* boas páginas */
  AND datetime(FCT.EVENT_DT, 'unixepoch') > date('now', '-3 meses')
GROUP BY strftime('%W semana', datetime(FCT.EVENT_DT, 'unixepoch'))
ORDER BY FCT.EVENT_DT

Consumo intenso de tráfego

A tabela mostra os grupos de usuários mais ativos e os dias de sua atividade.
Os mais ativos pertencem ao cluster Linux.

Mais estatísticas do site em seu pequeno armazenamento

SQL consulta de relatório

SELECT
1 AS 'Tabela: Agente de Usuário com Alto Uso',
strftime('%d.%m.%Y', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Dia',
ROUND(1.0 * SUM(FCT.BYTES) / 1000000, 1) AS 'Tráfego MB',
ROUND(1.0 * SUM(FCT.IP_CNT) / SUM(1), 1) AS 'IPs',
ROUND(1.0 * SUM(FCT.REQUEST_CNT) / SUM(1), 1) AS 'Solicitações',
USA.DIM_USER_AGENT_ID AS 'ID',
MAX(USA.USER_AGENT_NK) AS 'Agente de Usuário',
MAX(USA.AGENT_BOT) AS 'Bot'
FROM
FCT_ACCESS_USER_AGENT_DD FCT,
DIM_USER_AGENT USA
WHERE FCT.DIM_USER_AGENT_ID = USA.DIM_USER_AGENT_ID
  AND datetime(FCT.EVENT_DT, 'unixepoch') >= date('now', '-30 dias')
GROUP BY USA.DIM_USER_AGENT_ID, strftime('%d.%m.%Y', datetime(FCT.EVENT_DT, 'unixepoch')) 
ORDER BY SUM(FCT.BYTES) DESC, FCT.EVENT_DT
LIMIT 10

Usando os atributos dia e ID do agente, é possível encontrar e rastrear rapidamente as estatísticas por dias de grupos específicos de usuários. Se necessário, pode-se rapidamente encontrar informações detalhadas na tabela de estágio.

Como obter informações?

Informação do arquivo access.log pode ser feita ainda mais eficaz se forem integradas fontes de dados adicionais, introduzidos novos níveis de agregação e agrupamento.

Dados e entidades básicas

Os dados básicos incluem informações sobre entidades: páginas da web, imagens, conteúdos de vídeo e áudio, e no caso de lojas — produtos.

As próprias entidades funcionam como dimensões, e o processo de salvar as alterações dos atributos é chamado de historização. Na base de dados, esse processo é frequentemente implementado na forma de dimensões lentamente mutáveis (SCD).

A fonte de dados básicas pode ser a mais variada, portanto, quase sempre elas precisam ser integradas.

Dimensão lentamente mutável

A dimensão DIM_REQUEST conterá informações sobre solicitações no site em forma histórica.

Tabela SCD2

CRIE UMA TABELA DIM_REQUEST ( 
  DIM_REQUEST_ID      INTEIRO NÃO NULO CHAVE PRIMÁRIA AUTOINCREMENT,
  DIM_REQUEST_ID_HIST INTEIRO NÃO NULO DEFAULT -1,
  REQUEST_NK          TEXTO NÃO NULO DEFAULT 'n.a.', 
  PAGE_TITLE          TEXTO NÃO NULO DEFAULT 'n.a.',
  PAGE_DESCR          TEXTO NÃO NULO DEFAULT 'n.a.',
  PAGE_KEYWORDS       TEXTO NÃO NULO DEFAULT 'n.a.',
  DELETE_FLAG         INTEIRO NÃO NULO DEFAULT 0,
  UPDATE_DT           INTEIRO NÃO NULO DEFAULT 0,
  ÚNICO (REQUEST_NK, DIM_REQUEST_ID_HIST)
);
INSERIR NA DIM_REQUEST (DIM_REQUEST_ID) VALORES (-1);

Além disso, criaremos uma visão que sempre exibe todos os registros em seu estado mais recente. Isso é necessário para carregar a própria medida.

Mais estatísticas do site em seu pequeno armazenamento

Visão atual SCD2

/* Content: actual view on scd table */
SELECT HI.DIM_REQUEST_ID,
  HI.DIM_REQUEST_ID_HIST,
  HI.REQUEST_NK,
  HI.PAGE_TITLE,
  HI.PAGE_DESCR,
  HI.PAGE_KEYWORDS,
  NK.CNT AS HIST_CNT,
  HI.DELETE_FLAG,
  strftime('%d.%m.%Y %H:%M', datetime(HI.UPDATE_DT, 'unixepoch')) AS UPDATE_DT
FROM
  ( SELECT REQUEST_NK, MAX(DIM_REQUEST_ID) AS DIM_REQUEST_ID, SUM(1) AS CNT
    FROM DIM_REQUEST
    GROUP BY REQUEST_NK
  ) NK,
  DIM_REQUEST HI
WHERE 1 = 1
  AND NK.REQUEST_NK = HI.REQUEST_NK
  AND NK.DIM_REQUEST_ID = HI.DIM_REQUEST_ID;

E a visão, onde para cada registro, foi coletada informação histórica. Isso é necessário para construir uma relação historicamente correta com os fatos.

Mais estatísticas do site em seu pequeno armazenamento

Visão histórica SCD2

/* Content: actual view on scd table */
SELECT SCD.DIM_REQUEST_ID,
  SCD.DIM_REQUEST_ID_HIST,
  SCD.REQUEST_NK,
  SCD.PAGE_TITLE,
  SCD.PAGE_DESCR,
  SCD.PAGE_KEYWORDS,
  SCD.DELETE_FLAG,
  CASE
    WHEN HIS.UPDATE_DT IS NULL
    THEN 1
    ELSE 0 END ACTIVE_FLAG,
  SCD.DIM_REQUEST_ID_HIST AS ID_FROM,
  SCD.DIM_REQUEST_ID AS ID_TO,
  CASE
    WHEN SCD.DIM_REQUEST_ID_HIST=-1
    THEN 3600
    ELSE IFNULL(SCD.UPDATE_DT,3600)
  END AS TIME_FROM,
  CASE
    WHEN HIS.UPDATE_DT IS NULL
    THEN 253370764800
    ELSE HIS.UPDATE_DT
  END AS TIME_TO,
  CASE
    WHEN SCD.DIM_REQUEST_ID_HIST=-1
    THEN STRFTIME('%d.%m.%Y %H:%M', DATETIME(3600, 'unixepoch'))
    ELSE STRFTIME('%d.%m.%Y %H:%M', DATETIME(IFNULL(SCD.UPDATE_DT,3600), 'unixepoch'))
  END AS ACTIVE_FROM,
  CASE
    WHEN HIS.UPDATE_DT IS NULL
    THEN STRFTIME('%d.%m.%Y %H:%M', DATETIME(253370764800, 'unixepoch'))
    ELSE STRFTIME('%d.%m.%Y %H:%M', DATETIME(HIS.UPDATE_DT, 'unixepoch'))
  END AS ACTIVE_TO
FROM
  DIM_REQUEST SCD
  LEFT OUTER JOIN DIM_REQUEST HIS
  ON SCD.REQUEST_NK = HIS.REQUEST_NK AND SCD.DIM_REQUEST_ID = HIS.DIM_REQUEST_ID_HIST;

Agregação de dados

A compressão (agregação) permite avaliar os dados em um nível mais alto e identificar anomalias e tendências que não são visíveis em relatórios detalhados.

Por exemplo, na medida com códigos de status de solicitações DIM_HTTP_STATUS adicionaremos um grupo:

STATUS / GRUPO
0xx / n.a.
1xx / Informativo
2xx / Bem-sucedido
3xx / Redirecionamento
4xx / Erro do Cliente
5xx / Erro do Servidor

A medida de agentes de usuário DIM_USER_AGENT conterá os atributos AGENT_OS e AGENT_BOT, responsáveis pelos grupos. Eles podem ser preenchidos durante o processo ETL:

Carregamento DIM_USER_AGENT

/* Propagate the user agent from access log */
INSERT INTO DIM_USER_AGENT (USER_AGENT_NK, AGENT_OS, AGENT_ENGINE, AGENT_DEVICE, AGENT_BOT, UPDATE_DT)
WITH CLS AS (
	SELECT BROWSER
	FROM STG_ACCESS_LOG WHERE LENGTH(BROWSER)>1
	GROUP BY BROWSER
)
SELECT
	CLS.BROWSER AS USER_AGENT_NK,
	CASE
	WHEN INSTR(CLS.BROWSER,'Macintosh')>0
		THEN 'Macintosh'
	WHEN INSTR(CLS.BROWSER,'iPhone')>0
			 OR INSTR(CLS.BROWSER,'iPad')>0
			 OR INSTR(CLS.BROWSER,'iPod')>0
			 OR INSTR(CLS.BROWSER,'Apple TV')>0
			 OR INSTR(CLS.BROWSER,'Darwin')>0
		THEN 'iOS'
	WHEN INSTR(CLS.BROWSER,'Android')>0
		THEN 'Android'
	WHEN INSTR(CLS.BROWSER,'X11;')>0 OR INSTR(CLS.BROWSER,'Wayland;')>0 OR INSTR(CLS.BROWSER,'linux-gnu')>0
		THEN 'Linux'
	WHEN INSTR(CLS.BROWSER,'BB10;')>0 OR INSTR(CLS.BROWSER,'BlackBerry')>0
		THEN 'BlackBerry'
	WHEN INSTR(CLS.BROWSER,'Windows')>0
		THEN 'Windows'
	ELSE 'n.a.' END AS AGENT_OS, -- OS
	CASE
	WHEN INSTR(CLS.BROWSER,'AppleCoreMedia')>0
		THEN 'AppleWebKit'
	WHEN INSTR(CLS.BROWSER,') ')>1 AND LENGTH(CLS.BROWSER)>INSTR(CLS.BROWSER,') ')
		THEN COALESCE(SUBSTR(CLS.BROWSER, INSTR(CLS.BROWSER,') ')+2, LENGTH(CLS.BROWSER) - INSTR(CLS.BROWSER,') ')-1), 'N/A')
	ELSE 'n.a.' END AS AGENT_ENGINE, -- Engine
	CASE
	WHEN INSTR(CLS.BROWSER,'iPhone')>0
		THEN 'iPhone'
	WHEN INSTR(CLS.BROWSER,'iPad')>0
		THEN 'iPad'
	WHEN INSTR(CLS.BROWSER,'iPod')>0
		THEN 'iPod'
	WHEN INSTR(CLS.BROWSER,'Apple TV')>0
		THEN 'Apple TV'
	WHEN INSTR(CLS.BROWSER,'Android ')>0 AND INSTR(CLS.BROWSER,'Build')>0
		THEN COALESCE(SUBSTR(CLS.BROWSER, INSTR(CLS.BROWSER,'Android '), INSTR(CLS.BROWSER,'Build')-INSTR(CLS.BROWSER,'Android ')), 'n.a.')
	WHEN INSTR(CLS.BROWSER,'Android ')>0 AND INSTR(CLS.BROWSER,'MIUI')>0
		THEN COALESCE(SUBSTR(CLS.BROWSER, INSTR(CLS.BROWSER,'Android '), INSTR(CLS.BROWSER,'MIUI')-INSTR(CLS.BROWSER,'Android ')), 'n.a.')
	ELSE 'n.a.' END AS AGENT_DEVICE, -- Device
	CASE
	WHEN INSTR(LOWER(CLS.BROWSER),'yandex.com')>0
		THEN 'yandex'
	WHEN INSTR(LOWER(CLS.BROWSER),'googlebot')>0
		THEN 'google'
	WHEN INSTR(LOWER(CLS.BROWSER),'bingbot')>0
		THEN 'microsoft'
	WHEN INSTR(LOWER(CLS.BROWSER),'ahrefsbot')>0
		THEN 'ahrefs'
	WHEN INSTR(LOWER(CLS.BROWSER),'jobboersebot')>0 OR INSTR(LOWER(CLS.BROWSER),'jobkicks')>0
		THEN 'job.de'
	WHEN INSTR(LOWER(CLS.BROWSER),'mail.ru')>0
		THEN 'mail.ru'
	WHEN INSTR(LOWER(CLS.BROWSER),'baiduspider')>0
		THEN 'baidu'
	WHEN INSTR(LOWER(CLS.BROWSER),'mj12bot')>0
		THEN 'majestic-12'
	WHEN INSTR(LOWER(CLS.BROWSER),'duckduckgo')>0
		THEN 'duckduckgo'
	WHEN INSTR(LOWER(CLS.BROWSER),'bytespider')>0
		THEN 'bytespider'
	WHEN INSTR(LOWER(CLS.BROWSER),'360spider')>0
		THEN 'so.360.cn'
	WHEN INSTR(LOWER(CLS.BROWSER),'compatible')>0 OR INSTR(LOWER(CLS.BROWSER),'http')>0
		OR INSTR(LOWER(CLS.BROWSER),'libwww')>0 OR INSTR(LOWER(CLS.BROWSER),'spider')>0
		OR INSTR(LOWER(CLS.BROWSER),'java')>0 OR INSTR(LOWER(CLS.BROWSER),'python')>0
		OR INSTR(LOWER(CLS.BROWSER),'robot')>0 OR INSTR(LOWER(CLS.BROWSER),'curl')>0 OR INSTR(LOWER(CLS.BROWSER),'wget')>0
		THEN 'other'
	ELSE 'n.a.' END AS AGENT_BOT, -- Bot
	STRFTIME('%s','now') AS UPDATE_DT
FROM CLS
LEFT OUTER JOIN DIM_USER_AGENT TRG
ON CLS.BROWSER = TRG.USER_AGENT_NK
WHERE TRG.DIM_USER_AGENT_ID IS NULL

Integração de Dados

Inclui a organização da transferência de dados do sistema operacional para o relatório. Para isso, é necessário criar uma tabela de estágio com uma estrutura semelhante à do fonte.

Na etapa de estágio, a informação sobre páginas web chega a partir do backup do CMS na forma de consultas de inserção.

O carregamento da tabela histórica DIM_REQUEST com dados básicos ocorre em três etapas: carregamento de novas chaves e atributos, atualização dos existentes e registro dos registros removidos.

Carregamento de novos registros SCD2

/* Load request table SCD from master data */
INSERT INTO DIM_REQUEST (DIM_REQUEST_ID_HIST, REQUEST_NK, PAGE_TITLE, PAGE_DESCR, PAGE_KEYWORDS, DELETE_FLAG, UPDATE_DT)
WITH CLS  AS ( -- prepare keys
	SELECT
	'/' || NAME AS REQUEST_NK,
	TITLE       AS PAGE_TITLE,
	CASE WHEN DESCRIPTION = '' OR DESCRIPTION IS NULL
	     THEN 'n.a.' ELSE DESCRIPTION
	END AS PAGE_DESCR,
	CASE WHEN KEYWORDS = '' OR KEYWORDS IS NULL
	     THEN 'n.a.' ELSE KEYWORDS
	END AS PAGE_KEYWORDS
	FROM STG_CMS_MENU
	WHERE CONTENT_TYPE != 'folder' -- only web pages
	  AND PAGE_TITLE != 'n.a.' -- master data which make sense
)
/* new records from stage: CLS */
SELECT
	-1 AS DIM_REQUEST_ID_HIST,
	CLS.REQUEST_NK,
	CLS.PAGE_TITLE,
	CLS.PAGE_DESCR,
	CLS.PAGE_KEYWORDS,
	0 AS DELETE_FLAG,
	STRFTIME('%s','now') AS UPDATE_DT
FROM CLS
LEFT OUTER JOIN
 (
	SELECT
	DIM_REQUEST_ID,
	REQUEST_NK,
	PAGE_TITLE,
	PAGE_DESCR,
	PAGE_KEYWORDS
	FROM DIM_REQUEST_V_ACT
) TRG ON CLS.REQUEST_NK = TRG.REQUEST_NK
WHERE TRG.REQUEST_NK IS NULL -- no such record in data mart

Atualização de atributos SCD2

/* Load request table SCD from master data */
INSERT INTO DIM_REQUEST (DIM_REQUEST_ID_HIST, REQUEST_NK, PAGE_TITLE, PAGE_DESCR, PAGE_KEYWORDS, DELETE_FLAG, UPDATE_DT)
WITH CLS  AS ( -- prepare keys
	SELECT
	'/' || NAME AS REQUEST_NK,
	TITLE       AS PAGE_TITLE,
	CASE WHEN DESCRIPTION = '' OR DESCRIPTION IS NULL
	     THEN 'n.a.' ELSE DESCRIPTION
	END AS PAGE_DESCR,
	CASE WHEN KEYWORDS = '' OR KEYWORDS IS NULL
	     THEN 'n.a.' ELSE KEYWORDS
	END AS PAGE_KEYWORDS
	FROM STG_CMS_MENU
	WHERE CONTENT_TYPE != 'folder' -- only web pages
	  AND PAGE_TITLE != 'n.a.' -- master data which make sense
)
/* updated records from stage: CLS and build reference to history: HIST */
SELECT
	HIST.DIM_REQUEST_ID AS DIM_REQUEST_ID_HIST,
	HIST.REQUEST_NK,
	CLS.PAGE_TITLE,
	CLS.PAGE_DESCR,
	CLS.PAGE_KEYWORDS,
	0 AS DELETE_FLAG,
	STRFTIME('%s','now') AS UPDATE_DT
FROM CLS,
     DIM_REQUEST_V_ACT TRG,
     DIM_REQUEST HIST
WHERE CLS.REQUEST_NK = TRG.REQUEST_NK
  AND TRG.DIM_REQUEST_ID = HIST.DIM_REQUEST_ID
  AND ( CLS.PAGE_TITLE != HIST.PAGE_TITLE /* changes only */
     OR CLS.PAGE_DESCR != HIST.PAGE_DESCR
     OR CLS.PAGE_KEYWORDS != HIST.PAGE_KEYWORDS )

Registros removidos SCD2

/* Load request table SCD from master data */
INSERT INTO DIM_REQUEST (DIM_REQUEST_ID_HIST, REQUEST_NK, PAGE_TITLE, PAGE_DESCR, PAGE_KEYWORDS, DELETE_FLAG, UPDATE_DT)
WITH CLS  AS ( -- prepare keys
	SELECT
	'/' || NAME AS REQUEST_NK,
	TITLE       AS PAGE_TITLE
	FROM STG_CMS_MENU
	WHERE CONTENT_TYPE != 'folder' -- only web pages
	  AND PAGE_TITLE != 'n.a.' -- master data which make sense
)
/*  deleted records in data mart: TRG */
SELECT
	TRG.DIM_REQUEST_ID AS DIM_REQUEST_ID_HIST,
	TRG.REQUEST_NK,
	TRG.PAGE_TITLE,
	TRG.PAGE_DESCR,
	TRG.PAGE_KEYWORDS,
	1 AS DELETE_FLAG,
	STRFTIME('%s','now') AS UPDATE_DT
FROM (
	SELECT
	DIM_REQUEST_ID,
	REQUEST_NK,
	PAGE_TITLE,
	PAGE_DESCR,
	PAGE_KEYWORDS
	FROM DIM_REQUEST_V_ACT
	WHERE PAGE_TITLE != 'n.a.' -- track master data only
	  AND DELETE_FLAG = 0 -- not already deleted
) TRG
LEFT OUTER JOIN CLS ON TRG.REQUEST_NK = CLS.REQUEST_NK
WHERE CLS.REQUEST_NK IS NULL -- no such record in stage

Cada fonte de dados deve ser acompanhada de uma descrição formal, por exemplo, em um arquivo readme.txt:

Destinatário dos dados formal/técnico: nome, endereço eletrônico
Fornecedor de dados formal/técnico: nome, endereço eletrônico
Fonte de dados: caminho para o arquivo, nomes dos serviços
Informação sobre acesso aos dados: usuários e senhas

Um esquema de movimentação de dados ajudará no processo de manutenção e atualização, por exemplo, em formato de texto:

Movimento do arquivo. Origem: ftp.domain.net: /logs/access.log Destino: /var/www/access.log
Leitura na etapa. Destino: STG_ACCESS_LOG
Carregamento e transformação. Objetivo: FCT_ACCESS_REQUEST_REF_HH
Carregamento e transformação. Objetivo: FCT_ACCESS_USER_AGENT_DD
Relatório. Objetivo: /var/www/report.html

Saída

Assim, o artigo descreve mecanismos como a integração de dados básicos e a introdução de novos níveis de agregação. Eles são necessários para construir armazéns de dados com o objetivo de obter conhecimentos adicionais e melhorar a qualidade da informação.

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster