Home / Software Development / SQL Explained Simply
Databases

SQL Explained Simply

A query can return the same total twice without the database storing duplicate payments. Where did the extra rows come from?

SQL is the language applications use to ask precise questions of relational data and make controlled changes. A useful database is more than a spreadsheet: keys connect records, constraints block impossible values, transactions keep multi-step work consistent and indexes help large searches remain fast. This guide follows real banking, school, booking and shopping questions while explaining why correct results and safe concurrency matter as much as writing a query.

7 min read
SQL Explained Simply — original ExplainItSimply topic illustration

By

A join can repeat a payment row when it matches several related records. Summing after that join can therefore multiply the amount.

Check the relationship and row counts before aggregating. A payment linked to three guest rows is still one payment, unless the business rule explicitly allocates it.

One reservation became six rows. Where did the extra records come from?

A booking has two guests and three benefits. Joining both child lists directly to the booking can produce six combinations. The database has not created six bookings; the query asked for matching pairs from both relationships.

We will count those combinations before calculating a total. No SQL experience is needed to follow the table. The aim is to recognise the grain of a result: what one row actually represents.

Why SQL is powerful

Most serious systems need databases. Banks, hospitals, shops, schools and travel websites all store data. SQL helps developers work with that data accurately.

Why can joining two tables make a total too large?

A join operates on matching row pairs.

Trace the explanation

If a booking matches three related records, its amount can appear three times before aggregation. Decide the intended grain and aggregate or filter related data accordingly; DISTINCT is not a universal repair.

Source and further reading: Microsoft Learn

One booking can turn into three rows without three bookings existing

A join between a booking table and a benefits table returns one row for each matching combination. If a booking has three benefits, its booking amount can appear three times. Summing the joined amount then triples the total even though the join is behaving as instructed.

Decide the grain of the answer first: one row per booking, per benefit or per reservation? Aggregate the child data at the needed level, or test existence when you only need to know whether a match exists. DISTINCT may hide duplicated rows without correcting a duplicated calculation.

One booking; 2 guests; 3 benefits to 6 rows repeat one booking amount1: One booking; 2 guests; 3 benefits. 2: Join yields 2 × 3 matching pairs. 3: 6 rows repeat one booking amount.1One booking; 2 guests; 3benefits2Join yields 2 × 3 matchingpairs36 rows repeat one bookingamount

Count the matching combinations before you sum money

Guest A matches benefits 1, 2 and 3. Guest B also matches benefits 1, 2 and 3. That gives 2 × 3 = 6 result rows, each of which can repeat the booking's amount.

If the booking total is R900, summing that repeated column produces R5,400. That is an incorrect answer to 'what is the booking total?', even though the join is doing what was requested.

Decide the intended output first. To show one row per booking, aggregate each child relationship at the booking level before combining it, or use an existence test when you only need a yes/no match. DISTINCT is not a universal repair for an incorrect calculation.

Count the matching combinations before you sum money — worked evidence
GuestMatching benefit rowsBooking total repeated
A1, 2, 3R900 on each row
B1, 2, 3R900 on each row
All combinations6 rowsIncorrect naive sum: R5,400

A different booking has four guests and two benefits. How many combinations can the same join produce?

This checks a prediction about the worked example. It does not run server code, contact an API or change a booking.

Read the explanation without the checker

8: each of four guests can match each of two benefits. That does not mean there are eight bookings.

Before the next SUM, finish the sentence 'one result row means…'. Then check whether the amount being summed belongs at that level.

Practical examples: SQL

Bank

A banking query can retrieve the transactions belonging to one authorised account and calculate a balance from controlled records. Transfers use transactions so the debit and credit succeed together; saving only half of the operation would create money errors.

School

A school database links learners, subjects, classes, assessments and marks using keys. SQL can produce one report without copying all that information into one enormous table, while constraints prevent a mark from being attached to a learner or assessment that does not exist.

Booking

A booking search filters dates, resort, occupancy and status, but confirmation must recheck availability. Transactions and locking protect the final room from being sold twice when requests arrive together.

Online

An online shop joins products, prices, stock and orders to answer different questions. Indexes help find relevant rows quickly, while audit fields preserve when records changed. Performance must be measured as data grows; a query that is fast with one hundred rows may struggle with millions.

Further context: Count the matching combinations before you sum money

Why it matters in practice

Good SQL is not only about getting the right rows. Developers also think about indexes, concurrency, transactions and parameterised queries. A query that works instantly with 100 rows may behave very differently with 50 million rows, and concatenating user input into SQL can create a serious injection vulnerability.

A detail worth getting right

SQL databases are not just giant spreadsheets. A relational database can enforce keys, relationships, constraints and transactions while many users are reading and writing at the same time. That combination is why they remain central to banking, booking, billing and business systems.

A R900 booking joins to 3 matching notes and its amount is summed on each row. What incorrect total results?

Trace the explanation

900 × 3 = R2700. The correct booking value remains R900. The query counted duplicated representations of the booking, not three independent bookings.

Worked relationship: 900 × 3 = R2700. 900: booking value; 3: joined copies; 2700: inflated total

Source and further reading: Microsoft Learn

Why can two users both reserve the last room after checking availability?

Each can read 'one left' before either saves. A separate read followed by a write leaves a race between competing requests.

The final decision needs appropriate transactional protection in the database. A screen check is useful feedback, but it cannot be the sole guarantee against overbooking.

Why the same query can be fast today and slow next year

Database performance changes with scale. A query that scans 500 booking rows may feel instant, while the same approach against tens of millions of transaction rows can consume large amounts of CPU and disk I/O. An execution plan shows how the database intends to find and combine the data. Useful indexes can reduce the amount of work, but too many indexes slow inserts and updates, so tuning is a trade-off instead of a rule to index every column.

Concurrency is another part users rarely see. Two people can try to reserve the same scarce item at nearly the same moment. Transactions, constraints and careful update logic are used to make sure the database ends in a valid state even when requests overlap. This is one reason important business rules should be enforced close to the data as well as in friendly user-interface checks.

A precise question can still need a better access path

A database can scan many rows to find a few matches, or use a suitable index to narrow the work. An index costs storage and maintenance during writes, so indexing every column is not a free performance switch.

Compare a realistic query's execution plan and data volume rather than assuming 'SQL is slow'. Keep parameters separate from SQL text to avoid treating user input as executable syntax. Also test date boundaries: an end-of-day timestamp is not the same as a date-only comparison, and null needs an explicit meaning.

If a table has 10 bookings and each has two guests, must a booking-to-guest join return 10 rows?

No. With two matches per booking it can return 20 rows. The result describes matching pairs; counting bookings requires the correct grain or a distinct booking-key count.

Connect database questions to the application that is allowed to ask them

A browser should not connect directly to production tables. Follow the API boundary and C# server rules to see how trusted code validates a request before SQL runs. Then use the production-project guide to understand transactions, monitoring and recovery around the database change.

Questions about SQL Explained Simply

Is SQL programming?

SQL is a query language. It is used to ask databases for information and make changes.

Is SQL still useful?

Yes. SQL remains one of the most important skills in software development and data work.

What is a JOIN?

A join combines related rows from two or more tables using matching values, such as connecting a Booking row to the Resort row referenced by its ResortKey.

What is an index?

An index is an additional data structure that helps the database find certain rows without scanning every row. It speeds some reads but also takes storage and adds work when data changes.

Why use a transaction?

A transaction groups changes that belong together. If one required step fails, the database can roll the whole group back instead of leaving half-completed data.

What is SQL injection?

It happens when untrusted input is allowed to become part of SQL code. Parameterised queries separate data from commands and are a fundamental defence.

How would you investigate a report that changes totals when a comments table is added?

One reasoned response

Count rows per booking before and after the join. Inspect key uniqueness and one-to-many relationships. Use EXISTS for a presence test when the related rows themselves should not multiply the result.

Source and further reading: Microsoft Learn

Example: retrieve only the rows you need

SQL lets an application ask a relational database for specific information. A query should name the columns and conditions clearly so the result matches the question being asked.

SELECT BookingKey, CheckInDate, CheckOutDate
FROM Bookings
WHERE ParticipantKey = @ParticipantKey
  AND CheckInDate >= @StartDate
ORDER BY CheckInDate;

The parameters beginning with @ are placeholders supplied by the application, rather than text concatenated into the SQL statement. Parameterised queries help separate data values from SQL instructions and reduce injection risk. The table and column names here are illustrative; use the schema in your own system. Also consider permissions, indexes, date boundaries and whether the query should include cancelled bookings. A query that runs successfully is not necessarily the correct query for the business rule.

Sources

Sources and further reading

A closer look

Two one-to-many joins can multiply totals

One booking has two guests and three payments. Joining the booking directly to both child tables produces six combinations. Summing the payment amount after that join counts each payment twice.

Work through the distinction

Aggregate payments to one row per booking before joining them to guest details, or keep the queries separate. Test the row count and the intended level of detail before trusting the total. DISTINCT on the amount is not a safe repair when two real payments have equal amounts.

Two guests × three payments = six joined rows: Each real payment appears twice. Aggregate at the intended level before summing.

How many guest-payment combinations are produced?

Choose an answer to see feedback. You can try again.

Show the answer and explanation

Six. Aggregate payments to one row per booking before joining them to guest details, or keep the queries separate. Test the row count and the intended level of detail before trusting the total. DISTINCT on the amount is not a safe repair when two real payments have equal amounts.

Help us make learning clearer

Was this explanation useful?

Your rating helps us understand which explanations are working and where we can improve.

—/ 5·0 ratings
Community

Join the conversation

Add your perspective, ask a useful question or share what stood out.

0 comments
◌ No comments yet

This conversation is waiting for its first useful thought.