Więcej statystyk strony w twoim małym magazynie

Analizując statystyki strony, uzyskujemy obraz tego, co się z nią dzieje. Wyniki porównujemy z inną wiedzą na temat produktu lub usługi, co pozwala nam poprawić nasze doświadczenia.

Gdy analiza wstępnych wyników zostanie zakończona, przemyślano informacje i wyciągnięto wnioski, zaczyna się kolejny etap. Pojawiają się pomysły: a co by było, gdyby spojrzeć na dane z innej perspektywy?

Na tym etapie istnieją ograniczenia narzędzi analitycznych. To jedna z przyczyn, dla których nie wystarczało mi narzędzie Google Analytics, a konkretnie, ze względu na ograniczone możliwości podglądania i manipulowania danymi.

Zawsze chciałem szybko załadować podstawowe dane (dane główne), dodać inny poziom agregacji lub w inny sposób zinterpretować dostępne wartości.

To łatwe do zrobienia w swoim małym magazynie na podstawie pliku access.log i wystarczy do tego język SQL.

Jakie pytania chciałem zadać?

Co i kiedy zmieniało się na stronie

Historia zmian danych głównych zawsze stanowi ciekawostkę.

Więcej statystyk strony w twoim małym magazynie

Zapytanie SQL raportu

SELECT
	1 AS 'SideStackedBar: Aktualizacje treści według miesięcy',
	strftime('%m/%Y', datetime(UPDATE_DT, 'unixepoch')) AS 'Dzień',
	COUNT(CASE WHEN PAGE_TITLE != 'n.a.' THEN DIM_REQUEST_ID END) AS 'Aktualizacje stron',
	COUNT(CASE WHEN PAGE_DESCR = 'IMAGES' THEN DIM_REQUEST_ID END) AS 'Wgrywania obrazów',
	COUNT(CASE WHEN PAGE_DESCR = 'VIDEO' THEN DIM_REQUEST_ID END) AS 'Wgrywania wideo',
	COUNT(CASE WHEN PAGE_DESCR = 'AUDIO' THEN DIM_REQUEST_ID END) AS 'Wgrywania 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

Na przykład w jakimś momencie przeprowadzono optymalizację pod kątem wyszukiwarek lub dodano nową treść na stronę, w związku z tym oczekiwano wzrostu ruchu.

Grupy użytkowników

Najprostszym przykładem grupy może być agent użytkownika lub nazwa systemu operacyjnego.

Pomiar agentów użytkowników zebrał około tysiąca zapisów i byłem ciekaw, jak wygląda dynamika rozkładu agentów w obrębie grupy.

Więcej statystyk strony w twoim małym magazynie

Zapytanie SQL raportu

SELECT
	1 AS 'SideStackedBar: Agenci użytkowników',
	AGENT_OS AS 'OS',
	SUM(CASE WHEN AGENT_BOT = 'n.a.' THEN 1 ELSE 0 END) AS 'Agent użytkowników',
	SUM(CASE WHEN AGENT_BOT != 'n.a.' THEN 1 ELSE 0 END) AS 'Agent botów'
FROM DIM_USER_AGENT
WHERE DIM_USER_AGENT_ID != -1
GROUP BY AGENT_OS
ORDER BY 3 DESC

Najwięcej różnych kombinacji agentów przychodzi na stronę ze świata Windows. Wśród nieokreślonych znalazły się takie jak WhatsApp, PocketImageCache, PlayStation, SmartTV itd.

Aktywność grup użytkowników według tygodni

Łącząc niektóre grupy, można zaobserwować rozkład ich aktywności.

Na przykład, użytkownicy klastra Linux konsumują więcej ruchu na stronie niż wszyscy inni.

Więcej statystyk strony w twoim małym magazynie

Zapytanie SQL raportu

SELECT
1 as 'StackedBar: Objętość ruchu według systemu operacyjnego użytkowników i tygodnia',
strftime('%W tydzień', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Tydzień',
SUM(CASE WHEN USG.AGENT_OS IN ('Android', 'Linux') THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Użytkownicy Android/Linux',
SUM(CASE WHEN USG.AGENT_OS IN ('Windows') THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Użytkownicy Windows',
SUM(CASE WHEN USG.AGENT_OS IN ('Macintosh', 'iOS') THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Użytkownicy Mac/iOS',
SUM(CASE WHEN USG.AGENT_OS IN ('n.a.', 'BlackBerry') THEN FCT.BYTES ELSE 0 END) / 1000 AS 'Inni'
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.' /* tylko użytkownicy */
  AND HST.STATUS_GROUP IN ('Successful') /* dobre strony */
  AND datetime(FCT.EVENT_DT, 'unixepoch') > date('now', '-3 month')
GROUP BY strftime('%W tydzień', datetime(FCT.EVENT_DT, 'unixepoch'))
ORDER BY FCT.EVENT_DT

Intensywne zużycie ruchu

Z tabeli widać najbardziej aktywne grupy użytkowników oraz dni ich aktywności.
Najbardziej aktywne dotyczą klastra Linux.

Więcej statystyk strony w twoim małym magazynie

Zapytanie SQL raportu

SELECT
1 AS 'Tabela: Użytkownik Agent z dużym użyciem',
strftime('%d.%m.%Y', datetime(FCT.EVENT_DT, 'unixepoch')) AS 'Dzień',
ROUND(1.0 * SUM(FCT.BYTES) / 1000000, 1) AS 'Ruch MB',
ROUND(1.0 * SUM(FCT.IP_CNT) / SUM(1), 1) AS 'IPs',
ROUND(1.0 * SUM(FCT.REQUEST_CNT) / SUM(1), 1) AS 'Żądania',
USA.DIM_USER_AGENT_ID AS 'ID',
MAX(USA.USER_AGENT_NK) AS 'Użytkownik Agent',
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

Używając atrybutów dnia i ID agenta, powstaje możliwość szybkiego znalezienia i śledzenia statystyk według dni dla poszczególnych grup użytkowników. W razie potrzeby można szybko znaleźć szczegółowe informacje w tabeli etapowej.

Jak uzyskać informacje?

Informacje z pliku access.log można uczynić jeszcze bardziej efektywnymi, integrując dodatkowe źródła danych, wprowadzając nowe poziomy agregacji i grupowania.

Podstawowe dane i encje

Do podstawowych danych należy informacja o encjach: strony internetowe, zdjęcia, filmy i treści audio, w przypadku sklepu — produkty.

Same encje pełnią rolę wymiarów, a proces zachowywania zmian atrybutów nazywa się historiowaniem. W bazie danych proces ten często realizowany jest w formie powoli zmieniających się wymiarów (SCD).

Źródłem podstawowych danych mogą być bardzo różnorodne systemy, dlatego prawie zawsze należy je integrować.

Powoli zmieniające się wymiar

Pomiar DIM_REQUEST będzie zawierał informacje o zapytaniach na stronie w formie historycznej.

Tabela SCD2

CREATE TABLE DIM_REQUEST ( /* scd table for user requests */
  DIM_REQUEST_ID      INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  DIM_REQUEST_ID_HIST INTEGER NOT NULL DEFAULT -1,
  REQUEST_NK          TEXT NOT NULL DEFAULT 'n.a.', /* request without ?parameters */
  PAGE_TITLE          TEXT NOT NULL DEFAULT 'n.a.',
  PAGE_DESCR          TEXT NOT NULL DEFAULT 'n.a.',
  PAGE_KEYWORDS       TEXT NOT NULL DEFAULT 'n.a.',
  DELETE_FLAG         INTEGER NOT NULL DEFAULT 0,
  UPDATE_DT           INTEGER NOT NULL DEFAULT 0,
  UNIQUE (REQUEST_NK, DIM_REQUEST_ID_HIST)
);
INSERT INTO DIM_REQUEST (DIM_REQUEST_ID) VALUES (-1);

Dodatkowo stworzymy jedno widok, które zawsze wyświetla wszystkie rekordy w ostatnim stanie. Jest to konieczne do załadowania samego pomiaru.

Więcej statystyk strony w twoim małym magazynie

Aktualne widok 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;

I widok, w którym dla każdego rekordu zebrane są informacje historyczne. Jest to niezbędne do budowy historycznie poprawnych powiązań z faktami.

Więcej statystyk strony w twoim małym magazynie

Historyczne widok 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;

Agregacja danych

Kompresja (agregacja) pozwala ocenić dane na wyższym poziomie i wykryć anomalie oraz trendy, które nie są widoczne w szczegółowych raportach.

Na przykład, w pomiarze z kodami statusu zapytań DIM_HTTP_STATUS dodamy grupę:

STATUS / GRUPA
0xx / n.a.
1xx / Informacyjne
2xx / Pomyślne
3xx / Przekierowanie
4xx / Błąd klienta
5xx / Błąd serwera

Pomiar agentów użytkowników DIM_USER_AGENT będzie zawierał atrybuty AGENT_OS i AGENT_BOT, które odpowiadają za grupy. Można je wypełniać w trakcie ETL:

Załadunek 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

Integracja danych

Zawiera organizację przekazywania danych z systemu operacyjnego do raportu. W tym celu należy stworzyć tabelę staging o strukturze podobnej do źródła.

W stagingu informacje o stronach internetowych trafiają z kopii zapasowej CMS w postaci zapytań wstawiających.

Załadunek historycznej tabeli DIM_REQUEST podstawowymi danymi odbywa się w trzech krokach: załadunek nowych kluczy i atrybutów, aktualizacja istniejących oraz rejestrowanie usuniętych rekordów.

Załadunek nowych rekordów 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

Aktualizacja atrybutów 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 )

Usunięte rekordy 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

Każde źródło danych należy opatrzyć formalnym opisem, na przykład w pliku readme.txt:

Odbiorca danych formalnie/technicznie: imię, adres e-mail
Dostawca danych formalnie/technicznie: imię, adres e-mail
Źródło danych: ścieżka do pliku, nazwy usług
Informacje o dostępie do danych: użytkownicy i hasła

Schemat ruchu danych pomoże w procesie utrzymania i aktualizacji, na przykład w formie tekstowej:

Przenoszenie pliku. Źródło: ftp.domain.net: /logs/access.log Cel: /var/www/access.log
Odczyt w strefie. Cel: STG_ACCESS_LOG
Ładowanie i transformacja. Cel: FCT_ACCESS_REQUEST_REF_HH
Ładowanie i transformacja. Cel: FCT_ACCESS_USER_AGENT_DD
Raport. Cel: /var/www/report.html

Wnioski

W związku z tym artykuł opisuje mechanizmy takie jak integracja danych podstawowych oraz wprowadzenie nowych poziomów agregacji. Są one potrzebne przy budowie hurtowni danych w celu uzyskania dodatkowych informacji i poprawy jakości danych.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster