
SQL â çfarĂ« mund tĂ« jetĂ« mĂ« e thjeshtĂ«? Secili prej nesh mund tĂ« shkruajĂ« njĂ« query tĂ« thjeshtĂ« â shkruajmĂ« select, rendisim kolonat e nevojshme, pastaj from, emrin e tabelĂ«s, pak kushte te ku dhe kaq â tĂ« dhĂ«nat e dobishme i kemi nĂ« xhep, thuajse pavarĂ«sisht se cila DBMS fshihet pas skenĂ«s nĂ« atĂ« moment (madje ndoshta ). Si rezultat, puna pothuajse me çdo burim tĂ« dhĂ«nash (relacional apo jo fort i tillĂ«) mund tĂ« shihet nga kĂ«ndvĂ«shtrimi i kodit tĂ« zakonshĂ«m (me gjithçka qĂ« sjell kjo â version control, code review, analizĂ« statike, autoteste e tĂ« tjera). Dhe kjo vlen jo vetĂ«m pĂ«r vetĂ« tĂ« dhĂ«nat, skemat dhe migrimet, por nĂ« pĂ«rgjithĂ«si pĂ«r tĂ« gjithĂ« ciklin e jetĂ«s sĂ« ruajtjes sĂ« tĂ« dhĂ«nave. NĂ« kĂ«tĂ« artikull do tĂ« flasim pĂ«r detyrat dhe problemet e pĂ«rditshme gjatĂ« punĂ«s me DB tĂ« ndryshme nĂ«n prizmin e "database as code".
Dhe do ta nisim pikërisht me . Përplasjet e para të tipit "SQL vs ORM" janë vënë re që në .
Mapimi objekt-relacional
Mbështetësit e ORM tradicionalisht vlerësojnë shpejtësinë dhe thjeshtësinë e zhvillimit, pavarësinë nga DBMS dhe pastërtinë e kodit. Për shumë prej nesh, kodi për punën me DB (dhe shpesh edhe vetë DB)
zakonisht duket afĂ«rsisht kĂ«shtuâŠ
@Entity
@Table(name = "stock", catalog = "maindb", uniqueConstraints = {
@UniqueConstraint(columnNames = "STOCK_NAME"),
@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {
@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "STOCK_ID", unique = true, nullable = false)
public Integer getStockId() {
return this.stockId;
}
...Modeli është i mbushur me anotime inteligjente, ndërsa diku prapa skenës ORM-ja trimërore gjeneron dhe ekzekuton tonelata kodi SQL. Meqë ra fjala, zhvilluesit përpiqen me të gjitha forcat të distancohen nga DB-ja e tyre përmes kilometrash abstraksionesh, gjë që flet për njëfarë .
NĂ« anĂ«n tjetĂ«r tĂ« barrikadĂ«s, ithtarĂ«t e SQL-sĂ« sĂ« pastĂ«r "handmade" vĂ«nĂ« nĂ« dukje mundĂ«sinĂ« pĂ«r tâi nxjerrĂ« maksimumin DBMS-sĂ« sĂ« tyre pa shtresa dhe abstraksione shtesĂ«. Si rezultat lindin projekte "data-centric", ku me bazĂ«n merren njerĂ«z tĂ« specializuar posaçërisht (tĂ« ashtuquajturit "bazistĂ«", "specialistĂ« tĂ« bazĂ«s" e kĂ«shtu me radhĂ«), ndĂ«rsa zhvilluesve u mbetet vetĂ«m tĂ« "thĂ«rrasin" view-t e gatshme dhe stored procedure, pa hyrĂ« nĂ« hollĂ«si.
Po sikur të marrim më të mirën nga të dy botët? Pikërisht siç është bërë në mjetin e shkëlqyer me emrin optimist . Do të sjell disa rreshta nga koncepti i përgjithshëm në përkthimin tim të lirë, ndërsa me të mund të njiheni më në hollësi .
Clojure është një gjuhë e shkëlqyer për krijimin e DSL-ve, por SQL tashmë është vetë një DSL i shkëlqyer dhe nuk na duhet edhe një tjetër. S-shprehjet janë të mrekullueshme, por këtu nuk sjellin asgjë të re. Në fund marrim thjesht kllapa për hir të kllapave. Nuk jeni dakord? Atëherë prisni momentin kur abstraksioni mbi DB do të fillojë të rrjedhë dhe ju do të nisni betejën me funksionin (raw-sql)
Po çfarĂ« tĂ« bĂ«jmĂ«? Le ta lĂ«mĂ« SQL-in tĂ« mbetet SQL i zakonshĂ«m â njĂ« skedar pĂ«r njĂ« kĂ«rkesĂ«:
-- name: users-by-country
select *
from users
where country_code = :country_code⊠dhe më pas lexoni këtë skedar, duke e kthyer në një funksion të zakonshëm Clojure:
(defqueries "some/where/users_by_country.sql"
{:connection db-spec})
;;; A function with the name `users-by-country` has been created.
;;; Let's use it:
(users-by-country {:country_code "GB"})
;=> ({:name "Kris" :country_code "GB" ...} ...)Duke iu përmbajtur parimit "SQL veçmas, Clojure veçmas", ju përfitoni:
- AsnjĂ« surprizĂ« sintaksore. Baza juaj e tĂ« dhĂ«nave (si çdo tjetĂ«r) nuk pĂ«rputhet 100% me standardin SQL â por pĂ«r Yesql kjo nuk ka rĂ«ndĂ«si. Nuk do tĂ« humbni kurrĂ« kohĂ« duke kĂ«rkuar funksione me sintaksĂ« ekuivalente me SQL. Nuk do tâju duhet kurrĂ« tĂ« ktheheni te funksioni (raw-sql "some (âfunkyâ :: SYNTAX)")).
- Mbështetje më e mirë nga editori. Editori juaj tashmë ka mbështetje të shkëlqyer për SQL. Duke e ruajtur SQL-in si SQL, thjesht mund ta përdorni atë.
- Përputhshmëri më e mirë me ekipin. DBA-të tuaj mund të lexojnë dhe të shkruajnë SQL-in që përdorni në projektin tuaj Clojure.
- Konfigurim më i thjeshtë i performancës. Duhet të ndërtoni planin për një kërkesë problematike? Nuk është problem, kur kërkesa juaj është SQL i zakonshëm.
- RipĂ«rdorim i kĂ«rkesave. Merrini kĂ«ta skedarĂ« SQL dhe pĂ«rdorini nĂ« projekte tĂ« tjera, sepse Ă«shtĂ« thjesht SQL i vjetĂ«r i mirĂ« â mjafton ta ndani.
Mendoj se ideja është shumë e bukur dhe njëkohësisht shumë e thjeshtë, prandaj projekti ka fituar shumë në gjuhë nga më të ndryshmet. Ndërsa më tej do të përpiqemi të zbatojmë një filozofi të ngjashme të ndarjes së kodit SQL nga gjithçka tjetër, shumë përtej ORM.
IDE & DB-menaxherë
Le të nisim me një detyrë të thjeshtë të përditshme. Shpesh na duhet të kërkojmë objekte të ndryshme në DB, për shembull të gjejmë një tabelë në një skemë dhe të shqyrtojmë strukturën e saj (cilat kolona, çelësa, indekse, constraints dhe elemente të tjera përdoren). Dhe nga çdo IDE grafike ose një DB-manager sado modest, para së gjithash presim pikërisht këto mundësi. Që gjithçka të jetë e shpejtë dhe të mos duhet të presim gjysmë ore derisa të hapet dritarja me informacionin e nevojshëm (sidomos me një lidhje të ngadaltë me DB në distancë), dhe njëkohësisht që informacioni i marrë të jetë i freskët dhe aktual, jo një cache e vjetruar. Madje, sa më komplekse e më e madhe të jetë DB dhe sa më i lartë të jetë numri i tyre, aq më e vështirë bëhet kjo.
Por zakonisht e lë mënjanë miun dhe thjesht shkruaj kod. Supozojmë se duhet të mësojmë se cilat tabela (dhe me çfarë vetish) ndodhen në skemën "HR". Në shumicën e DBMS-ve, rezultati i nevojshëm mund të merret me një query të thjeshtë nga information_schema:
select table_name
, ...
from information_schema.tables
where schema = 'HR'Nga një bazë te tjetra, përmbajtja e këtyre tabelave referuese ndryshon në varësi të mundësive të secilit DBMS. Për shembull, në MySQL nga i njëjti katalog mund të merren parametrat e tabelës specifikë për këtë DBMS:
select table_name
, storage_engine -- "Engine" i përdorur ("MyISAM", "InnoDB" etc)
, row_format -- Formati i rreshtit ("Fixed", "Dynamic" etc)
, ...
from information_schema.tables
where schema = 'HR'Oracle nuk e mbështet information_schema, por ka , ndaj nuk lindin probleme të mëdha:
select table_name
, pct_free -- Minimumi i hapësirës së lirë në bllokun e të dhënave (%)
, pct_used -- Minimumi i hapësirës së përdorur në bllokun e të dhënave (%)
, last_analyzed -- Data e mbledhjes së fundit të statistikave
, ...
from all_tables
where owner = 'HR'As ClickHouse nuk bën përjashtim:
select name
, engine -- "Engine" i përdorur ("MergeTree", "Dictionary" etc)
, ...
from system.tables
where database = 'HR'Diçka e ngjashme mund të bëhet edhe në Cassandra (ku ka columnfamilies në vend të tables dhe keyspace në vend të skemave):
select columnfamily_name
, compaction_strategy_class -- Strategjia e grumbullimit të mbeturinave
, gc_grace_seconds -- Koha e jetës së mbeturinave
, ...
from system.schema_columnfamilies
where keyspace_name = 'HR'Edhe për shumicën e DB-ve të tjera mund të krijohen query të ngjashme (madje edhe Mongo ka , i cili përmban informacion për të gjitha koleksionet në sistem).
Natyrisht, nĂ« kĂ«tĂ« mĂ«nyrĂ« mund tĂ« merrni informacion jo vetĂ«m pĂ«r tabelat, por nĂ« pĂ«rgjithĂ«si pĂ«r çdo objekt. HerĂ« pas here, njerĂ«z tĂ« mirĂ« ndajnĂ« njĂ« kod tĂ« tillĂ« pĂ«r DB tĂ« ndryshme, si pĂ«r shembull nĂ« serinĂ« e artikujve nĂ« Habr "Funksione pĂ«r dokumentimin e bazave tĂ« tĂ« dhĂ«nave PostgreSQL" (, , ). Sigurisht, ta mbash nĂ« mendje gjithĂ« kĂ«tĂ« mal me kĂ«rkesa dhe tâi shtypĂ«sh vazhdimisht Ă«shtĂ« njĂ« "kĂ«naqĂ«si" jo fort e madhe, ndaj nĂ« IDE/redaktorin tim tĂ« preferuar kam njĂ« grup snippets tĂ« pĂ«rgatitura paraprakisht pĂ«r kĂ«rkesat qĂ« pĂ«rdor mĂ« shpesh, dhe mbetet vetĂ«m tĂ« shkruaj emrat e objekteve nĂ« shabllon.
Në fund, kjo mënyrë navigimi dhe kërkimi të objekteve është shumë më fleksibël, kursen shumë kohë dhe ju lejon të merrni pikërisht atë informacion dhe në atë formë që ju nevojitet në atë moment (siç përshkruhet, për shembull, në postimin ).
Veprime me objektet
Pasi kemi gjetur dhe shqyrtuar objektet e nevojshme, është koha të bëjmë me to diçka të dobishme. Natyrisht, pa i hequr duart nga tastiera.
Nuk është sekret që fshirja e thjeshtë e një tabele do të duket pothuajse njësoj në pothuajse të gjitha DB:
drop table hr.personsNdĂ«rsa me krijimin e njĂ« tabele bĂ«het mĂ« interesante. Praktikisht çdo DBMS (pĂ«rfshirĂ« edhe shumĂ« NoSQL) nĂ« njĂ« formĂ« apo nĂ« njĂ« tjetĂ«r mbĂ«shtet "create table", dhe pjesa kryesore e saj ndryshon shumĂ« pak (emri, lista e kolonave, llojet e tĂ« dhĂ«nave), por detajet e tjera mund tĂ« ndryshojnĂ« rrĂ«njĂ«sisht dhe varen nga arkitektura e brendshme dhe mundĂ«sitĂ« e DBMS-sĂ« konkrete. Shembulli im i preferuar Ă«shtĂ« se nĂ« dokumentacionin e Oracle vetĂ«m BNF-tĂ« "e zhveshura" pĂ«r sintaksĂ«n e "create table" . DBMS tĂ« tjera kanĂ« mundĂ«si mĂ« modeste, por secila prej tyre gjithashtu ofron shumĂ« veçori interesante dhe unike pĂ«r krijimin e tabelave (, , , ). VĂ«shtirĂ« se ndonjĂ« "wizard" grafik nga ndonjĂ« IDE tjetĂ«r (sidomos universale) do tĂ« mund tâi mbulojĂ« plotĂ«sisht tĂ« gjitha kĂ«to mundĂ«si, dhe edhe nĂ«se do tĂ« mundej, nuk do tĂ« ishte pamje pĂ«r ata qĂ« e kanĂ« zemrĂ«n e dobĂ«t. Nga ana tjetĂ«r, njĂ« komandĂ« create table e shkruar saktĂ« dhe nĂ« kohĂ«n e duhur do tâju lejojĂ« tâi shfrytĂ«zoni pa vĂ«shtirĂ«si tĂ« gjitha kĂ«to, duke e bĂ«rĂ« ruajtjen dhe qasjen nĂ« tĂ« dhĂ«nat tuaja tĂ« besueshme, optimale dhe sa mĂ« komode.
Gjithashtu, nĂ« shumĂ« DBMS ka lloje specifike objektesh qĂ« mungojnĂ« nĂ« DBMS tĂ« tjera. Madje mund tĂ« kryejmĂ« operacione jo vetĂ«m mbi objektet e bazĂ«s sĂ« tĂ« dhĂ«nave, por edhe mbi vetĂ« DBMS-nĂ«, pĂ«r shembull tĂ« âvrasimâ njĂ« proces, tĂ« lirojmĂ« njĂ« zonĂ« tĂ« caktuar tĂ« memories, tĂ« aktivizojmĂ« gjurmimin, tĂ« kalojmĂ« nĂ« modalitetin âread onlyâ dhe shumĂ« tĂ« tjera.
Dhe tani të vizatojmë pak
NjĂ« nga detyrat mĂ« tĂ« zakonshme Ă«shtĂ« ndĂ«rtimi i njĂ« diagrami me objektet e bazĂ«s sĂ« tĂ« dhĂ«nave, qĂ« nĂ« njĂ« pamje tĂ« qartĂ« tĂ« shihen objektet dhe lidhjet mes tyre. KĂ«tĂ« e mbĂ«shtet pothuajse çdo IDE grafike, utilitete tĂ« veçanta tĂ« âcommand lineâ, mjete grafike tĂ« specializuara dhe modelues. Ato do tâju vizatojnĂ« diçka âsiç dinĂ«â, ndĂ«rsa ndikimi mbi kĂ«tĂ« proces zakonisht kufizohet nĂ« disa parametra nĂ« skedarin e konfigurimit ose disa opsione nĂ« ndĂ«rfaqe.
Por kjo çështje mund tĂ« zgjidhet shumĂ« mĂ« thjesht, mĂ« fleksibĂ«l dhe mĂ« elegant, dhe sigurisht me ndihmĂ«n e kodit. PĂ«r ndĂ«rtimin e diagrameve tĂ« çdo kompleksiteti kemi disa gjuhĂ« tĂ« specializuara markimi (DOT, GraphML etc), si dhe njĂ« gamĂ« tĂ« tĂ«rĂ« aplikacionesh (GraphViz, PlantUML, Mermaid), qĂ« dinĂ« tĂ« lexojnĂ« kĂ«to udhĂ«zime dhe tâi vizualizojnĂ« nĂ« formate tĂ« ndryshme. NdĂ«rsa informacionin pĂ«r objektet dhe lidhjet mes tyre tashmĂ« e dimĂ« si ta marrim.
Le të japim një shembull të shkurtër se si mund të dukej kjo, duke përdorur PlantUML dhe (majtas është SQL-kërkesa që gjeneron udhëzimin e nevojshëm për PlantUML, ndërsa djathtas rezultati):

select '@startuml'||chr(10)||'hide methods'||chr(10)||'hide stereotypes' union all
select distinct ccu.table_name || ' --|> ' ||
tc.table_name as val
from table_constraints as tc
join key_column_usage as kcu
on tc.constraint_name = kcu.constraint_name
join constraint_column_usage as ccu
on ccu.constraint_name = tc.constraint_name
where tc.constraint_type = 'FOREIGN KEY'
and tc.table_name ~ '.*' union all
select '@enduml'Dhe nëse bëjmë pak më shumë përpjekje, atëherë mbi bazën e mund të marrim diçka shumë të ngjashme me një diagramë të vërtetë ER:
Një SQL-kërkesë paksa më e ndërlikuar
-- Koka
select '@startuml
!define Table(name,desc) class name as "desc" << (T,#FFAAAA) >>
!define primary_key(x) <b>x</b>
!define unique(x) <color:green>x</color>
!define not_null(x) <u>x</u>
hide methods
hide stereotypes'
union all
-- Tabelat
select format('Table(%s, "%s n informacion rreth %s") {'||chr(10), table_name, table_name, table_name) ||
(select string_agg(column_name || ' ' || upper(udt_name), chr(10))
from information_schema.columns
where table_schema = 'public'
and table_name = t.table_name) || chr(10) || '}'
from information_schema.tables t
where table_schema = 'public'
union all
-- Marrëdhëniet midis tabelave
select distinct ccu.table_name || ' "1" --> "0..N" ' || tc.table_name || format(' : "Një %s mund të ketë shumë %s"', ccu.table_name, tc.table_name)
from information_schema.table_constraints as tc
join information_schema.key_column_usage as kcu on tc.constraint_name = kcu.constraint_name
join information_schema.constraint_column_usage as ccu on ccu.constraint_name = tc.constraint_name
where tc.constraint_type = 'FOREIGN KEY'
and ccu.constraint_schema = 'public'
and tc.table_name ~ '.*'
union all
-- Fundi
select '@enduml' 
Nëse e shikoni me vëmendje, shumë mjete vizualizimi nën kapak përdorin pikërisht kërkesa të ngjashme. Vërtet, këto kërkesa zakonisht janë thellë , pa folur fare për ndonjë modifikim të tyre.
Metrikat dhe monitorimi
Le tĂ« kalojmĂ« te njĂ« temĂ« tradicionalisht e vĂ«shtirĂ« â monitorimi i performancĂ«s sĂ« DB. MĂ« kujtohet njĂ« histori e vogĂ«l e vĂ«rtetĂ«, qĂ« ma tregoi ânjĂ« miku imâ. NĂ« njĂ« nga projektet ishte njĂ« DBA shumĂ« i fuqishĂ«m dhe pak zhvillues e njihnin personalisht, madje rrallĂ« kush e kishte parĂ« ndonjĂ«herĂ« me sy (edhe pse, sipas thashethemeve, punonte diku nĂ« godinĂ«n ngjitur). NĂ« orĂ«n âXâ, kur sistemi production i njĂ« retaileri tĂ« madh niste sĂ«rish âtĂ« mos ndihej mirĂ«â, ai dĂ«rgonte nĂ« heshtje screenshot-e tĂ« grafikĂ«ve nga Oracle Enterprise Manager, ku me kujdes theksonte me marker tĂ« kuq pikat kritike âpĂ«r qartĂ«siâ (qĂ«, pĂ«r ta thĂ«nĂ« butĂ«, nuk ndihmonte shumĂ«). Dhe pikĂ«risht sipas kĂ«saj âfotografieâ duhej bĂ«rĂ« diagnostikimi dhe zgjidhja. NdĂ«rkohĂ«, askush nuk kishte akses te Enterprise Manager-i i çmuar (nĂ« tĂ« dy kuptimet e fjalĂ«s), sepse sistemi Ă«shtĂ« kompleks dhe i shtrenjtĂ«, e âmos vallĂ« zhvilluesit klikojnĂ« ndonjĂ« gjĂ« dhe prishin gjithçkaâ. Prandaj zhvilluesit, nĂ« mĂ«nyrĂ« âempirikeâ, gjenin vendin dhe shkakun e ngadalĂ«simeve dhe publikoni njĂ« patch. NĂ«se letra kĂ«rcĂ«nuese nga DBA nuk vinte sĂ«rish nĂ« tĂ« ardhmen e afĂ«rt, tĂ« gjithĂ« merrnin frymĂ« tĂ« lehtĂ«suar dhe ktheheshin te detyrat e tyre aktuale (deri te Letra e radhĂ«s).
Por procesi i monitorimit mund tĂ« duket mĂ« i thjeshtĂ« dhe mĂ« miqĂ«sor, dhe mĂ« e rĂ«ndĂ«sishmja â i arritshĂ«m dhe transparent pĂ«r tĂ« gjithĂ«. TĂ« paktĂ«n pjesa e tij bazĂ«, si shtesĂ« ndaj sistemeve kryesore tĂ« monitorimit (tĂ« cilat, pa dyshim, janĂ« tĂ« dobishme dhe nĂ« shumĂ« raste tĂ« pazĂ«vendĂ«sueshme). Ădo DBMS Ă«shtĂ« e gatshme tĂ« ndajĂ« lirisht dhe plotĂ«sisht falas informacion pĂ«r gjendjen e saj aktuale dhe performancĂ«n. NĂ« tĂ« njĂ«jtĂ«n Oracle DB âtĂ« pĂ«rgjakshmeâ, pothuajse çdo informacion pĂ«r performancĂ«n mund tĂ« merret nga pamjet sistemore, duke filluar nga proceset dhe sesionet e deri te gjendja e buffer cache (pĂ«r shembull, , seksioni âMonitoringâ). Edhe nĂ« Postgresql ekziston njĂ« gamĂ« e tĂ«rĂ« pamjesh sistemore pĂ«r , nĂ« veçanti ato kaq tĂ« pazĂ«vendĂ«sueshme nĂ« pĂ«rditshmĂ«rinĂ« e çdo DBA-je, si , , . NĂ« MySQL pĂ«r kĂ«tĂ« qĂ«llim ekziston madje njĂ« skemĂ« e veçantĂ« . NdĂ«rsa nĂ« Mongo, i integruar grumbullon tĂ« dhĂ«nat e performancĂ«s nĂ« koleksionin sistemor .
Kështu, duke përdorur një mjet për mbledhjen e metrikave (Telegraf, Metricbeat, Collectd) që mund të ekzekutojë kërkesa SQL të personalizuara, një ruajtje për këto metrika (InfluxDB, Elasticsearch, Timescaledb) dhe një mjet vizualizimi (Grafana, Kibana), mund të krijoni një sistem monitorimi mjaft të lehtë dhe fleksibël, i cili do të integrohet ngushtë me metrika të tjera të përgjithshme të sistemit (të marra, për shembull, nga serveri i aplikacionit, nga OS etj.). Për shembull, si është bërë te pgwatch2, ku përdoret kombinimi InfluxDB + Grafana dhe një grup kërkesash ndaj pamjeve të sistemit, të cilave gjithashtu mund t'u .
Përveç kësaj
Dhe kjo është vetëm një listë e përafërt e asaj që mund të bëhet me bazën tonë të të dhënave përmes kodit të zakonshëm SQL. Jam i sigurt se mund të gjenden edhe shumë përdorime të tjera, shkruani në komente. Ndërsa për mënyrën se si (dhe më e rëndësishmja pse) e gjithë kjo mund të automatizohet dhe të përfshihet në pipeline-in tuaj CI/CD, do të flasim herën tjetër.
Burimi: habr.com
