
SQL, mis vĂ”iks olla lihtsam? IgaĂŒhel meist on vĂ”imalik kirjutada lihtne pĂ€ring â kirjutame select, loetleme vajalikud veerud, siis from, tabeli nime, paar tingimust where ja kĂ”ik â kasulikud andmed on meil taskus, peaaegu sĂ”ltumata sellest, milline andmebaas praegu töötamas on (vĂ”i ei ole see ). Selle tulemusena saab töö peaaegu igasuguste andmeallikatega (suhtelised ja mitte eriti) kĂ€sitleda tavalise koodina (kĂ”ik, mis sellega kaasneb â versiooni kontroll, koodi ĂŒlevaatus, staatiline analĂŒĂŒs, automaatne testimine ja nii edasi). Ja see ei puuduta ainult andmeid, skeeme ja migratsioone, vaid tegelikult kogu andmehoidla tegevust. Selles artiklis rÀÀgime igapĂ€evastest ĂŒlesannetest ja probleemidest erinevate andmebaasidega töötamisel "andmebaas kui kood" kontekstis.
Ja alustame otse . Esimesed lahingud "SQL vs ORM" olid mÀrgatud juba .
Objekti-relatsiooniline kaardistamine
ORM-i pooldajad hindavad traditsiooniliselt arenduse kiirust ja lihtsust, sÔltumatust andmebaasidest ning koodi puhtust. Paljude jaoks meie seas nÀeb andmebaasiga töötamise kood (ja sageli ka sama andmebaas)
tavaliselt vĂ€lja umbes niiâŠ
@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;
}
...Mudel on kaunistatud nutikate annotatsioonidega, ja kusagil lava taga genereerib ja kĂ€ivitab vapper ORM tonnide viisi mingit SQL-koodi. Ătlemata selgelt, arendajad pĂŒĂŒavad igal juhul tĂ”kestada ennast oma andmebaasi eest kilomeetrite kaupa abstraktsioone, mis viitab teatud .
Teiselt poolt barricade pooldajad puhtast "handmade"-SQL-ist mĂ€rgivad, et on vĂ”imalus vĂ€lja pigistada kĂ”ik, mis suudab nende andmebaasi, ilma tĂ€iendavate vahekihtide ja abstraktsioonideta. SeetĂ”ttu tekivad "andmekesksed" projektid, kus andmetega tegelevad spetsiaalselt koolitatud inimesed (nemad on "basiistid", nad on "basiinikud", nad on "basiidid" jne), ja arendajad saavad lihtsalt "tĂ”mmata" valmis vaatamisvÀÀrsusi ja salajasi protseduure, mitte sĂŒvenedes detailidesse.
Aga mis kui vĂ”tta parem osa kahest maailmast? Kuidas seda on tehtud imelises tööriistas, millel on elujĂ”uline nimi . TĂ”in vĂ€lja paar rida ĂŒldkonseptsioonist minu vabalt tĂ”lgitud versioonis, kuid selle kohtadega saab tutvuda pĂ”hjalikumalt .
Clojure on suurepĂ€rane keel DSL-ide loomiseks, kuid SQL ise on juba lahe DSL ning me ei vajagi veel ĂŒhte. S-expressionid on imelised, kuid siin ei lisa need midagi uut. LĂ”puks saame ainult sulud sulude pĂ€rast. Ei nĂ”ustu? Siis oodake aega, mil andmebaasi abstraktsioon hakkab lekima, ja te alustate funktsiooni vastu vĂ”itlemist. (raw-sql)
Ja mida teha? JĂ€tame SQL lihtsaks SQL-iks â ĂŒks fail ĂŒhe pĂ€ringu jaoks:
-- name: users-by-country
select *
from users
where country_code = :country_code⊠ja seejÀrel loe see fail ning muuda see tavaliseks Clojure funktsiooniks:
(defqueries "some/where/users_by_country.sql"
{:connection db-spec})
;;; Funktsioon nimega `users-by-country` on loodud.
;;; Kasutame seda:
(users-by-country {:country_code "GB"})
;=> ({:name "Kris" :country_code "GB" ...} ...)JÀrgides pÔhimÔtet "SQL eraldi, Clojure eraldi", saate:
- Mitte mingeid sĂŒntaktilisi ĂŒllatusi. Teie andmebaas (nagu iga teine) ei vasta 100% SQL standardile â kuid Yesqli jaoks ei ole see oluline. Te ei pea kunagi aega kulutama funktsioonide jahtimisele, mille sĂŒntaks vastab SQL-ile. Te ei pea kunagi tagasi pöörduma funktsiooni (raw-sql "some (âfunkyâ :: SYNTAX)")).
- Parim toimetaja tugi. Teie toimetaja juba toetab SQLi suurepÀraselt. SQL-i sÀilitades saate seda lihtsalt kasutada.
- Meeskonna ĂŒhilduvus. Teie DBA-d saavad lugeda ja kirjutada SQL-i, mida te kasutate oma Clojure projektis.
- Lihtsustatud jÔudluse seadistamine. Peate koostama plaani probleemse pÀringu jaoks? See pole probleem, kui teie pÀring on tavaline SQL.
- PĂ€ringute taaskasutamine. TĂ”stke need samad SQL-failid teistesse projektidesse, sest see on lihtsalt hea vana SQL â jagage seda lihtsalt.
Minu arvates on idee vÀga lahe ja samas vÀga lihtne, tÀnu millele on projekt omandanud palju erinevates keeltes. Ja proovime edasi rakendada sarnast filosoofiat SQL-koodi eraldamiseks kÔigest muust, kaugel ORM-ist.
IDE & DB-haldurid
Alustame lihtsast igapĂ€evasest ĂŒlesandest. Tihti peame andmebaasist otsima objekte, nĂ€iteks leidma skeemist tabeli ja uurima selle struktuuri (milliseid veerge, vĂ”tmeid, indekseid, piiranguid jms kasutatakse). Iga graafilise IDE vĂ”i vĂ€hesegi DB-manageri puhul ootame nende funktsioonide olemasolu esmajĂ€rjekorras. See peab olema kiire ja me ei peaks ootama poole tunni kuni akna ilmumiseni vajaliku teabega (eriti aeglase ĂŒhenduse korral kaugarvutiga), ja samas peab saadud teave olema vĂ€rske ja asjakohane, mitte mingisugune vahemĂ€llu salvestatud vanarahu. Mida keerulisem ja suurem on andmebaas ning mida rohkem neid on, seda keerulisem on seda teha.
Aga tavaliselt panen hiire kuhugi nurka ja lihtsalt kirjutan koodi. Oletame, et peame vĂ€lja selgitama, millised tabelid (ja milliste omadustega) on skeemis "HR". Enamikus andmebaasihaldussĂŒsteemides saab vajaliku tulemuse kĂ€tte sellise lihtsa pĂ€ringuga information_schema-st:
select table_name
, ...
from information_schema.tables
where schema = 'HR'Andmebaasist andmebaasi sisaldus varieerub sĂ”ltuvalt iga andmebaasihaldussĂŒsteemi vĂ”imalustest. NĂ€iteks MySQL-i puhul vĂ”ib sellelt samalt registrilt saada selle andmebaasihaldussĂŒsteemi spetsiifilisi tabeli parameetreid:
select table_name
, storage_engine -- Kasutatav "mootor" ("MyISAM", "InnoDB" jne)
, row_format -- Ridade formaat ("Fixed", "Dynamic" jne)
, ...
from information_schema.tables
where schema = 'HR'Oracle ei tunne information_schema-d, aga sellel on olemas , ja suuri probleeme ei teki:
select table_name
, pct_free -- Minimaalne vaba ruum andmepaketis (%)
, pct_used -- Minimaalne kasutatud ruum andmepaketis (%)
, last_analyzed -- Viimane statistika kogumise kuupÀev
, ...
from all_tables
where owner = 'HR'Sama kÀib ka ClickHouse'i kohta:
select name
, engine -- Kasutatav "mootor" ("MergeTree", "Dictionary" jne)
, ...
from system.tables
where database = 'HR'M midagi sarnast saab teha ka Cassandra's (kus on columnfamilies tabelite asemele ja keyspace'id skeemide asemele):
select columnfamily_name
, compaction_strategy_class -- PrĂŒgi kogumise strateegia
, gc_grace_seconds -- PrĂŒgi eluiga
, ...
from system.schema_columnfamilies
where keyspace_name = 'HR'Enamikule teistest andmebaasidest vĂ”ib ka sarnaseid pĂ€ringuid vĂ€lja mĂ”elda (isegi Mongos on , mis sisaldab teavet kĂ”igi sĂŒsteemi kollektsioonide kohta).
Muidugi vĂ”ib selle meetodi abil saada teavet mitte ainult tabelite, vaid ka ĂŒldiselt iga objekti kohta. Aeg-ajalt jagavad head inimesed sellist koodi erinevate andmebaaside jaoks, nagu nĂ€iteks Habr'i artiklis "Funktsioonid PostgreSQL andmebaaside dokumenteerimiseks" (, , ). Loomulikult ei ole kogu selle hulga pĂ€ringute meeles hoidmine ja pidev nende sisestamine just "tohutu meelelahutus", seetĂ”ttu on minu lemmik-IDE/redaktoris eelnevalt ettevalmistatud komplekt sageli kasutatavaid snippet'e, ning jÀÀb vaid objekti nimed mallidesse sisestada.
Nii et selline navigeerimise ja objektide otsimise meetod on palju paindlikum, sÀÀstab palju aega ning vÔimaldab saada tÀpselt seda teavet ja sellisel kujul, nagu praegu vajalik on (nagu nÀiteks postituses ).
Objektidega operatsioonid
PÀrast seda, kui oleme leidnud ja uurinud vajalikud objektid, on Ôige aeg nendega midagi kasulikku teha. Loomulikult, hoides samal ajal sÔrmed klaviatuurilt eemal.
Ei ole saladus, et lihtne tabeli kustutamine nÀeb vÀlja peaaegu sama kÔigis andmebaasides:
drop table hr.personsKuid tabeli loomine on juba huvitavam. Praktikas teab praktiliselt iga andmebaasitarkvara (sealhulgas paljud NoSQL lahendused) mingil kujul "create table", ning selle peamine osa ei erine eriti (nimi, veergude loetelu, andmetĂŒĂŒbid), kuid teised detailid vĂ”ivad oluliselt erineda ning sĂ”ltuvad konkreetse andmebaasihalduse sisemisest struktuurist ja vĂ”imalustest. Minu lemmiknĂ€ide â Oracle'i dokumentatsioonis on ainult tĂŒhi BNF andmestruktuuri kohta "create table" . Teised andmebaasid pakuvad tagasihoidlikumaid vĂ”imalusi, kuid igaĂŒhel on ka palju huvitavaid ja ainulaadseid funktsioone tabelite loomisel (, , , ). Vaevalt suudab mĂ”ni graafiline "tuntud" IDE (eriti universaalne) tĂ€ielikult katta kĂ”iki neid vĂ”imalusi, ja isegi kui suudab, on see kindlasti nĂ€gemine mitte nĂ”rganĂ€rvilistele. Samal ajal vĂ”imaldab Ă”igesti ja Ă”igeaegselt kirjutatud kĂ€sk create table kasutada kĂ”iki neid vĂ”imalusi tĂ”rgeteta, tagades teie andmete salvestamise ja juurdepÀÀsu usaldusvÀÀrsuse, optimaalsuse ja maksimaalse mugavuse.
Paljude andmebaaside puhul on ka spetsiaalsed objektitybid, mis puuduvad teistes andmebaasides. Lisaks saame sooritada toiminguid mitte ainult andmebaasi objektide, vaid ka andmebaasi enda ĂŒle, nĂ€iteks "tappa" protsess, vabastada mĂ€lu, lubada jĂ€lgimist, minna "ainult lugemise" reĆŸiimi ja palju muud.
NĂŒĂŒd joonistame natuke.
Ăks levinumaid ĂŒlesandeid on luua diagramm andmebaasi objektidest, et ilusas pildis nĂ€ha objekte ja nende omavahelisi seoseid. Peaaegu iga graafiline IDE, mĂ”ned kĂ€surea utiliidid, spetsialiseeritud graafilised tööriistad ja modelleerijad saavad sellega hakkama. Nad joonistavad teile "nagu oskavad", ning sellele protsessile saab mĂ”juda vaid mĂ”ne parameetri abil konfiguratsioonifailis vĂ”i valikutega liideses.
Kuid seda probleemi saab lahendada palju lihtsamalt, paindlikumalt ja elegantsemalt, loomulikult koodi abil. Igasuguste diagrammide koostamiseks on meil mitmeid spetsialiseeritud markeerimiskeeli (DOT, GraphML jne), ja nende jaoks terve rida rakendusi (GraphViz, PlantUML, Mermaid), mis oskavad selliseid juhiseid lugeda ja visualiseerida erinevates formaatides. Ăldiselt teame, kuidas hankida teavet objektide ja nende seoste kohta.
Tuletame meelde vÀikese nÀite selle kohta, kuidas see vÔiks vÀlja nÀha, kasutades PlantUML ja (vasakul SQL-pÀring, mis genereerib vajaliku juhise PlantUML jaoks, ja paremal tulemus):

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'Ja kui natuke vaeva nÀha, siis ER-mallile toetudes saame midagi, mis sarnaneb tÔelise ER-diagrammiga:
SQL-pÀring on veidi keerulisem.
-- Pealkiri
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
-- Tabelid
select format('Table(%s, "%s n information about %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
-- Suhted tabelite vahel
select distinct ccu.table_name || ' "1" --> "0..N" ' || tc.table_name || format(' : "A %s may haven many %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
-- Jalus
select '@enduml' 
Kui tĂ€helepanelikult vaadata, siis paljud visualiseerimistööriistad kasutavad ka alla sarnaseid pĂ€ringuid. TĂ”si, et need pĂ€ringud on tavaliselt sĂŒgavalt , rÀÀkimata nende modifitseerimisest.
MÔÔdikud ja jÀlgimine.
LĂ€hme edasi traditsiooniliselt keerulise teema juurde - andmebaasi jĂ”udluse jĂ€lgimine. KĂ”nelen ĂŒhest vĂ€ikesest tĂ”estisĂŒndinud loost, mille rÀÀkis mulle "ĂŒks mu sĂ”ber". Ăhes projektis oli olemas ĂŒlivĂ”imas DBA, kellel oli vĂ€he tutvusi arendajate seas ning kelle nĂ€gu oli paljudele tundmatu (kuigi kuulujuttude jĂ€rgi töötas ta kusagil naaberhoones). Hetkel "X", kui suure jaemĂŒĂŒja tootmisse sĂŒsteem hakkas taas "halvasti tundma", saatis ta vaikselt ekraanipilte Oracle Enterprise Managerist, kus ta hoolikalt tĂ€histas kriitilisi kohasid punase markeriga "arusaadavuse" jaoks (see aitas, Ă”rnalt öeldes, vĂ€he). Nii tuli selle "fotoga" probleeme lahendada. Samas ei olnud kellelgi juurdepÀÀsu kulukale (nii otseses kui kaudses mĂ”ttes) Enterprise Managerile, kuna sĂŒsteem oli keeruline ja kallis, Ă€kki "arendajad puutuvad millegi kĂŒlge ja rikuvad kĂ”ik Ă€ra". SeetĂ”ttu leidsid arendajad "empriirilisel" teel viivituste pĂ”hjused ja vĂ€ljastasid parandused. Kui DBA Ă€hvardav kiri ei saabunud varsti uuesti, hingasid kĂ”ik kergendatult vĂ€lja ja naasid oma praeguste ĂŒlesannete juurde (enne uut Kirja).
Kuid jĂ€lgimisprotsess vĂ”ib olla palju toredam ja sĂ”bralikum ning mis kĂ”ige tĂ€htsam - juurdepÀÀsetav ja lĂ€bipaistev kĂ”igile. Olgugi et vĂ”iks vĂ€hemalt pĂ”hiosa, kui tĂ€iendus peamistele jĂ€lgimissĂŒsteemidele (mis on kindlasti kasulikud ja paljudes olukordades asendamatud). Iga andmebaas on valmis jagama teavet oma hetkeseisust ja jĂ”udlusest vabalt ja tĂ€iesti tasuta. Samas "veres" Oracle DB-s saab praktiliselt iga teabe jĂ”udluse kohta saada sĂŒsteemsetest vaadetest, alates protsessidest ja sessioonidest kuni vahemĂ€lu olekuni (nĂ€iteks, , jaotis "JĂ€lgimine"). Postgresql-is on samuti terve hulk sĂŒsteemseid vaateid, et , sealhulgas mĂ”ned, mis on asendamatud iga DBA igapĂ€evaelus, nagu , , . MySQL-is on selle jaoks isegi eraldi skeem . Ja MongoDB-s on sisseehitatud , mis kogub jĂ”udlusandmeid sĂŒsteemilikku kollektsiooni .
Seega, varustades end mĂ”ne mÔÔdikute koguja (Telegraf, Metricbeat, Collectd) ja mÔÔdikute salvestiga (InfluxDB, Elasticsearch, Timescaledb) ning visualiseerijaga (Grafana, Kibana), on vĂ”imalik luua piisavalt kerge ja paindlik jĂ€lgimissĂŒsteem, mis on tihedalt integreeritud teiste ĂŒldiste sĂŒsteemimeetriatega (mida saadakse nĂ€iteks rakenduste serverilt, operatsioonisĂŒsteemilt jne). Nagu nĂ€iteks on tehtud pgwatch2-s, kus kasutatakse kombinatsiooni InfluxDB + Grafana ja komplekti pĂ€ringutest sĂŒsteemivaadetele, kuhu saab ka lisada kohandatud pĂ€ringuid. .
KokkuvÔttes
Ja see on ainult umbkaudne nimekiri sellest, mida meie andmebaasiga saab teha tavalise SQL-koodi abil. Olen kindel, et leidub veel palju kasutusvÔimalusi, kirjutage kommentaaridesse. Ja sellest, kuidas (ja mis kÔige tÀhtsam, miks) kÔik see automatiseerida ja oma CI/CD torujuhtmesse kaasata, rÀÀgime jÀrgmine kord.
Allikas: habr.com
