PostgreSQL: Liczba rekordów w każdej tabeli

2022-03-16

Jeden z ostatnich kroków migracji danych, to sprawdzenie, czy migracja „niczego nie zgubiła”. Potencjalnie można się np. spodziewać, że tuż po zakończeniu migracji, jakiś zbłąkany użytkownik dopisał swoje rekordy do źródłowej bazy danych, a my tego rekordu nie przenieśliśmy. Jeden z „topornych” sposobów sprawdzenia, czy żaden rekord nie zostanie utracony, jest po prostu policzenie rekordów w źródłowej i docelowej bazie danych.

Najpierw wypadałoby ustalić, jakie mamy schematy w bazie. Robimy to głównie po to, żeby wykluczyć z raportu schematy systemowe, względnie inne, które mają być z jakiegoś powodu ominięte. psql pozwala na uruchomienie skróconego polecenia \dn, ale ta właśnie pomija schematy systemowe. Dlatego proponuję:

SELECT schema_name FROM information_schema.schemata;

Oto możliwy wynik:

 schema_name 
--------------------
 public
 information_schema
 pg_catalog
 pg_toast_temp_1
 pg_temp_1
 pg_toast

Znając schematy bazy danych, pora na sztuczkę. Poniższe zapytanie automatycznie zbierze ze wszystkich tabel z wybranych schematów liczbę rekordów. Ponieważ zapytanie jest uruchamiane na faktycznych danych (a nie np. ze statystyk), jego wykonanie może zająć chwilę czasu. Zapytanie jest sprytne, bo korzysta z możliwości wykonania XML w tym zapytaniu. Poniekąd więc dla każdego rekordu zwracanego z tabel systemowych uruchomi się jeszcze jedno zapytanie XML, zliczające liczbę rekordów:

select table_schema, 
 table_name, 
 (xpath('/row/cnt/text()', xml_count))[1]::text::int as row_count
from (
 select table_name, 
 table_schema, 
 query_to_xml(format('select count(*) as cnt from %I.%I', table_schema, table_name), false, true, '') as xml_count
 from information_schema.tables
 where table_schema NOT LIKE 'pg_%' and table_schema != 'information_schema'
) t

U mnie zwrcony wynik wygląda mniej więcej tak:

 table_schema | table_name         | row_count 
--------------+--------------------+-----------
 public       | pg_stat_statements | 0
 public       | towns4             | 1000000
 public       | pg_buffercache     | 262144
 public       | towns5             | 1000000
 public       | towns              | 1000000
 public       | towns2             | 1000000
 public       | towns3             | 1000000
 public       | towns6             | 1000000
 public       | towns7             | 1000000
 public       | towns8             | 1000000
 public       | towns9             | 1000000
 public       | towns0             | 1000000
 public       | large_test         | 20000000
 public       | a_table            | 1013
(14 rows)

Powyższe zapytanie można by uruchomić na obu bazach danych i po prostu porównać uzyskane wyniki – czy to excelu, czy pythonie, czy palcem na ekranie – obojętnie.

Istnieją też inne propozycje rozwiązania tego problemu, ale niekoniecznie zliczające rekordy co do jednego, np. zapytanie:

SELECT relname, 
       n_tup_ins - n_tup_del as rowcount 
FROM pg_stat_all_tables 
WHERE schemaname NOT LIKE 'pg_%' and schemaname != 'information_schema';

zwraca:

 relname    | rowcount 
------------+----------
 towns      | 1044898
 towns5     | 1000000
 towns6     | 1000000
 a_table    | 1016
 towns4     | 1000000
 large_test | 20000000
 towns7     | 1000000
 towns2     | 1000000
 towns0     | 1000000
 towns9     | 1000000
 towns8     | 1000000
 towns3     | 1000000
(12 rows)

ale już dla tabeli a_table, widać różnice… Jeśli więc, chcesz mieć dokładne wyniki, to musisz liczyć wszystko. Z pomocą powyższych zapytań będzie to i tak rozwiązanie automatyczne, tylko może z punktu widzenia PostgreSQL, jakby trochę na piechotę.

By Rafał Kraik in PostgreSQL

PostgreSQL: Wykrywanie tabel bez primary key i dodawanie primary key

2022-03-16

Niektóre mechanizmy PostgreSQL, jak np. replikacja logiczna wymagają, aby tabele posiadały primary key. Dlatego przyda się wiedzieć, czy baza danych spełnia wymogi dla replikacji logicznej. Oto polecenie, które wyświetli informację o tabelach bez primary key:

select tab.table_schema,
       tab.table_name
from information_schema.tables tab
left join information_schema.table_constraints tco 
     on tab.table_schema = tco.table_schema
        and tab.table_name = tco.table_name 
        and tco.constraint_type = 'PRIMARY KEY'
where tab.table_type = 'BASE TABLE'
      and tab.table_schema not in ('pg_catalog', 'information_schema')
      and tco.constraint_name is null
order by table_schema, table_name;

I co jeśli takie tabele się znajdą? Ogólnie masz kłopot, ale w najlepszym przypadku, znajdzie się w tabelach kolumna, która może być potraktowana jako primary key. Zaczynamy więc od sprawdzenia struktur tabel:

mytest=> \d towns
 Table "public.towns"
 Column     | Type                  | Collation | Nullable | Default 
------------+-----------------------+-----------+----------+-----------------------------------
 id         | integer               |           | not null | nextval('towns_id_seq'::regclass)
 code       | character varying(10) |           | not null | 
 article    | text                  |           |          | 
 name       | text                  |           | not null | 
 department | character varying(4)  |           | not null | 
Indexes:
 "towns_code_department_key" UNIQUE CONSTRAINT, btree (code, department)
 "towns_id_key" UNIQUE CONSTRAINT, btree (id)

Dodanie indeksu opartego o kolumnę id wyglądałoby tak:

alter table towns add primary key (id);

I gotowe!

 

By Rafał Kraik in PostgreSQL

Python: Grupowanie po dacie z sumą – redukcja liczby wierszy

2022-03-12

Problem:

Dane pochodzące z wielu plików, zostały zaimportowane do Pandas Data Frame w następującej postaci:

Chcielibyśmy uniknąć wielokrotnie powtarzanej daty, wartości numeryczne znajdujące się w col1, col2 i col3 powinny się nasumować, dając taki efekt:

Rozwiązanie:

Wydaje się, że funkcjonalnością stworzoną do takiego celu jest grupowanie. Wystarczy na rzecz obiektu data frame wywołać metodę groupby wskazując, że grupowanie ma się odbyć ze względu na datę, a dla kolumn numerycznych chcemy zobaczyć sumę. Oto propozycja rozwiązania:

 

import pandas as pd
data = ({
 'date' :['2000-01','2000-02','2000-01','2000-02','2000-01','2000-02'],
 'col1' :[22000,25000,0,0,0,0],
 'col2' :[0,0,23000,24000,0,0],
 'col3' :[0,0,0,0,26000,25000]
 })
df = pd.DataFrame(data, columns=['date','col1','col2','col3'])
print(df)

df2 = df.groupby('date').sum()
print(df2)

 

By Rafał Kraik in Python

Azure: ustalenie minimalnych uprawnień wymaganych do pewnej czynności

2022-03-05

Zadanie z jakim się zmierzam, to ustalenie minimalnych uprawnień jakie powinien posiadać użytkownik, aby wykonać pewną czynność. W tym przypadku chodziło o budowanie specyficznej infrastruktury z wykorzystaniem skryptu Terraform, ale metoda sprawdzi się też w innych scenariuszach.

Zacząłem od stworzenia service principal, bo w moim przypadku skrypt miał być uruchamiany na service principal. Gdyby miało to być zwykłe konto użytkownika, to oczywiście należało by stworzyć konto:

az ad sp create-for-rbac --name myserviceprincipal

W wyniku tego polecenia jest tworzony service principal, a  w zwróconym output można znaleźć coś w tym stylu:

{
  "appId": "6...XXX...",
  "displayName": "myserviceprincipal",
  "password": "y...XXX...",
  "tenant": "6...XXX..."
}

Mamy tu: appId, bo jest on wykorzystywany przez dalsze polecenia i można go utożsamiać z określonym service principal i password, bo to tajna część utworzonego właśnie service principal i wreszcie tenant określający, gdzie tego service principal można używać (w jakiej organizacji). Czytaj dalej »

By Rafał Kraik in Azure

Linux: Vim: praca z oknami

2022-02-23

CTRL+w  s   podział okna na 2 ułożone obok siebie – s jak split

CTRL+w  v  podział okna na 2 ułożone jedno pod drugim – v jak vertical

CTRL+w  n  otwarcie nowego okna, poniżej – n jak new

CTRL+w  q  wyjście z okna – q jak quit

CTRL+w i jeden z klawiszy hjkl lub strzałki – zmiana aktywnego okna na po lewej, prawej u góry lub na dole

CTRL+w w – zmiana aktywnego okna na kolejne (pętelka)

CTRL+w r – zmiana miejscami okien – r jak rotate

CTRL+w i jeden z klaiszy + – = – zmiana wielkości okna: powieksz, pomniejsz, ułóż równo

:ter – uruchomienie terminala (można dzięki temu symulować wygląd code)

:set cursorline cursorcolumn – bieżący wiersz i kolumna będą wyróżnione

:highlight CursorColumn ctermbg=Grey ctermfg=Black – zmiana koloru kolumny wyróżniającej

 

Jeśli w trakcie testowania klawiszy przypadkiem naciśniesz CTRL+s, to terminal się „zamrozi”. Odmrożenie można wykonać naciskając CTRL+q

 

Ustawienia, zwłaszcza te dotyczące kolorów, można na stałe zapisać w pliku .vimrc o tak:

 cat .vimrc
set cursorline cursorcolumn
highlight CursorColumn ctermbg=Grey ctermfg=Black

Pełny help dotyczący również innych skrótów zobaczysz wpisując polecenie:

:help windows.txt
By Rafał Kraik in Linuxy

Windows Server 2019: Jak zainstalować Edge?

2022-02-19

Domyślne linki pozwalające na instalację przeglądarki MS Edge na Windows Server 2019 nie działają… dzięki Microsoft!

Ale ten link zadziała:

https://www.microsoft.com/en-us/edge/business/download

Co istotne, może być wymagana zmiana ustawień bezpieczeństwa w IE, żeby instalator edge dało się pobrać.

  1. Należy przejść do IE -> Internet Options
  2. Na zakładce Security wybrać Custom Level
  3. Przewinąć prawie na sam dół. Jest tam sekcja Scripting
  4. Upewnić się,  że opcja Active Scripting jest ustawiona na „Enable”

Enjoy!

Azure: AzCli: Wybór subskrypcji z linii komend polenia az

2022-02-09

Gdy skrypt, który przygotowujesz musi pracować z róznymi tenantami i subskrypcjami przyda się wiedza na temat tego jak wybrać właściwą subskrypcję z linii komend. A robi się to tak:

Logowanie poleceniem az do Azure:

az login

Sprawdzenie „a gdzie to ja się właściwie zalogowałem”:

 az account show

{
  "environmentName": "AzureCloud",
  "homeTenantId": "7d*****-****-****-****-**********aa",
  "id": "cc******-****-****-****-**********78",
  "isDefault": true,
  "managedByTenants": [],
  "name": "Azure Pass - Sponsorship",
  "state": "Enabled",
  "tenantId": "7d*****-****-****-****-**********aa",
  "user": {
    "name": "m******@*****.pl",
    "type": "user"
  }
}

Identyfikator subskrypcji widać już właściwie powyżej w polu id.

Wyświetlenie dostępnych subskrypcji:

az account list --output table

Name                      CloudName    SubscriptionId                        State    IsDefault
------------------------  -----------  ------------------------------------  -------  -----------
Azure Pass - Sponsorship  AzureCloud   cce*****-****-****-****-**********78  Enabled  True
MSDN Platforms            AzureCloud   b0f*****-****-****-****-**********56  Enabled  False

Przełączenie się do innej subskrypcji:

az account set --subscription "MSDN Platforms"

To polecenie jest ciche, więc po wszystkim radziłbym ponownie wylistować konta i sprawdzić, czy aktywna jest właściwa subskrypcja

 

Więcej: https://docs.microsoft.com/pl-pl/cli/azure/manage-azure-subscriptions-azure-cli

By Rafał Kraik in Azure