Más estadísticas del sitio en su pequeño almacenamiento

Analizando las estadísticas del sitio, obtenemos una idea de lo que está sucediendo con él. Comparamos los resultados con otros conocimientos sobre el producto o servicio y así mejoramos nuestra experiencia.

Cuando se ha completado el análisis de los primeros resultados, se reflexiona sobre la información y se sacan conclusiones, comienza la siguiente etapa. Surgen ideas: ¿qué pasaría si miramos los datos desde otro ángulo?

En esta etapa hay limitaciones en las herramientas de análisis. Esta es una de las razones por las que la herramienta Google Analytics no me resultó suficiente, debido a la capacidad limitada para ver y manipular mis datos.

Siempre he querido cargar rápidamente los datos básicos (metadatos), agregar otro nivel de agregación o interpretar de otra manera los valores existentes.

Esto es fácil de hacer en su pequeño almacén basado en el archivo access.log y para ello es suficiente con el lenguaje SQL.

Entonces, ¿qué preguntas quería responder?

Qué y cuándo se cambió en el sitio

La historia de los cambios en los datos básicos (metadatos) siempre es interesante.

Más estadísticas del sitio en su pequeño almacenamiento

Consulta SQL del informe

SELECT
	1 as 'SideStackedBar: Actualizaciones de Contenido por Meses',
	strftime('%m/%Y', datetime(UPDATE_DT, 'unixepoch')) AS 'Día',
	COUNT(CASE WHEN PAGE_TITLE != 'n.a.' THEN DIM_REQUEST_ID END) AS 'Actualizaciones de páginas web',
	COUNT(CASE WHEN PAGE_DESCR = 'IMAGES' THEN DIM_REQUEST_ID END) AS 'Cargas de imágenes',
	COUNT(CASE WHEN PAGE_DESCR = 'VIDEO' THEN DIM_REQUEST_ID END) AS 'Cargas de videos',
	COUNT(CASE WHEN PAGE_DESCR = 'AUDIO' THEN DIM_REQUEST_ID END) AS 'Cargas de audio'
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 ejemplo, en algún momento se llevó a cabo una optimización de búsqueda o se añadió nuevo contenido al sitio, por lo tanto, se espera un aumento en el tráfico.

Grupos de usuarios

El ejemplo más sencillo de un grupo puede ser un agente de usuario o el nombre del sistema operativo.

La medición de los agentes de usuario ha acumulado alrededor de mil registros y me interesaba ver la dinámica de distribución de los agentes dentro del grupo.

Más estadísticas del sitio en su pequeño almacenamiento

Consulta SQL del informe

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

La mayor cantidad de combinaciones de agentes provienen del mundo de Windows. Entre los indefinidos se encuentran opciones como WhatsApp, PocketImageCache, PlayStation, SmartTV, etc.

Actividad de grupos de usuarios por semanas

Al combinar algunos grupos, se puede observar la distribución de su actividad.

Por ejemplo, los usuarios del clúster de Linux consumen más tráfico en el sitio que los demás.

Más estadísticas del sitio en su pequeño almacenamiento

Consulta SQL del informe

SELECT
1 as 'StackedBar: Volumen de Tráfico por SO de Usuario y 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 'Usuarios Android/Linux',
SUM(CASE WHEN USG.AGENT_OS IN ('Windows') THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Usuarios Windows',
SUM(CASE WHEN USG.AGENT_OS IN ('Macintosh', 'iOS') THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Usuarios Mac/iOS',
SUM(CASE WHEN USG.AGENT_OS IN ('n.a.', 'BlackBerry') THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Otros'
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.' /* solo usuarios */
  AND HST.STATUS_GROUP IN ('Exitoso') /* buenas páginas */
  AND datetime(FCT.EVENT_DT, 'unixepoch') > date('now', '-3 month')
GROUP BY strftime('%W semana', datetime(FCT.EVENT_DT, 'unixepoch'))
ORDER BY FCT.EVENT_DT

Consumo intensivo de tráfico

La tabla muestra los grupos de usuarios más activos y los días de su actividad.
Los más activos pertenecen al clúster de Linux.

Más estadísticas del sitio en su pequeño almacenamiento

Consulta SQL del informe

SELECT
1 AS 'Tabla: Agente de Usuario con Uso Alto',
strftime('%d.%m.%Y', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Día',
ROUND(1.0 * SUM(FCT.BYTES) / 1000000, 1) AS 'Tráfico MB',
ROUND(1.0 * SUM(FCT.IP_CNT) / SUM(1), 1) AS 'IPs',
ROUND(1.0 * SUM(FCT.REQUEST_CNT) / SUM(1), 1) AS 'Solicitudes',
USA.DIM_USER_AGENT_ID AS 'ID',
MAX(USA.USER_AGENT_NK) AS 'Agente de Usuario',
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 day')
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

Utilizando los atributos día e ID de agente, se puede encontrar y rastrear rápidamente las estadísticas por días de grupos de usuarios específicos. Si es necesario, se puede encontrar rápidamente información detallada en la tabla de etapa.

¿Cómo obtener información?

La información del archivo access.log se puede hacer aún más eficiente si se integran fuentes de datos adicionales, se introducen nuevos niveles de agregación y agrupación.

Datos y entidades básicas

Los datos básicos incluyen información sobre las entidades: páginas web, imágenes, contenido de vídeo y audio, en el caso de una tienda, productos.

Las entidades en sí actúan como dimensiones, y el proceso de guardar los cambios de atributos se llama historización. En la base de datos, este proceso a menudo se realiza en forma de dimensiones que cambian lentamente (SCD).

Las fuentes de datos básicas pueden ser sistemas muy diversos, por lo tanto, casi siempre es necesario integrarlos.

Dimensión de cambio lento

La dimensión DIM_REQUEST contendrá información sobre las solicitudes en el sitio en forma histórica.

Tabla SCD2

CREAR TABLA DIM_REQUEST ( 
  DIM_REQUEST_ID      ENTERO NO NULO CLAVE PRINCIPAL AUTOINCREMENT,
  DIM_REQUEST_ID_HIST ENTERO NO NULO POR DEFECTO -1,
  REQUEST_NK          TEXTO NO NULO POR DEFECTO 'n.a.', 
  PAGE_TITLE          TEXTO NO NULO POR DEFECTO 'n.a.',
  PAGE_DESCR          TEXTO NO NULO POR DEFECTO 'n.a.',
  PAGE_KEYWORDS       TEXTO NO NULO POR DEFECTO 'n.a.',
  DELETE_FLAG         ENTERO NO NULO POR DEFECTO 0,
  UPDATE_DT           ENTERO NO NULO POR DEFECTO 0,
  ÚNICO (REQUEST_NK, DIM_REQUEST_ID_HIST)
);
INSERTAR EN DIM_REQUEST (DIM_REQUEST_ID) VALORES (-1);

Además, crearemos una vista que siempre muestre todos los registros en su último estado. Esto es necesario para cargar la propia dimensión.

Más estadísticas del sitio en su pequeño almacenamiento

Vista actual 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;

Y una vista donde se recopila información histórica para cada registro. Esto es necesario para construir una relación históricamente precisa con los hechos.

Más estadísticas del sitio en su pequeño almacenamiento

Vista 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;

Agregación de datos

La agregación permite evaluar los datos a un nivel más alto y detectar anomalías y tendencias que no son visibles en los informes detallados.

Por ejemplo, en la dimensión con códigos de estado de solicitudes DIM_HTTP_STATUS agregaremos el grupo:

ESTADO / GRUPO
0xx / n.a.
1xx / Informativo
2xx / Exitoso
3xx / Redirección
4xx / Error del Cliente
5xx / Error del Servidor

La dimensión de agentes de usuario DIM_USER_AGENT contendrá los atributos AGENT_OS y AGENT_BOT, que corresponden a los grupos. Estos se pueden rellenar durante el proceso ETL:

Carga de 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

Integración de datos

Incluye la organización de la transferencia de datos desde el sistema operativo al informe. Para ello, es necesario crear una tabla de etapa con una estructura similar a la del origen.

En la etapa, la información sobre las páginas web proviene de una copia de seguridad del CMS en forma de consultas de inserción.

La carga de la tabla histórica DIM_REQUEST con datos básicos se realiza en tres etapas: carga de nuevas claves y atributos, actualización de existentes y registro de registros eliminados.

Carga de nuevos 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

Actualización 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 eliminados 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 fuente de datos debe ser acompañada de una descripción formal, por ejemplo, en un archivo readme.txt:

Receptor de datos formal/técnico: nombre, dirección de correo electrónico
Proveedor de datos formal/técnico: nombre, dirección de correo electrónico
Fuente de datos: ruta al archivo, nombres de servicios
Información sobre el acceso a los datos: usuarios y contraseñas

Un esquema de movimiento de datos ayudará en el proceso de mantenimiento y actualización, por ejemplo, en forma de texto:

Movimiento de archivo. Fuente: ftp.domain.net: /logs/access.log Objetivo: /var/www/access.log
Lectura en la etapa. Objetivo: STG_ACCESS_LOG
Carga y transformación. Objetivo: FCT_ACCESS_REQUEST_REF_HH
Carga y transformación. Objetivo: FCT_ACCESS_USER_AGENT_DD
Informe. Objetivo: /var/www/report.html

Salida

Así, el artículo describe mecanismos como la integración de bases de datos y la introducción de nuevos niveles de agregación. Estos son necesarios al construir almacenes de datos con el fin de obtener conocimientos adicionales y mejorar la calidad de la información.

Fuente: habr.com

Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS 🔥 Compra un hosting fiable para sitios web con protección contra DDoS, servidores VPS VDS | ProHoster