diff --git a/semester4/dmdb/assets/join-types.png b/semester4/dmdb/assets/join-types.png new file mode 100644 index 0000000..bcf7584 Binary files /dev/null and b/semester4/dmdb/assets/join-types.png differ diff --git a/semester4/dmdb/code/sql/aggregations.sql b/semester4/dmdb/code/sql/aggregations.sql new file mode 100644 index 0000000..a29daeb --- /dev/null +++ b/semester4/dmdb/code/sql/aggregations.sql @@ -0,0 +1,16 @@ +SELECT MAX(price) as MAX_PRICE FROM Products; -- Get the most expensive product +SELECT AVG(grade) as AVG_GRADE FROM Exams WHERE id = 0; -- Get the average grade for an exam + +-- Aggregation with grouping (Remember, only aggregates and cols in GROUP BY clause can +-- appear in SELECT clause!) +SELECT id, AVG(grade) as "Average Grade", COUNT(grade) as "Count" +FROM ExamResults GROUP BY id; -- Average grade for each exam and number of students taking it + +SELECT id, AVG(grade) as avg, COUNT(grade) as cnt FROM ExamResults +GROUP BY id HAVING cnt > 0 ORDER BY avg DESC LIMIT 20; + +-- Get exam details for all exams as filtered before +SELECT * FROM ( + SELECT id, AVG(grade) as avg, COUNT(grade) as cnt FROM ExamResults + GROUP BY id HAVING cnt > 0 ORDER BY avg DESC LIMIT 20 +) as filtered JOIN Exams e ON filtered.id = e.id; diff --git a/semester4/dmdb/data-modelling-databases-summary.pdf b/semester4/dmdb/data-modelling-databases-summary.pdf index 590d4cb..84539a0 100644 Binary files a/semester4/dmdb/data-modelling-databases-summary.pdf and b/semester4/dmdb/data-modelling-databases-summary.pdf differ diff --git a/semester4/dmdb/data-modelling-databases-summary.tex b/semester4/dmdb/data-modelling-databases-summary.tex index 439a533..3f590e6 100644 --- a/semester4/dmdb/data-modelling-databases-summary.tex +++ b/semester4/dmdb/data-modelling-databases-summary.tex @@ -22,28 +22,28 @@ \vspace{1cm} \begin{center} - \includegraphics[width=0.5\linewidth]{~/projects/latex/assets/logo.jpg} + \includegraphics[width=0.5\linewidth]{~/projects/latex/assets/logo.jpg} \end{center} \vspace{2cm} \begin{center} - \begin{Large} - \quote{A curry can be written in many different ways} - \end{Large} + \begin{Large} + \quote{A curry can be written in many different ways} + \end{Large} - \hspace{3cm} - Prof. Dr. Gustavo Alonso, 2026 + \hspace{3cm} - Prof. Dr. Gustavo Alonso, 2026 \end{center} \vspace{1.5cm} \begin{center} - FS2026, ETHZ + FS2026, ETHZ - \begin{Large} - Summary of the Lecture Slides - \end{Large} + \begin{Large} + Summary of the Lecture Slides + \end{Large} - \url{https://systems.ethz.ch/education/courses/2026-spring/dmdb.html} + \url{https://systems.ethz.ch/education/courses/2026-spring/dmdb.html} \end{center} \newpage diff --git a/semester4/dmdb/glossary/basics.tex b/semester4/dmdb/glossary/basics.tex index 72f6a22..94c43f2 100644 --- a/semester4/dmdb/glossary/basics.tex +++ b/semester4/dmdb/glossary/basics.tex @@ -1,16 +1,24 @@ \newglossaryentry{declarative}{ - name={declarative}, - description={Describes \textit{what} to achieve, but not how} + name={declarative}, + description={Describes \textit{what} to achieve, but not how} } \newglossaryentry{imperative}{ - name={imperative}, - description={Describes how to achieve something} + name={imperative}, + description={Describes how to achieve something} } \newglossaryentry{record}{ - name={record}, - description={} + name={record}, + description={} } \newglossaryentry{schema}{ - name={schema}, - description={A set of relations} + name={schema}, + description={A set of relations} +} +\newglossaryentry{bag}{ + name={bag}, + description={A bag, or multiset, is a set, in which, (as is usual with sets) order does not matter, but duplicates are allowed} +} +\newglossaryentry{subquery}{ + name={subquery}, + description={Also Nested Query, a separate query that can be used in the \texttt{FROM} or \texttt{WHERE} clause of another query. See \ref{sec:nested-queries}} } diff --git a/semester4/dmdb/parts/01_sql/01_ddl.tex b/semester4/dmdb/parts/01_sql/01_ddl.tex index 714b09a..edf169d 100644 --- a/semester4/dmdb/parts/01_sql/01_ddl.tex +++ b/semester4/dmdb/parts/01_sql/01_ddl.tex @@ -3,14 +3,18 @@ The DDL is used to describe the schema of relations, in \sql\ called tables. For that, we provide a table name, the columns and their data types. \sql\ supports the following data types (plus more): -\begin{multicols}{2} - \begin{itemize}[itemsep=-2pt] - \item \texttt{char(n)} - \item \texttt{varchar(n)} - \item \texttt{integer} - \item \texttt{blob} or \texttt{raw} (for large binaries) - \item \texttt{date} - \end{itemize} +\begin{multicols}{3} + \begin{itemize}[itemsep=-2pt] + \item \texttt{CHAR(n)} + \item \texttt{VARCHAR(n)} + \item \texttt{INT} + \item \texttt{BIGINT} + \item \texttt{SMALLINT} + \item \texttt{FLOAT} + \item \texttt{BLOB(n)} (for large binaries) + \item \texttt{DATETIME} + \item \texttt{MONEY} + \end{itemize} \end{multicols} @@ -18,7 +22,8 @@ For that, we provide a table name, the columns and their data types. Use the DDL \texttt{CREATE TABLE} keyword to create a table. We can set default values for certain attributes, or can add constraints to them. -% TODO: Constraints (such as NOT NULL) +Since the primary key must be unique for each entry, it may be useful to configure the primary key attribute with the following constraints +\texttt{PrimaryKeyField INT NOT NULL AUTO\_INCREMENT} \inputcodewithfilename{sql}{}{code/sql/ddl/create.sql} @@ -34,3 +39,28 @@ Be aware that if we ADD a column to the schema, unless a default value is provid \subsubsection{Deleting Tables} \inputcodewithfilename{sql}{}{code/sql/ddl/delete.sql} + + +\subsubsection{Constraints} +\sql\ supports a number of constraints that can be put on the different attributes of a schema. +\begin{itemize} + \item \texttt{NOT NULL}: Disallows this column to be set to \texttt{NULL}, or have a \texttt{NULL} value + \item \texttt{UNIQUE}: The value of each value in this column must be unique, but it can be \texttt{NULL}. + An arbitrary number of columns can have \texttt{NULL} and still be valid + \item \texttt{PRIMARY KEY}: Sets this column as the primary key. \texttt{NOT NULL} and \texttt{UNIQUE} is set on it implicitly. + Contrary to common intuition, it can be set on multiple columns. + \item \texttt{CHECK c}: Check that the values fulfil a condition. + This condition has the same syntax as for the \texttt{WHERE} clause (see \ref{sec:sql-basic-ops}) + \item \texttt{REFERENCES table}: A Foreign Key, used to refer to another tuple from a different relation. + It is typically referencing the primary key of the other table. +\end{itemize} +For \texttt{REFERENCES}, there are many options for maintenance, which can be set on creating a reference: +\begin{itemize} + \item \bi{Cascade}: Propagate \texttt{UPDATE} and \texttt{DELETE} + \item \bi{Restrict}: Prevent deletion of the primary key before the change, causes error + \item \bi{No Action}: Prevent modifications after attempting change, causes error + \item \bi{Set default, Set Default}: Set references to \texttt{NULL} or default value +\end{itemize} + +\inlineexample +\mint{sql}|CREATE TABLE tab (id integer REFERENCES OtherTable ON DELETE cascade ON UPDATE cascade)| diff --git a/semester4/dmdb/parts/01_sql/03_query-language/00_intro.tex b/semester4/dmdb/parts/01_sql/03_query-language/00_intro.tex index 0f2d228..bd1b3ea 100644 --- a/semester4/dmdb/parts/01_sql/03_query-language/00_intro.tex +++ b/semester4/dmdb/parts/01_sql/03_query-language/00_intro.tex @@ -7,16 +7,23 @@ To rename, we can set \texttt{I = Column as Name, Column2 as Name2}, etc % TODO: Make RA an acronym, too \inlinetheorem Every SPJR RA expression can be written in \texttt{SELECT ... FROM ... WHERE ...} form: -\begin{tables}{lll}{Operation & Notation & SQL} - Selection & $\sigma_c(R)$ & \texttt{SELECT * FROM R WHERE c;} \\ - Projection & $\Pi_{A_1, \ldots, A_n} R$ & \texttt{SELECT A1, \ldots, An FROM R;} \\ - Cartesian Product & $R_1 \times R_2$ & \texttt{SELECT * FROM (R1, R2);} \\ - Rename & $\rho_{a, b, c} R$ & \texttt{SELECT A as a, \ldots, B as c FROM R;} \\ - Union & $R_1 \cup R_2$ & \texttt{R1 UNION R2;} \\ - Difference & $R_1 - R_2$ & \texttt{R1 EXCEPT R2;} \\ - Intersection & $R_1 - R_2$ & \texttt{R1 INTERSECT R2;} \\ +\begin{tables}{lll}{Operation & Notation & SQL} + Selection & $\sigma_c(R)$ & \texttt{SELECT * FROM R WHERE c;} \\ + Projection & $\Pi_{A_1, \ldots, A_n} R$ & \texttt{SELECT A1, \ldots, An FROM R;} \\ + Cartesian Product & $R_1 \times R_2$ & \texttt{SELECT * FROM (R1, R2);} \\ + Rename & $\rho_{a, b, c} R$ & \texttt{SELECT A as a, \ldots, B as c FROM R;} \\ + Union & $R_1 \cup R_2$ & \texttt{R1 UNION R2;} \\ + Difference & $R_1 - R_2$ & \texttt{R1 EXCEPT R2;} \\ + Intersection & $R_1 - R_2$ & \texttt{R1 INTERSECT R2;} \\ \end{tables} -Since \sql\ implements bag and not set semantics, there is a \texttt{DISTINCT} keyword, which is used to remove duplicates. -For the last three operations, \texttt{R1} and \texttt{R2} typically are other \sql\ statements: +Since \sql\ implements \gls{bag} and not set semantics, there is a \texttt{DISTINCT} keyword, which is used to remove duplicates. +It is to be applied in the \texttt{SELECT} clause (\texttt{I} above), e.g. +\mint{sql}|SELECT DISTINCT name FROM Data;| + +For the last three operations, \texttt{R1} and \texttt{R2} typically are other \sql\ statements, example: \mint{sql}|(SELECT name FROM FirstTable) UNION (SELECT name FROM SecondTable)| +In addition, to apply \textit{bag semantics}, as opposed to \textit{set semantics} append \texttt{ALL} to the expression (e.g. \texttt{UNION ALL}) + +We can also name the tables, e.g. $\texttt{T} = \texttt{One o, Two t}$, and then access columns from a specific table using +$\texttt{I} = \texttt{o.Col, b.Col}$, or the like. diff --git a/semester4/dmdb/parts/01_sql/03_query-language/01_basic-operators.tex b/semester4/dmdb/parts/01_sql/03_query-language/01_basic-operators.tex new file mode 100644 index 0000000..e833c50 --- /dev/null +++ b/semester4/dmdb/parts/01_sql/03_query-language/01_basic-operators.tex @@ -0,0 +1,33 @@ +\newpage +\subsubsection{Basic operators} +\label{sec:sql-basic-ops} +\paragraph{Logical Operators} +The following \sql\ logical operators are available (may not be exhaustive for all systems, \texttt{cop} is a comparison operator): +\begin{tables}{p{4.5cm}p{12cm}}{Operator & Description} + \texttt{P1 AND P2} & TRUE if \texttt{P1} and \texttt{P2} both are true \\ + \texttt{P1 OR P2} & TRUE if one of \texttt{P1} or \texttt{P2}, or both are true \\ + \texttt{C cop ALL query} & TRUE if all values \texttt{x} returned in \gls{subquery}\ \texttt{query} fulfil \texttt{C cop x} \\ + \texttt{C cop ANY query} & TRUE if one (or more) values \texttt{x} returned in \gls{subquery}\ \texttt{query} fulfil \texttt{C cop x}. + \texttt{SOME} is logically equivalent \\ + \texttt{C BETWEEN low AND high} & TRUE if numerical, text or date value of \texttt{C} is between \texttt{low} and \texttt{high}. Both ends inclusive, negatable \\ + \texttt{C IN list} & TRUE if value of \texttt{C} is in the provided \texttt{list}. + List format: \texttt{(val, val2)}. List can also be a \gls{subquery}, negatable \\ + \texttt{C LIKE R} & TRUE if value of \texttt{C} matches regex-like pattern \texttt{R}. + (\% matches any number of chars, \_ matches a single, arbitray character) \\ + \texttt{EXISTS query} & TRUE if \gls{subquery}\ \texttt{query} retuns at least one row \\ + % \texttt{IN} & TRUE if \\ +\end{tables} + + +\paragraph{Comparison \& Arithmetic Operators} +\shade{gray}{Comparison Operators} $<, <=, =, >=, >$ are defined as usually, $<>$ is defined as not equal. + +\shade{gray}{Arithmetic Operators} $+, -, *, /, \%$ are defined as usual. + +We can use the arithmetic operators like this: +\mint{sql}|SELECT Value * 1.1 FROM Data;| +or in \texttt{WHERE} clauses like this: +\mint{sql}|SELECT Value FROM (Data a, Data b) WHERE a.Value = b.Value - 1;| + +\inlineremark You may have noticed that in the above query, the same table was used twice. +This is referred to as a \bi{Self-Join} and can come in handy when comparing values in a single table. diff --git a/semester4/dmdb/parts/01_sql/03_query-language/02_join.tex b/semester4/dmdb/parts/01_sql/03_query-language/02_join.tex new file mode 100644 index 0000000..a330283 --- /dev/null +++ b/semester4/dmdb/parts/01_sql/03_query-language/02_join.tex @@ -0,0 +1,39 @@ +\newpage +\subsubsection{Join} +Specifying two (or more) tables in the \texttt{FROM} clause will perform a cartesian product, e.g. +\mint{sql}|SELECT * FROM (Table1, Table2);| +returns all rows of the tables, next to each other. + +Here, naming the tables in the \texttt{FROM} clause may come in handy, to not write out the entire name of the tables: +\mint{sql}|SELECT * FROM (Table1 t, Table2 s) WHERE t.id = s.id;| +\mint{sql}|SELECT * FROM (Table1, Table2) WHERE Table1.id = Table2.id;| +Both of the above statements produce the same result. + + +\paragraph{Nested Queries} +\label{sec:nested-queries} +To not have the cartesian product executed in the \texttt{FROM} clause, or to get a much more complex starting table, we can use a \gls{subquery}. +These are parenthesized normal SQL queries. + +We can create an alias for them using an \texttt{AS name} clause after the parenthesis as follows +\mint{sql}|SELECT r.name FROM (SELECT * FROM Table) AS r;| +or alternatively, using a \texttt{WITH} clause (replace the dummy queries with real queries): +\mint{sql}|WITH CteName (SELECT * FROM Table) SELECT name FROM CteName;| + + +\paragraph{Join Operations} +If we don't specify the kind of join, an \texttt{INNER JOIN} is executed. The following three queries are equivalent +\mint{sql}|SELECT * FROM Student, Tests WHERE Student.PersNr = Tests.PersNr;| +\mint{sql}|SELECT * FROM Student JOIN Tests ON Student.PersNr = Tests.PersNr;| +\mint{sql}|SELECT * FROM Student JOIN Tests USING (PersNr);| +For the latter of the three, the parenthesis around the column are important, as omitting them is invalid syntax. + +Other types of \texttt{JOIN} are: +\begin{itemize} + \item \texttt{NATURAL JOIN} (no \texttt{ON} required), joins on columns with same name + \item \texttt{LEFT/RIGHT/FULL OUTER JOIN} (requires \texttt{ON}), also returns non-matching rows from left, right or both sides, respectively +\end{itemize} + +\begin{center} + \includegraphics[width=0.5\textwidth]{assets/join-types.png} +\end{center} diff --git a/semester4/dmdb/parts/01_sql/03_query-language/03_aggregation.tex b/semester4/dmdb/parts/01_sql/03_query-language/03_aggregation.tex new file mode 100644 index 0000000..30d1f45 --- /dev/null +++ b/semester4/dmdb/parts/01_sql/03_query-language/03_aggregation.tex @@ -0,0 +1,41 @@ +\newpage +\subsubsection{Aggregations, Grouping, Sorting} +\paragraph{Aggregations} +\sql\ supports (at least) the aggregation functions \texttt{SUM}, \texttt{MIN}, \texttt{MAX}, \texttt{AVG}, \texttt{COUNT}, +whose output should be evident. + +Please note that you cannot use the aggregation functions and still output another column, +as they produce a single value. + +For each of the functions, you can optionally specify \texttt{DISTINCT}. + +We typically want to specify an \texttt{AS} clause, to make the output nicer to read + + +\paragraph{Grouping} +Grouping is used to, well, group together the outputs on a column. +Using SQL, this is achieved using the \texttt{GROUP BY} clause. +We specify the columns on which the grouping should occur. + +\hl{\textbf{\textit{IMPORTANT}}} We can only use aggregates and columns appearing in the \texttt{GROUP BY} clause in the \texttt{SELECT} clause. + +Using a \texttt{JOIN} function, it is of course then possible to go back retrieve the other columns. + +To apply filtering on the columns, the \texttt{HAVING} clause can be used. +It applies a comparison to a grouping column or an aggregate as specified in the \texttt{SELECT} clause: +\mint{sql}|SELECT COUNT(name) as cnt FROM Students GROUP BY Department HAVING cnt > 1;| + + +\paragraph{Sorting} +We can sort the output of any query using the \texttt{ORDER BY column DIRECTION} clause, where \texttt{DIRECTION} is to be replaced with \texttt{ASC} or \texttt{DESC}: +\mint{sql}|SELECT name FROM Students ORDER BY name DESC;| + + +\paragraph{Limit} +We can limit the number of columns to return using the \texttt{LIMIT} clause. Example: +\mint{sql}|SELECT * FROM Exams LIMIT 10;| + + +\paragraph{Example} +Below some examples of \sql\ Queries using grouping, aggregations, sorting and grouping +\inputcodewithfilename{sql}{}{code/sql/aggregations.sql} diff --git a/semester4/dmdb/parts/01_sql/03_query-language/04_nulls.tex b/semester4/dmdb/parts/01_sql/03_query-language/04_nulls.tex new file mode 100644 index 0000000..fb564d7 --- /dev/null +++ b/semester4/dmdb/parts/01_sql/03_query-language/04_nulls.tex @@ -0,0 +1,21 @@ +\newpage +\subsubsection{Null} +\label{sec:sql-null} +Whenever we have incomplete information, we can set a column to be \texttt{NULL}. +If you have a column that has \texttt{NULL} values, when using \texttt{WHERE} clauses, these values are skipped. +\textit{However} if you are not filtering on the column, a \texttt{NULL} value is returned. + +To check if a column is \texttt{NULL}, you can use the \texttt{IS} keyword: +\mint{sql}|SELECT * FROM Data WHERE name IS NULL| + +\begin{itemize}[itemsep=-3pt] + \item In \texttt{GROUP BY}, all \texttt{NULL}s form a single group. + \item \texttt{COUNT(*)}, also counts rows with \texttt{NULL} + \item \texttt{COUNT(TheColWithNull)} ignores rows with \texttt{NULL} + \item Most other aggregations ignore \texttt{NULL} + \item If all rows in an aggregations are \texttt{NULL}, so is the result +\end{itemize} + +The \texttt{OUTER JOIN} operators may (and likely will) introduce \texttt{NULL} + +% TODO: Schema concepts? (slides 05, P20ff) diff --git a/semester4/dmdb/parts/01_sql/03_query-language/05_views.tex b/semester4/dmdb/parts/01_sql/03_query-language/05_views.tex new file mode 100644 index 0000000..eb23880 --- /dev/null +++ b/semester4/dmdb/parts/01_sql/03_query-language/05_views.tex @@ -0,0 +1,23 @@ +\subsubsection{Views} +Views are a useful way of abstracting a complex query and is the result of a stored query. + +We can create views using the following template: +\mint{sql}|CREATE VIEW name AS query;| + +We can then operate on it as if it were a table. + +Common applications for views are privacy / access control, because you can limit access to tables only via certain views for every user; +Usability is also often much better due to having less complex queries to write - you are just writing them once. + +\shade{orange}{How a view is evaluated} +This is quite simple in essence, the \texttt{VIEW}'s query is inserted into the query using the view. +It then is then optimized as it normally would be, too. Of course, it is likely that more optimization can and has to be done on views. + + +\paragraph{Updatable Views} +Updating Views refers to updating values of the tables in the views. +This is not straight forward and thus, many restrictions are placed on what kind of views are considered updatable. + +In \sql, a view is updatable if and only if it involves \bi{one} base relation and its key, it does \bi{not} involve aggregates, groups or duplicate elimination. + +In short, if there is one-to-one mapping between the view and base relation, it is updatable. diff --git a/semester4/dmdb/parts/01_sql/main.tex b/semester4/dmdb/parts/01_sql/main.tex index d67d343..44e44ef 100644 --- a/semester4/dmdb/parts/01_sql/main.tex +++ b/semester4/dmdb/parts/01_sql/main.tex @@ -4,6 +4,12 @@ \input{parts/01_sql/01_ddl.tex} \input{parts/01_sql/02_dml.tex} \input{parts/01_sql/03_query-language/00_intro.tex} +\input{parts/01_sql/03_query-language/01_basic-operators.tex} +\input{parts/01_sql/03_query-language/02_join.tex} +\input{parts/01_sql/03_query-language/03_aggregation.tex} +\input{parts/01_sql/03_query-language/04_nulls.tex} +\input{parts/01_sql/03_query-language/05_views.tex} +% \input{parts/01_sql/03_query-language/} -\input{parts/01_sql/01_operations.tex} +% \input{parts/01_sql/01_operations.tex} % \input{parts/01_sql/}