SQL je jednou ze základních technologií, které používám všude tam, kde aplikace potřebují spolehlivě pracovat se strukturovanými relačními daty.

Se SQL pracuji v backendových systémech, webových aplikacích, desktopovém softwaru, Android aplikacích, automatizačních workflow a úlohách zaměřených na zpracování dat.

SQL pro mě není jen způsob, jak načítat řádky z tabulky. Je to jazyk pro vyjádření toho, jak mají být strukturovaná data ukládána, propojována, filtrována, agregována a transformována.

Jak SQL používám

SQL používám například pro:

Se SQL běžně pracuji prostřednictvím relačních databázových systémů, jako jsou PostgreSQL a SQLite.

Relační datové modelování

Kvalitní SQL obvykle začíná kvalitním datovým modelem.

Relační struktury navrhuji podle skutečných entit a vztahů mezi nimi, nikoli jako nahodilou kolekci tabulek.

Typický model může obsahovat:

Pečlivě zvažuji:

Dobře navržené schéma může výrazně zjednodušit zbytek aplikace.

Dotazy SELECT

Nejběžnější operací v SQL je načítání dat.

Dotazy SELECT používám k získání přesně těch informací, které aplikace potřebuje, namísto načítání celých tabulek a následného filtrování v aplikačním kódu.

Základní dotaz může vypadat například takto:

SELECT id, title, status
FROM projects
WHERE status = 'active'
ORDER BY created_at DESC;

I jednoduché dotazy začínají být důležité, pokud se spouštějí často nebo nad velkými objemy dat.

Proto věnuji pozornost jak jejich přehlednosti, tak výkonu.

Filtrování

Podmínky WHERE používám k omezení výsledků už na úrovni databáze.

Filtrování může zahrnovat:

Přesunutí vhodného filtrování přímo do SQL bývá efektivnější než načíst zbytečná data a zpracovávat je až později.

Řazení

Řazení dat bývá často přímo součástí dotazu.

ORDER BY používám všude tam, kde výsledky potřebují předvídatelné pořadí, například:

U stránkovaných API je deterministické řazení obzvlášť důležité.

Bez něj se mohou záznamy mezi jednotlivými požadavky objevovat na nekonzistentních pozicích.

JOINy

JOINy patří mezi nejdůležitější možnosti relačních databází.

Používám je ke kombinování informací uložených v oddělených, ale vzájemně souvisejících tabulkách.

Například:

SELECT p.title, u.name
FROM projects p
JOIN users u ON u.id = p.owner_id;

Databáze tak může vztahy reprezentovat explicitně namísto opakovaného ukládání stejných informací do mnoha záznamů.

Podle toho, zda je vztah povinný nebo volitelný, používám odpovídající typ JOINu.

Agregace

SQL je velmi užitečné také pro sumarizaci dat.

Agregační funkce používám například pro výpočty:

Například:

SELECT status, COUNT(*)
FROM projects
GROUP BY status;

To může sloužit jako základ pro:

Provádět tyto výpočty přímo v databázi může být výrazně efektivnější než zpracovávat rozsáhlé výsledky v aplikačním kódu.

GROUP BY

GROUP BY umožňuje shrnovat záznamy podle smysluplných kategorií.

Používám jej například pro otázky typu:

Dbám na to, aby seskupené dotazy odpovídaly skutečné obchodní logice a nevytvářely sice technicky platné, ale zavádějící agregace.

Poddotazy

Poddotazy jsou užitečné v případech, kdy jeden dotaz závisí na výsledku jiného.

Používám je tam, kde zpřehledňují záměr a kde je databázový optimalizátor dokáže efektivně provést.

U složitějších případů může být čitelnějším řešením common table expression nebo JOIN.

Common Table Expressions

Common Table Expressions, obvykle zapisované pomocí WITH, mohou výrazně zpřehlednit složitější SQL.

Umožňují rozdělit větší operaci do pojmenovaných logických kroků.

Například:

WITH active_projects AS (
    SELECT *
    FROM projects
    WHERE status = 'active'
)
SELECT *
FROM active_projects
ORDER BY created_at DESC;

CTE používám tam, kde zvyšují čitelnost a usnadňují údržbu vícekrokových dotazů.

Vkládání a aktualizace

SQL se používá také ke změnám stavu aplikace.

Operace INSERT, UPDATE a DELETE používám opatrně, zejména pokud zasahují více souvisejících záznamů.

Před spuštěním destruktivních dotazů ověřuji, zda jsou podmínky dostatečně přesné a nehrozí změna nechtěných dat.

U složitějších aktualizací si často nejprve ověřím cílové řádky odpovídajícím dotazem SELECT.

Transakce

Transakce jsou zásadní všude tam, kde se více operací musí chovat jako jeden logický celek.

Typické workflow může zahrnovat:

  1. vytvoření záznamu,
  2. aktualizaci související tabulky,
  3. zápis historie,
  4. potvrzení výsledku.

Pokud jeden krok selže, lze celou transakci vrátit zpět.

Tím se databáze chrání před částečně dokončenými operacemi.

Transakce používám všude tam, kde je konzistence dat důležitější než nezávislé provedení jednotlivých příkazů.

Integrita dat

Preferuji databáze, které důležitá pravidla samy vynucují.

Může jít například o:

Validace v aplikaci je užitečná pro poskytování srozumitelných chybových hlášení.

Databázová omezení však tvoří poslední obrannou vrstvu proti neplatnému perzistentnímu stavu.

Primární klíče

Každá důležitá relační entita potřebuje spolehlivý identifikátor.

Primární klíče používám k zajištění stabilní identity záznamů.

Podle typu systému může jít například o:

Strategie klíčů závisí na tom, jak jsou data vytvářena, distribuována a odkazována.

Cizí klíče

Cizí klíče vyjadřují vztahy mezi tabulkami.

Používám je k zajištění toho, aby odkazy směřovaly na platné záznamy.

Tím lze zabránit situacím, jako jsou:

Tam, kde je to vhodné, zároveň explicitně definuji chování při mazání.

Unikátní omezení

Pokud by duplicitní hodnota porušovala pravidla aplikace, patří kontrola unikátnosti do databáze.

Může jít například o:

Kontrola duplicit pouze v aplikačním kódu může stále umožnit vznik race conditions.

Databázové omezení poskytuje silnější ochranu.

Indexy

Indexy mohou mít zásadní vliv na výkon dotazů.

Přidávám je pro sloupce, které se často používají při:

Indexy však nejsou zdarma.

Zvyšují nároky na úložiště a přidávají režii při zápisu.

Proto je vytvářím podle skutečných vzorců dotazování, nikoli automaticky pro každý sloupec.

Výkon dotazů

Dotaz, který funguje dobře nad 100 řádky, se může nad miliony řádků chovat zcela jinak.

Sleduji zejména:

Při problémech s výkonem raději zkoumám skutečný execution plan, než abych pouze odhadoval příčinu.

Omezení N+1 dotazů

Jedním z častých výkonových problémů na aplikační úrovni je vzor N+1 dotazů.

Například:

  1. načíst 100 projektů,
  2. pro vlastníka každého projektu spustit další dotaz.

Jedna logická operace se tak může změnit ve stovky databázových požadavků.

Související data se snažím načítat efektivně prostřednictvím:

Omezení počtu round-tripů bývá často důležitější než mikroskopická optimalizace jednotlivých SQL příkazů.

Stránkování

Velké množiny výsledků by se neměly vždy vracet najednou.

Stránkování používám tam, kde je vhodné, například pro:

Podle aplikace lze použít:

U velmi velkých datasetů mohou být kurzorové přístupy předvídatelnější než stále větší offsety.

Parametrizované dotazy

SQL dotazy nikdy nevytvářím slepým spojováním nedůvěryhodných hodnot do řetězce dotazu.

Místo toho používám parametrizované dotazy.

Tím se chrání aplikace proti SQL injection a zároveň se odděluje syntaxe SQL od samotných dat.

Koncepčně:

SELECT *
FROM users
WHERE email = ?;

Databázový ovladač zpracuje hodnotu nezávisle na struktuře dotazu.

Jde o jeden z nejzákladnějších principů databázové bezpečnosti.

Prevence SQL injection

SQL injection není pouze teoretické riziko.

Každá aplikace přijímající externí vstupy musí počítat s tím, že uživatelé mohou odeslat neočekávané hodnoty.

Spoléhám na:

Samotný escaping není náhradou za správnou parametrizaci dotazů.

Databázové migrace

Databázová schémata se vyvíjejí společně s aplikacemi.

Změny schématu považuji za verzované změny aplikace.

Migrace mohou:

Dobrá migrace musí počítat s existujícími produkčními daty, nikoli pouze s prázdnou vývojovou databází.

Zpětná kompatibilita

Změny schématu mohou ovlivnit starší verze aplikace nebo dlouhodobě běžící nasazení.

Snažím se vyhýbat zbytečným breaking changes.

U větších systémů může být bezpečnější:

  1. přidat novou strukturu,
  2. aktualizovat aplikační logiku,
  3. migrovat data,
  4. zastaralá pole odstranit až později.

Tento postupný přístup může výrazně snížit rizika nasazení.

SQL a PostgreSQL

PostgreSQL je jedním z databázových systémů, které používám pro serverové aplikace.

Nabízí bohatou implementaci SQL a pokročilé funkce pro větší backendová zatížení.

PostgreSQL používám v případech, kdy projekt potřebuje:

SQL je jazyk, kterým s touto databází komunikuji.

SQL a SQLite

SQL používám také se SQLite.

SQLite je vhodná například pro:

Stejné relační principy zůstávají zachovány:

Hlavním rozdílem je architektura nasazení, nikoli základní datový model.

SQL ve vývoji pro Android

V Android aplikacích běžně pracuji se SQLite prostřednictvím Room.

Room poskytuje typovanou abstrakci nad databází, ale znalost SQL zůstává důležitá.

Dotazy stále mohou definovat:

Znalost SQL usnadňuje návrh Room entit i diagnostiku výkonových problémů.

SQL s Pythonem

Python se s relačními databázemi velmi dobře doplňuje.

Používám jej například pro:

SQL řeší práci s databází, zatímco Python koordinuje širší workflow zpracování.

SQL s Flaskem

V aplikacích založených na Flasku bývá SQL často součástí perzistenční vrstvy.

Typická architektura může vypadat například takto:

HTTP požadavek → validace → aplikační služba → SQL/databáze → JSON odpověď

Databázové záležitosti preferuji oddělovat od HTTP routingu, aby se dotazy a obchodní pravidla snáze testovaly a udržovaly.

SQL s PHP a WordPressem

Se SQL se intenzivně setkávám také při vývoji v PHP a WordPressu.

Samotný WordPress používá relační databázi například pro:

U vlastní funkcionality preferuji databázová API WordPressu nebo bezpečný parametrizovaný přístup namísto vytváření nebezpečných raw SQL dotazů.

Znalost podkladového datového modelu je užitečná při diagnostice výkonu i při implementaci pokročilejších vlastních funkcí.

SQL a REST API

REST API často zpřístupňují relační data.

Jeden API požadavek může vést k SQL operacím, jako jsou:

API kontrakty a databázová schémata držím oddělené.

Veřejné API by nemělo být nuceno kopírovat každý detail interní struktury databáze.

SQL a zpracování dat

SQL je mimořádně užitečné při transformaci strukturovaných datasetů.

Používám jej například pro:

Pokud data již leží v databázi, může být provedení těchto operací přímo v SQL výrazně efektivnější než jejich úplný export do jiného jazyka.

Window Functions

Pro analytické a reportovací úlohy jsou mimořádně užitečné window functions.

Umožňují provádět výpočty nad souvisejícími řádky bez zkolabování celé výsledné množiny.

Lze je využít například pro:

Jde o velmi silný nástroj pro řešení problémů, které by jinak vyžadovaly složité zpracování na aplikační straně.

Views

Views mohou poskytovat znovupoužitelné databázové reprezentace složitých dotazů.

Používám je tam, kde více částí systému potřebuje stejnou odvozenou datovou strukturu.

Dobře navržený view může zjednodušit aplikační dotazy a současně zachovat normalizované podkladové tabulky.

Views však nepoužívám k zakrývání zbytečně komplikované databázové architektury.

JSON a SQL

Moderní relační databáze umějí pracovat také s JSON.

JSON sloupce používám tam, kde určitá část dat skutečně těží z flexibilnější struktury.

Neukládám však automaticky všechno do JSON jen proto, že to databáze umožňuje.

Pokud mají informace stabilní vztahy a často se podle nich filtruje, bývají klasické relační sloupce zpravidla lepším návrhem.

Hodnoty NULL

Práce SQL s hodnotou NULL vyžaduje vědomý přístup.

NULL představuje chybějící nebo neznámou informaci, nikoli prázdný řetězec nebo nulu.

Možnost hodnot NULL definuji podle skutečné domény.

Pokud je určitá hodnota pro platný záznam povinná, mělo by to schéma zpravidla také vynucovat.

Datum, čas a časová pásma

Datum a čas jsou častým zdrojem chyb v aplikacích.

Sleduji zejména:

Datové a časové hodnoty by se neměly považovat za libovolné řetězce, pokud je databáze umí reprezentovat sémanticky.

Konkurentní přístup

K databázi může současně přistupovat více požadavků.

Zápisové operace proto navrhuji s ohledem na konkurentní přístup.

Transakce, omezení a vhodná úroveň izolace pomáhají předcházet problémům, jako jsou:

Aplikační kód by nikdy neměl předpokládat, že je jediným procesem používajícím databázi.

Správa připojení

Serverové aplikace musí efektivně spravovat databázová připojení.

Otevírat zcela nové připojení pro každou drobnou operaci bez jakékoli strategie poolingu může vytvářet zbytečnou režii.

Podle frameworku a architektury nasazení používám vhodnou správu připojení a dbám na jejich správné uvolňování.

Zálohy

SQL databáze často obsahují některá z nejdůležitějších dat celé aplikace.

Zálohování proto považuji za součást produkční architektury.

Strategie záloh musí zohledňovat:

Záloha, u které nikdy nebyla ověřena obnova, sama o sobě nestačí.

Logování a debugging

Pokud se databáze chová neočekávaně, zkoumám:

To pomáhá odlišit chyby v aplikační logice od problémů s výkonem databáze.

Chování dotazů raději měřím, než abych pouze hádal.

SQL v mém technologickém stacku

SQL běžně používám společně s technologiemi, jako jsou:

SQL poskytuje relační datovou vrstvu, která tyto aplikace propojuje s trvalým strukturovaným úložištěm.

Proč používám SQL

SQL používám proto, že relační databáze zůstávají jedním z nejefektivnějších způsobů ukládání a zpracování strukturovaných aplikačních dat.

Jeho síla nespočívá pouze v načítání záznamů.

SQL umožňuje vyjadřovat vztahy, vynucovat integritu, agregovat informace, provádět složité dotazy a udržovat důležitý stav aplikace konzistentní.

Pro backendové systémy, mobilní aplikace, desktopový software i workflow zaměřená na zpracování dat je proto SQL jednou ze základních technologií mého stacku.

SQL is one of the core technologies I use when applications need reliable access to structured relational data.

I work with SQL in backend systems, web applications, desktop software, Android applications, automation workflows and data-processing tasks.

For me, SQL is not simply a way to retrieve rows from a table. It is a language for expressing how structured data should be stored, related, filtered, aggregated and transformed.

How I use SQL

I use SQL for tasks such as:

I commonly work with SQL through relational database systems such as PostgreSQL and SQLite.

Relational Data Modeling

Good SQL usually starts with good data modeling.

I design relational structures around actual entities and relationships rather than treating the database as a collection of arbitrary tables.

A typical model may contain:

I think carefully about:

A well-designed schema can simplify the rest of the application significantly.

SELECT Queries

The most common SQL operation is retrieving data.

I use SELECT queries to obtain exactly the information the application needs rather than loading entire tables and filtering them later in application code.

A basic query might look like:

SELECT id, title, status
FROM projects
WHERE status = 'active'
ORDER BY created_at DESC;

Even simple queries become important when they are executed frequently or against large datasets.

I therefore pay attention to both clarity and performance.

Filtering

I use WHERE conditions to reduce results at the database level.

Filtering can involve:

Pushing appropriate filtering into SQL is usually more efficient than retrieving unnecessary data and processing it later.

Sorting

Ordering data is often part of the query itself.

I use ORDER BY for results that need predictable presentation, such as:

For paginated APIs, deterministic ordering is especially important.

Without it, records may appear in inconsistent positions between requests.

JOINs

JOINs are one of the most important features of relational databases.

I use them to combine information stored in separate but related tables.

For example:

SELECT p.title, u.name
FROM projects p
JOIN users u ON u.id = p.owner_id;

This allows the database to represent relationships explicitly instead of duplicating the same information across many records.

I use the appropriate join type according to whether relationships are required or optional.

Aggregation

SQL is also useful for summarizing data.

I use aggregate functions for calculations such as:

For example:

SELECT status, COUNT(*)
FROM projects
GROUP BY status;

This can support:

Performing these calculations directly in the database can be far more efficient than processing large result sets in application code.

GROUP BY

GROUP BY allows records to be summarized according to meaningful categories.

I use it for questions such as:

I make sure grouped queries reflect the actual business logic rather than producing technically valid but misleading aggregates.

Subqueries

Subqueries can be useful when one query depends on the result of another.

I use them where they make the intent clear and where the database optimizer can execute them efficiently.

For more complex cases, a common table expression or a join may provide a more readable solution.

Common Table Expressions

Common Table Expressions, usually written with WITH, can make complex SQL easier to understand.

They allow a larger operation to be broken into named logical stages.

For example:

WITH active_projects AS (
    SELECT *
    FROM projects
    WHERE status = 'active'
)
SELECT *
FROM active_projects
ORDER BY created_at DESC;

I use CTEs when they improve clarity and make multi-stage queries easier to maintain.

Inserts and Updates

SQL is also used to modify application state.

I use INSERT, UPDATE and DELETE operations carefully, especially when they affect multiple related records.

Before writing destructive queries, I make sure the conditions are specific enough to avoid modifying unintended data.

For complex updates, I often verify the target rows first with a corresponding SELECT.

Transactions

Transactions are essential when multiple operations need to behave as one logical unit.

A typical workflow may involve:

  1. creating a record,
  2. updating a related table,
  3. writing history,
  4. committing the result.

If one step fails, the transaction can be rolled back.

This protects the database from partially completed operations.

I use transactions where data consistency matters more than simply executing each statement independently.

Data Integrity

I prefer databases that enforce important rules themselves.

That may include:

Application validation is useful for providing friendly error messages.

Database constraints provide a final line of defense against invalid persistent state.

Primary Keys

Every important relational entity needs a reliable identifier.

I use primary keys to provide stable identity for records.

Depending on the system, this may involve:

The key strategy depends on how the data is created, distributed and referenced.

Foreign Keys

Foreign keys express relationships between tables.

I use them to ensure that references point to valid records.

This prevents situations such as:

Where appropriate, I also define deletion behavior explicitly.

Unique Constraints

Uniqueness belongs in the database when duplicate values would violate application rules.

Examples may include:

Checking for duplicates only in application code can still allow race conditions.

A database constraint provides stronger protection.

Indexes

Indexes can make a major difference to query performance.

I add them for columns that are frequently used in:

However, indexes are not free.

They increase storage requirements and add overhead to writes.

I therefore create them according to actual query patterns rather than indexing every column automatically.

Query Performance

A query that works on 100 rows may behave very differently on millions of rows.

I pay attention to:

For performance problems, I prefer examining the actual execution plan rather than guessing.

Avoiding N+1 Queries

One common application-level performance problem is the N+1 query pattern.

For example:

  1. load 100 projects,
  2. execute another query for the owner of each project.

This can turn one logical operation into hundreds of database requests.

I try to fetch related data efficiently through:

Reducing round trips is often more important than micro-optimizing individual statements.

Pagination

Large result sets should not always be returned at once.

I use pagination where appropriate for:

Depending on the application, this may use:

For very large datasets, cursor-based approaches can be more predictable than increasingly large offsets.

Parameterized Queries

I never build SQL queries by blindly concatenating untrusted values into query strings.

Instead, I use parameterized queries.

This protects against SQL injection and keeps SQL syntax separate from data.

Conceptually:

SELECT *
FROM users
WHERE email = ?;

The database driver handles the value independently from the query structure.

This is one of the most fundamental database security practices.

SQL Injection Prevention

SQL injection is not only a theoretical risk.

Any application that accepts external input needs to assume that users can send unexpected values.

I rely on:

Escaping alone is not a substitute for proper query parameterization.

Database Migrations

Database schemas evolve together with applications.

I treat schema changes as versioned application changes.

Migrations may:

A good migration needs to consider existing production data, not only an empty development database.

Backward Compatibility

Schema changes can affect older application versions or long-running deployments.

I try to avoid unnecessary breaking changes.

For larger systems, it may be safer to:

  1. add the new structure,
  2. update application logic,
  3. migrate data,
  4. remove obsolete fields later.

This staged approach can reduce deployment risk.

SQL and PostgreSQL

PostgreSQL is one of the database systems I use for server-side applications.

It provides a rich SQL implementation and advanced functionality for larger backend workloads.

I use PostgreSQL when a project needs:

SQL provides the language I use to communicate with that database.

SQL and SQLite

I also use SQL with SQLite.

SQLite is useful for:

The same relational concepts still apply:

The main difference is deployment architecture rather than the fundamental data model.

SQL in Android Development

In Android applications, I commonly work with SQLite through Room.

Room provides a typed abstraction over the database, but SQL remains important.

Queries may still define:

Understanding SQL makes it easier to design Room entities and diagnose performance problems.

SQL with Python

Python works well with relational databases.

I use it for:

SQL handles the database work while Python coordinates the wider processing workflow.

SQL with Flask

In Flask-based applications, SQL is often part of the persistence layer.

A typical architecture may look like:

HTTP request → validation → application service → SQL/database → JSON response

I prefer keeping database concerns separated from HTTP routing so queries and business rules remain easier to test and maintain.

SQL with PHP and WordPress

I also encounter SQL extensively in PHP and WordPress development.

WordPress itself relies on a relational database for:

For custom functionality, I prefer using WordPress database APIs or safe parameterized access rather than constructing unsafe raw queries.

Understanding the underlying data model is useful when diagnosing performance or implementing more advanced custom features.

SQL and REST APIs

REST APIs frequently expose relational data.

An API request may result in SQL operations such as:

I keep API contracts and database schemas separate.

The public API should not be forced to mirror every detail of the internal database structure.

SQL and Data Processing

SQL is extremely useful for transforming structured datasets.

I use it for operations such as:

For database-resident data, performing these operations directly in SQL can be much more efficient than exporting everything into another language first.

Window Functions

For analytical and reporting workloads, window functions are especially useful.

They allow calculations across related rows without collapsing the result set.

This can support tasks such as:

They are a powerful tool for solving problems that would otherwise require complicated application-side processing.

Views

Views can provide reusable database-level representations of complex queries.

I use them when multiple parts of a system need the same derived data structure.

A well-designed view can simplify application queries while keeping the underlying tables normalized.

I avoid using views to hide unnecessarily complicated database architecture.

JSON and SQL

Modern relational databases can also work with JSON.

I use JSON columns where part of the data genuinely benefits from a flexible structure.

However, I do not automatically store everything as JSON simply because the database supports it.

If information has stable relationships and needs frequent filtering, normal relational columns are often the better design.

Null Values

SQL’s handling of NULL requires deliberate thinking.

A null value represents missing or unknown information, not an empty string or zero.

I define nullability according to the actual domain.

If a value is required for a valid record, the schema should usually enforce that requirement.

Dates and Time Zones

Dates and time are a frequent source of application bugs.

I pay attention to:

Date values should not be treated as arbitrary strings when the database can represent them semantically.

Concurrency

Databases may be accessed by many requests at once.

I design write operations with concurrency in mind.

Transactions, constraints and appropriate isolation help prevent issues such as:

Application code should not assume it is the only process using the database.

Connection Management

Server applications need to manage database connections efficiently.

Opening a completely new connection for every small operation without any pooling strategy can create unnecessary overhead.

Depending on the framework and deployment architecture, I use appropriate connection management and ensure connections are released correctly.

Backups

SQL databases often contain some of the most important state in an application.

I treat backups as part of production architecture.

A backup strategy needs to consider:

A backup that has never been tested for restoration is not enough by itself.

Logging and Debugging

When database behavior is unexpected, I investigate:

This helps distinguish between application logic errors and database performance problems.

I prefer measuring query behavior rather than guessing.

SQL in My Technology Stack

I commonly use SQL alongside technologies such as:

SQL provides the relational data layer connecting these applications with persistent structured storage.

Why I Use SQL

I use SQL because relational databases remain one of the most effective ways to store and work with structured application data.

Its strength is not only retrieving records.

SQL makes it possible to express relationships, enforce integrity, aggregate information, perform complex queries and keep important application state consistent.

For backend systems, mobile applications, desktop software and data-processing workflows, that makes SQL one of the fundamental technologies in my stack.