sql >> Databáze >  >> RDS >> PostgreSQL

Pochopení a čtení katalogu systému PostgreSQL

Správa databází není malý úkol a může být snadno frustrující, aniž byste věděli, co se děje pod pokličkou. Ať už se snažíte zjistit, zda jsou užitečné nové indexy, počítají transakce s databází v určitou dobu nebo kdo je k databázi v kteroukoli chvíli připojen, data, která správcům umožní skutečně vědět, jak si jejich databáze vedou, jsou rozhodující. Naštěstí s PostgreSQL jsou tato data k dispozici v systémovém katalogu PostgreSQL.

Systémový katalog PostgreSQL je schéma s tabulkami a pohledy, které obsahují metadata o všech ostatních objektech v databázi a další. Díky němu můžeme zjistit, kdy dochází k různým operacím, jak se přistupuje k tabulkám nebo indexům a dokonce i to, zda databázový systém čte informace z paměti nebo potřebuje načíst data z disku.

Zde projdeme přehledem katalogu systému a zdůrazníme, jak jej číst a jak z něj čerpat užitečné informace. Některá metadata jsou přímočará a další části vyžadují trochu trávení, aby vygenerovaly skutečně užitečné informace. Ať tak či onak, PostgreSQL nám poskytuje skvělou platformu pro vytváření jakýchkoli informací, které o databázi samotné potřebujeme.

Katalog PostgreSQL

PostgreSQL ukládá informace o metadatech o databázi a clusteru ve schématu ‚pg_catalog‘. Tyto informace částečně využívá samotný PostgreSQL ke sledování věcí sám, ale jsou také prezentovány tak, aby externí lidé/procesy pochopili i vnitřek databází.

Katalog PostgreSQL má docela pevné pravidlo:Podívejte se, nedotýkejte se. Zatímco PostgreSQL ukládá všechny tyto informace do tabulek jako jakákoli jiná aplikace, data v tabulkách jsou plně spravována samotným PostgreSQL a neměla by být upravována, pokud to není absolutní nouze, a i tak je pravděpodobné, že bude později nutné znovu sestavit.

Projdeme si pár užitečných katalogových tabulek, jak číst data a chytré věci, které můžeme dělat s daty samotnými. V katalogu je poměrně dost tabulek, kterými se nebudeme zabývat, ale všechny informace pro tyto různé tabulky lze nalézt v oficiální dokumentaci PostgreSQL.

Metadata celého systému

Značná část tabulek, na které můžeme v katalogu dotazovat, jsou „systémové“ tabulky, kde nezáleží na tom, ke které databázi jsme připojeni, data představují celý cluster, žádnou jednotlivou databázi.

Informace o databázi

Obecné informace o databázi jsou uloženy v pg_database a statistiky jsou uloženy v pg_stat_database.

pg_database:

postgres=# SELECT oid,* FROM pg_database WHERE datname = 'severalnines';
-[ RECORD 1 ]-+-------------
oid           | 16396
datname       | severalnines
datdba        | 10
encoding      | 6
datcollate    | en_US.UTF-8
datctype      | en_US.UTF-8
datistemplate | f
datallowconn  | t
datconnlimit  | -1
datlastsysoid | 13804
datfrozenxid  | 548
datminmxid    | 1
dattablespace | 1663
datacl        |

Tabulka pg_database obsahuje řádek pro každou databázi v clusteru, včetně tří, které vycházejí z krabice (postgres, template0 a template1). Tento řádek obsahuje informace pro kódování, limit připojení a další základní metadata.

Sloupce zájmu:

oid – Identifikátor objektu, který se neobjeví ve výstupu dotazu, pokud na něj není přímo odkazováno. Toto číslo bude odpovídat adresáři v datovém adresáři clusterů /base/.
datname - Název databáze.
datdba - Vlastník databáze, oid reference pg_authid .oid.
encoding - Kódování znaků pro tuto databázi, pg_encoding_to_char() se převede na čitelný název.
datconnlimit - Maximální počet souběžných připojení povolených v databázi.
dattablespace - The výchozí tabulkový prostor pro tuto databázi, odkazy na pg_tablespace.oid.

pg_stat_database:

postgres=# SELECT * FROM pg_stat_database WHERE datname = 'severalnines';
-[ RECORD 1 ]--+------------------------------
datid          | 13805
datname        | postgres
numbackends    | 2
xact_commit    | 477460
xact_rollback  | 13
blks_read      | 417
blks_hit       | 16488702
tup_returned   | 252376522
tup_fetched    | 2637123
tup_inserted   | 114
tup_updated    | 3
tup_deleted    | 1
conflicts      | 0
temp_files     | 0
temp_bytes     | 0
deadlocks      | 0
blk_read_time  | 0
blk_write_time | 0
stats_reset    | 2018-02-04 19:52:39.129052+00

Tato statistická tabulka je místem, kde získáváme zajímavá a užitečná data. Každý řádek v této tabulce obsahuje aktuální data pro každou databázi a lze je pravidelně exportovat a sledovat v průběhu času, pokud chcete sledovat změny.

Transakce

Informace o transakcích lze nalézt ve sloupcích xact_commit a xact_rollback, které obsahují počet transakcí, které databáze potvrdila a odvolala. To pomůže ukázat, jak aktivní databáze je, a také odhalit možná selhání programů, které mohou chybovat nebo se vracet zpět alarmujícím tempem.

Přístup k disku a paměti

Informace o tom, zda se data načítají z disku nebo paměti, jsou uloženy ve sloupcích blks_read a blks_hit. Blks_read ukazuje počet bloků, které tato databáze přečetla z disku, zatímco blks_hit ukazuje počet bloků, které byly nalezeny ve vyrovnávací paměti PostgreSQL (reprezentované parametrem shared_buffers). Protože RAM je mnohem rychlejší než disk, v ideálním případě bychom viděli blks_hit trvale vyšší než blks_read, a pokud ne, můžeme přehodnotit naši dostupnou paměť.

Nice

Následujících několik sloupců se zabývá n-ticemi. Tup_returned je počet řádků vrácených v databázi, což je počet řádků přečtených sekvenčním skenováním z tabulky nebo počet položek indexu vrácených z indexu. Tup_fetched je počet řádků načtených v databázi, což znamená, že byly výsledkem skenování bitmapy, což je počet řádků tabulky načtených skenováním bitmapy, pokud z tabulky, nebo řádků tabulky načtených jednoduchým skenováním indexu, pokud používáte index.

Máme také tup_inserted, tup_updated a tup_deleted, což představuje počet vložených, aktualizovaných a smazaných n-tic v této databázi. To pomůže pochopit, jak data vstupují, mění a opouštějí databázi. Vzhledem k tomu, že aktualizované a odstraněné n-tice mají za následek mrtvé řádky, vysoké hodnoty v těchto sloupcích by naznačovaly, že operace autovakuování by měly být vyladěny tak, aby vyhovovaly potřebám databázové aktivity.

Konflikty

Pokud je dotyčná databáze rezervní server, konflikty sloupců se hodí jako způsob, jak sledovat, kolik dotazů bylo zrušeno kvůli konfliktům s pohotovostním režimem, který je v „režimu obnovy“. Pokud nejde o pohotovostní cluster, lze tento sloupec ignorovat.

Dočasné soubory / data

Někdy bude potřeba dotazy zapisovat do dočasných souborů. K tomu může dojít, když bylo vyčerpáno množství work_mem přidělené připojení a je třeba pokračovat v operaci řazení na disku, nikoli v paměti. Sloupec temp_files sleduje počet těchto souborů, které byly vytvořeny, a temp_bytes sleduje celkovou velikost všech použitých dočasných souborů. Tato data mohou pomoci při ladění work_mem nebo dokonce při hledání dotazů, které by mohly vyžadovat přepsání, pokud je velikost dočasného souboru příliš velká.

Zablokování

Sloupec uváznutí sleduje, kolikrát k zablokování dojde. Vzhledem k tomu, že uváznutí může způsobit chyby u dotazů, které by jinak nebyly chybné, je dobré to sledovat a ujistit se, že si aplikace navzájem nešlapou.

Doby čtení a zápisu

Sloupce blk_read_time a blk_write_time sledují celkový počet milisekund, které backendy v databázi stráví čtením a zápisem dat, což může být užitečné při pokusu o porovnání/zvýšení rychlosti čtení/zápisu na disk

Resetování statistik

Tento sloupec, stats_reset, jednoduše zobrazuje časové razítko (s časovým pásmem) posledního resetování statistik uvedených v tomto řádku. Nulová hodnota znamená, že nebyly od počátku resetovány, nebo dokonce možná došlo k havárii databáze, což mohlo tyto statistiky vymazat.

Kontrolní body a autor pozadí

pg_stat_bgwriter

postgres=# SELECT * FROM pg_stat_bgwriter;
-[ RECORD 1 ]---------+------------------------------
checkpoints_timed     | 47829
checkpoints_req       | 2
checkpoint_write_time | 7323
checkpoint_sync_time  | 38
buffers_checkpoint    | 76
buffers_clean         | 0
maxwritten_clean      | 0
buffers_backend       | 5
buffers_backend_fsync | 0
buffers_alloc         | 440
stats_reset           | 2018-02-04 19:52:34.712832+00

Cluster PostgtreSQL spravuje zápis dat na disk několika různými způsoby. Pokud jde o „špinavé vyrovnávací paměti“ (data v paměti, která byla změněna od načtení z disku, ale tato změna ještě nebyla zapsána na disk), je to provedeno buď kontrolním bodem, nebo zapisovačem na pozadí. Vzhledem k tomu, že špinavá vyrovnávací paměť musí být zapsána na disk, než může být uvolněna nebo přerozdělena, je velmi důležité ujistit se, že tyto procesy jsou jemně vyladěny, a tato tabulka pomáhá osvětlit, jak to všechno funguje.

Kontrolní body

Ke kontrolnímu bodu dojde buď podle plánu (reprezentovaného parametrem checkpoint_timeout), nebo když bylo od posledního kontrolního bodu použito maximální množství souborů WAL a je potřeba vynutit kontrolní bod. V každém případě kontrolní bod zapisuje špinavé vyrovnávací paměti na disk a sledují jej čtyři sloupce.

Sloupce checkpoints_timed a checkpoints_req ukazují počet plánovaných kontrolních bodů (načasovaných) a počet požadovaných kontrolních bodů (označovaných také jako vynucené). Vysoká stoupající hodnota checkpoint_req by mohla naznačovat nedostatečnou hodnotu max_wal_size.

Sloupce checkpoint_write_time a checkpoint_sync_time zaznamenávají celkovou dobu (v milisekundách), kterou proces kontrolního bodu strávil zápisem a synchronizací na disk.

Konečně buffers_checkpoint je celkový počet bufferů zapsaných na disk kontrolními body.

Autor na pozadí

Zápis na pozadí je samostatný proces, který zapisuje špinavé vyrovnávací paměti na disk, což v ideálním případě snižuje množství práce, kterou kontrolní ukazatel potřebuje.

Sloupec buffers_clean představuje počet vyrovnávacích pamětí zapsaných na disk procesem na pozadí. Ve srovnání s buffers_checkpoint ukazuje, jak velkou část zátěže zvládnou jednotlivé procesy (s přidanou znalostí, že zapisovač na pozadí má možnost zapisovat buffery vícekrát, pokud se často mění, oproti méně často s časovaným kontrolním bodem).

Maxwritten_clean představuje, kolikrát zapisovač na pozadí dosáhl maximálního počtu stránek k vyprázdnění při každém spuštění (řízeno parametrem bgwriter_lru_maxpages).

Obecné vyrovnávací paměti

Zbývající sloupce nám ukazují obecné informace o vyrovnávací paměti, přičemž buffers_backend je počet vyrovnávacích pamětí, které musel backend sám zapsat, namísto zapisovače na pozadí nebo kontrolního ukazatele, buffers_backend_fsync je počet, kolikrát musel backend provést své vlastní volání fsync a buffers_alloc ukazuje počet vyrovnávacích pamětí, které byly obecně přiděleny.

Aktivita a zámky databáze

Existují dva pohledy, které ukazují aktuální aktivitu uživatele, pg_stat_activity a pg_locks. Při dotazu zobrazují informace o aktuálních připojeních k databázím a jaké zámky mají na jaké vztahy.

pg_stat_activity

postgres=# SELECT * FROM pg_stat_activity;
-[ RECORD 1 ]----+--------------------------------
datid            | 13805
datname          | severalnines
pid              | 29730
usesysid         | 10
usename          | postgres
application_name | psql
client_addr      |
client_hostname  |
client_port      | -1
backend_start    | 2018-07-21 02:29:48.976588+00
xact_start       | 2018-07-21 02:30:03.73683+00
query_start      | 2018-07-21 02:30:03.73683+00
state_change     | 2018-07-21 02:30:03.736835+00
wait_event_type  |
wait_event       |
state            | active
backend_xid      |
backend_xmin     | 559
query            | SELECT first_name FROM customers WHERE customers_sid = 472;
backend_type     | client backend
Obecné informace

Pohled pg_stat_activity zobrazuje řádek pro každé připojení k databázi a některé základní informace o něm. Sloupec dataname představuje databázi, ke které je připojení skutečně připojeno, pid je ID procesu připojení na samotném hostiteli databáze a usesysid a usename představují připojeného uživatele databáze.

Pro zdroj klienta je client_addr IP adresa hostitele, ze kterého připojení pochází, null znamená, že jde o místní unixové soketové připojení.

Časová razítka

Existují čtyři sloupce časových značek, které ukazují, kdy určité věci začaly:backend_start je okamžik, kdy bylo připojení skutečně navázáno, xact_start je okamžik zahájení aktuální transakce (null, pokud klient nemá žádnou otevřenou transakci), query_start je okamžik, kdy byl spuštěn aktuální nebo nejnovější dotaz, a state_change je čas, kdy se naposledy změnil stav připojení.

Stav připojení

Poslední bity pg_stat_activity pokrývají skutečný stav připojení. Pokud dotaz čeká na jiného, ​​aby uvolnil zámky, wait_event_type obsahuje nějaké informace o tom, o jaký typ události čekání se jedná, a ve sloupci wait_event se zobrazí název události čekání. Je to dlouhý seznam, ale další informace najdete v dokumentaci PostgreSQL.

Nakonec sloupec ‚stav‘ ukazuje, v jakém stavu se aktuální připojení nachází, například aktivní, nečinný, nečinný v transakci, a sloupec dotazu zobrazuje aktuální nebo naposledy spuštěný dotaz.

pg_lock

SELECT * FROM pg_locks;
-[ RECORD 1 ]------+----------------
locktype           | virtualxid
database           |
relation           |
page               |
tuple              |
virtualxid         | 3/475862
transactionid      |
classid            |
objid              |
objsubid           |
virtualtransaction | 3/475862
pid                | 29730
mode               | ExclusiveLock
granted            | t
fastpath           | t

Tabulka pg_locks pracuje ruku v ruce s pg_stat_activity při pohledu na aktivitu dotazů. Kdykoli dojde k uzamčení vztahu, tato informace se uloží do pg_locks. Pomocí pid z pg_stat_activity se můžeme dotazovat na pg_locks, abychom viděli, jaké vztahy může mít připojení zámky, jaké druhy zámků to jsou a zda byly nebo nebyly zámky uděleny.

Nejdůležitější sloupce jsou 'pid', který odpovídá pid z pg_stat_activity, 'relation', který odpovídá OID z pg_class, 'mode' zobrazující název drženého režimu uzamčení a 'granted', který uvádí, zda je nebo není zámek v otázka byla schválena.

Informace o replikaci

Protože PostgreSQL má vestavěné funkce replikace, existuje několik pohledů, které osvětlují výkon a stav samotné replikace.

Zobrazit pg_stat_replication: obsahuje řádek pro každý proces odesílatele WAL obsahující informace o jeho stavu, umístění souborů WAL, na kterých pracuje, a informace o připojení pohotovostního hostitele, který přijímá data WAL pro replikaci.

Zobrazit pg_stat_wal_receiver: Pokud je cluster v pohotovostním režimu, bude obsahovat jeden řádek zobrazující statistiky o procesu přijímače od hostitele.

Zobrazit pg_stat_subscription: Pokud odesíláte data WAL do pohotovostního uzlu, každý řádek zde bude představovat toto předplatné a obsahovat informace o stavu předplatného.

Zobrazit pg_replication_slots: Obsahuje seznam všech replikačních slotů, které existují v klastru, a jejich aktuální stav.

Metadata specifická pro databázi

Uvnitř každé databáze je kolekce katalogových tabulek, které obsahují informace specifické pro databázi, která je dotazována. Pokud hledáme konkrétní data z těchto tabulek, musíme se při zadávání dotazů ujistit, že jsme připojeni ke správné databázi.

Zde může přijít jádro analýzy dat, kde můžeme vidět, jak se přistupuje k našim uživatelským datům. Od tabulek, přes indexy až po sekvence, dotazy, které přicházejí do databáze a načítají nebo upravují data, jejich akce a dopad budou uloženy v těchto tabulkách, a my se na tyto informace můžeme podívat a učinit informovaná rozhodnutí o správě databáze. silnice.

Metadata tabulky

Metadata o našich uživatelských tabulkách jsou uložena v následujících dvou tabulkách a každá z nich má řádek pro každou uživatelskou tabulku vytvořenou v systému. Tabulka pg_stat_user_tables obsahuje statistiky přístupu uživatelů k tabulce, zatímco pg_statio_user_tables obsahuje I/O statistiky pro každou tabulku.

POZNÁMKA:Zde uvedená data nejsou vždy 100% dokonalá a jejich správnost závisí na častých analýzách tabulek. Autoanalyze to pokrývá, ale dobré vyladění procesu autoanalyzy tak, aby bylo možné udržovat dobré statistiky o každé tabulce. Pokud se statistiky zdají být vypnuté, ručním spuštěním ANALYZE na stole je obnovíte.

Tabulka pg_stat_user_tables:

severalnines=> SELECT * FROM pg_stat_user_tables WHERE schemaname = 'public' AND relname = 'history';
-[ RECORD 1 ]-------+---------
relid               | 2766788
schemaname          | public
relname             | history
seq_scan            | 13817
seq_tup_read        | 466841
idx_scan            | 12251
idx_tup_fetch       | 127652
n_tup_ins           | 11
n_tup_upd           | 13
n_tup_del           | 3
n_tup_hot_upd       | 13
n_live_tup          | 3
n_dead_tup          | 21
n_mod_since_analyze | 19
last_vacuum         |
last_autovacuum     |
last_analyze        |
last_autoanalyze    |
vacuum_count        | 0
autovacuum_count    | 0
analyze_count       | 0
autoanalyze_count   | 0

Pro naše statistiky uživatelských tabulek máme poměrně dost údajů.

Metody přístupu k tabulce

Když klienti přistupují k datům z tabulky, činí tak buď přímo, nebo prostřednictvím indexů. Sloupec „seq_scan“ počítá počet sekvenčních skenů, které tabulka obdržela, a „seq_tup_read“ počítá počet n-tic přečtených tímto procesem. Sloupec ‘idx_scan’ počítá, kolikrát byl index v tabulce použit k načtení dat.

Tabulková aktivita n-tice

Nyní máme několik sloupců, které počítají různé aktivity v tabulce.

„n_tup_ins“ sleduje počet vložených n-tic

„n_tup_upd“ sleduje počet aktualizovaných n-tic

„n_tup_del“ sleduje počet smazaných n-tic

Stav n-tice tabulky

Kvůli aktualizacím a mazáním mohou existovat mrtvé n-tice, které již nejsou aktivními daty, a vakuový proces je nakonec uvolní. Sloupce ‚n_tup_ins‘ a ‚n_tup_ins‘ sledují počet n-tic, které jsou živé a mrtvé. Když mrtvé n-tice dosáhnou určitého bodu, spustí se autovakuum v závislosti na nastavení autovakua.

Činnost vysávání stolu

Údržba tabulky se provádí pomocí VACUUM nebo AUTOVACUUM a statistiky se shromažďují pomocí ANALYZE nebo AUTOANALYZE. Následující čtyři sloupce obsahují data, kdy byla každá z těchto operací naposledy spuštěna:‚last_vacuum‘, ‚last_autovacuum‘, ‚last_analyze‘, ‚last_autoanalyze‘.

Máme také čtyři pohodlnější sloupce, které jednoduše počítají, kolikrát došlo k předchozím akcím. Pomocí nich můžeme zjistit, které tabulky vykazují největší aktivitu:‘vacuum_count’, ‘autovacuum_count’, ‘analyze_count’ a ‘autoanalyze_count’.

Tabulka pg_statio_user_tables:

severalnines=> SELECT * FROM pg_statio_user_tables WHERE schemaname = 'public' AND relname = history;
-[ RECORD 1 ]---+---------
relid           | 2766788
schemaname      | public
relname         | history
heap_blks_read  | 4
heap_blks_hit   | 63081
idx_blks_read   | 5
idx_blks_hit    | 44147
toast_blks_read | 0
toast_blks_hit  | 0
tidx_blks_read  | 0
tidx_blks_hit   | 0

Výstup I/O je užitečný k tomu, aby vám pomohl porozumět tomu, jak se přistupuje k datům pod krytem. Sloupec ‚heap_blks_read‘ představuje počet diskových bloků přečtených pro tuto tabulku a ‚heap_blks_hit‘ představuje bloky vyrovnávací paměti přečtené z paměti v této tabulce. To je užitečné, chcete-li vědět, zda dotazy přistupující k tabulce musí neustále jít na disk nebo načítat data z paměti.

Statistiky indexu v tabulce zobrazují stejné informace se sloupci „idx_blks_read“ a „idx_blks_hit“.

A konečně, pokud tabulka obsahuje nějaké tabulky TOAST, sloupce ‚toast_blks_hit‘ a ‚toast_blks_read‘ sledují tabulky toastů, zatímco ‚tdix_blks_read‘ a ‚tdix_blks_read‘ sledují indexy na těchto tabulkách toastů.

Metadata indexu

pg_stat_user_indexes

severalnines=> SELECT * FROM pg_stat_user_indexes WHERE indexrelname = 'history_pkey';
-[ RECORD 1 ]-+-------------
relid         | 2766797
indexrelid    | 2766934
schemaname    | public
relname       | history
indexrelname  | history_pkey
idx_scan      | 43910
idx_tup_read  | 98147
idx_tup_fetch | 98147

Podobně jako protějšky tabulek obsahuje tato tabulka konkrétně informace o indexech. Jeden řádek na index, tato tabulka ukazuje, kolikrát byl index naskenován pomocí sloupce ‚idx_scan‘, kolik n-tic bylo přečteno pomocí ‚idx_tup_read‘ a kolik aktivních řádků bylo skutečně načteno pomocí ‚idx_tup_fetch‘.

pg_statio_user_indexes

severalnines=> SELECT * FROM pg_statio_user_indexes WHERE indexrelname = 'history_pkey';
-[ RECORD 1 ]-+-------------
relid         | 2766797
indexrelid    | 2766934
schemaname    | public
relname       | history
indexrelname  | history_pkey
idx_blks_read | 2
idx_blks_hit  | 49380

Pro pg_statio_user_indexes jsou pro data k dispozici dva sloupce „idx_blks_read“ a „idx_blks_hit“, které představují počet bloků přečtených z disku az paměti.

Stáhněte si Whitepaper Today Správa a automatizace PostgreSQL s ClusterControlZjistěte, co potřebujete vědět k nasazení, monitorování, správě a škálování PostgreSQLStáhněte si Whitepaper

Co můžeme s těmito daty dělat?

Buďte kreativní! Pokud se dotazy na konkrétní tabulku zdají být extrémně pomalé, sledujte její aktivitu v průběhu času, podívejte se, kolik sekvenčních skenů získává vs. index skenování, podívejte se, zda jde pro data na disk nebo do paměti.

Pokud se velký stůl často autovakuuje, sledujte v průběhu času živé až mrtvé n-tice, možná konkrétně potřebuje autovakuum vyladit, aby mohl být dokončen rychleji, nebo je dokonce stůl kandidátem na rozdělení.

Protože můžeme vidět, kdy data pocházejí z disku nebo paměti, můžeme vytvořit poměr paměti k disku v průběhu času a určit, zda se tento poměr během dne sníží.

Množství tabulek, které jsme pokryli, přesáhlo velké hity, což jsou hlavní data, která je užitečné vědět o vnitřním fungování databází. V systémovém katalogu je však mnohem více tabulek, které obsahují situačně užitečná data. Čtení dalších tabulek jako dříve vám pomůže získat přehled o stavu databáze obecně.

Pro více informací o jakýchkoli tabulkách nebo pohledech v katalogu PostgreSQL navštivte oficiální dokumentaci zde a také informace o sběrači statistik zde.


  1. Jak funguje GREATEST() v MariaDB

  2. Jak zobrazit protokoly transakcí v SQL Server 2008

  3. Průvodce instalací MySQL na Windows krok za krokem

  4. MIN a MAX agregační funkce v SQL Server