Relational Databases are made of tables Show
CellIntersection of the rows and columnsColumnRepresents some sort of entity, like the amount of an invoiceComposite primary keyWhen a primary key uses two or more columnsConstraintWhen assigning column attributes, a _constraint_ restricts the type of data
that can be stored in a column. For example, NOT NULL and UNIQUE.Data typeDetermines the type of information that is stored in the column
Created automatically for a tables primary and non-primary keysNon-primary keyUniquely identifies each row in the table.Primary keyUniqueily identifies each row in the table. Usually a single column, but can be more than 1 columnReferential integrityMakes sure that any changes to the data in the db do not create invalid relationships between tables.RowContains a set of values for a single instance of the entity Unique keyMySQL specific, not all dbs let you define one.NullValue that is unknown, unavailable, or not applicable.Default valueValue that is assigned to the column if another value is not providedAuto increment columnValue is generated automatically by the DBMSEntity-relationship (ER) or enhanced entity-relationship (EER) diagramUsed to show how the tables in a database are defined and relatedResult set/tablethe data that is returned by a queryDML (Data Manipulation Language) Statements tha work with the data in a db
Statements that create databases and work with the objects within a db (usually used by db admins)
Anything that is created from a CREATE command is a db object, including:
Building queriesBest practice to build queries one clause at a time. StatementsSELECTNames the columns to be retrievedINSERT INTONames the columns whose values are supplied in the VALUES clauseVALUESLists the data that is inserted with an INSERT clauseUPDATEChanges the data in one or more rows of a tableDELETEDeletes one or more rows from a tableFROMNames the base table from which the query retrieves the dataASAssigns a new name to a group of columns. The new name is the column alias.Called a calculated value because it exists only in this query When there is a space in the alias name, enclose the alias name in quotes. For example, 'Test Row'WHEREFilters the rows in the base table by the boolean expression that takes a true, false, or NULL value. For example, WHERE value > 0. If you omit the WHERE clause, all the rows in the base table are returned.INUsed in WHERE clause. Compares the value of the test expression with the list of expressions in the IN phrase. If the test expression is equal to one of the expressions in the list, the row is included in the query results and each of the expressions in the list is converted to the same thpe of data as the test expression automatically. Example: WHERE terms_id IN (1, 2, 3); WHERE vendor_state NOT IN ('CA', 'NV', 'OR'); BETWEEN - used in WHERE clause. Compares the value of the test expression to the range of values specified in the BETWEEN phrase. If the value falls within this range, it is included in the results. You can use the NOT operator with this.
WHERE invoice BETWEEN '2018-06-01' AND '2018-06-30'; WHERE vendor_zip_code NOT BETWEEN 93600 AND 93799;LIKE or REGEXPUsed in the **WHERE** clause. Use to retrieve rows that match a specific string pattern or mask. LIKE Wildcards:
REGEXP special characters/constructs
Example **** or **** - :
SELECTSyntaxSELECT select_list [FROM table_source] [WHERE search_condition] [ORDER BY order_by_list] [LIMIT row_limit] TestingUse SELECT clauses without a FROM clause to test expressions and functions. SELECT CURRENT_DATE, DATE_FORMAT(CURRENT_DATE, '%m/%d/%y') AS 'MM/DD/YY', DATE_FORMAT(CURRENT_DATE, '%e-%b-%Y') AS 'DD-Mon-YYYY'; SELECT 12345.6789 AS value, ROUND(12345.6789) AS nearest_dollar, ROUND(12345.6789, 1) AS nearest_dime;
Column specificationsBase table value All columns = * Column name = column_name Calculated value Result of a calculation = arithmetic expression Result of a function = functions (CONCAT, etc.) Arithmetic operators
Logical operatorsAND and OR - combine two or more search conditions in to a compound condition Example: Example:
FunctionsFunctions are used in the SELECT clause CONCAT - used to join strings.
SELECT vendor_name, CONCAT(vendor_city, ', ', vendor_state, ' ', vendor_zip_code) AS address FROM vendors;
SELECT vendor_contact_first_name, vendor_contact_last_name, CONCAT(LEFT(vendor_contact_first_name, 1), LEFT(vendor_contact_last_name, 1)) AS initials FROM vendors;
For example, '%m/%d/%y' returns 04/08/18
JOINSRetrives data from two tables and joins it together in a single result set
SELECT vendor_name, invoice_number, invoice_date, invoice_total (1) FROM vendors INNER JOIN invoices (2) ON vendors.vendor_id = invoices.vendor_id (3) WHERE invoice_total >= 500 (4) ORDER BY vendor_name, invoice_total DESC; (5)
Retrieve data from multiple tablesINNER JOINCombines columns from 2 or more tables into a result set based on the join conditions For inner joins, only the two rows that satisfy the join condition are included in the result set How to code inner join:Code the names of the 2 tables in the FROM clause along with the JOIN keyword and an ON phrase that specifies the join condition SELECT invoice_number, vendor_name (1) FROM vendors INNER JOIN invoices (2) ON vendors.vendor_id = invoices.vendor_id (3) ORDER BY invoice_number; (4)
Implicit syntaxNot used often, prefer Explicit syntax outlined above. Code the tables in the SELECT invoice_number, vendor_name FROM vendors v, invoices i (1) WHERE v.vendor_id = i.vendor_id ORDER BY invoice_number;
Table aliasesAn alternative table name thats typically just a letter or two. Like a variable. SELECT invoice_number, vendor_name, invoice_due_date, invoice_total - payment_total - credit_total AS balance_due FROM vendors v JOIN invoices i (1) ON v.vendor_id = i.vendor_id (2) WHERE invoice_total - payment_total - credit_total > 0 ORDER BY invoice_due_date DESC;
JOIN to a table in another dbTables are organized into databases, which are also called schemas SELECT vendor_name, customer_last_name, customer_first_name, vendor_state AS state, vendor_city AS city FROM vendors v JOIN om.customers c (1) ON v.vendor_zip_code = c.customer_zip ORDER BY state, city;
Compound JOINCreate two or more comparisons in a join condition using the AND or OR operators SELECT customer_first_name, customer_last_name FROM customers c JOIN employees e ON c.customer_first_name = e.first_name AND c.customer_last_name = e.last_name; (1)
Self JOINJoins a table to itself. Useful for retrieving data that can't be retrieved any other way. SELECT DISTINCT v1.vendor_name, v1.vendor_city, v1.vendor_state FROM vendors v1 JOIN vendors v2 (1) ON v1.vendor_city = v2.vendor_city AND (2) v1.vendor_state = v2.vendor_state AND v1.vendor_name = v2.vendor_name ORDER BY v1.vendor_state, v1.vendor_city;
JOIN more than two tablesThis example joins tables based on the relationship between the primary key of one table and a foreign key of the other table. SELECT vendor_name, invoice_number, invoice_date, line_item_amount, account_description FROM vendors v JOIN invoices i ON v.vendor_id = i.vendor_id JOIN invoice_line_items li ON i.invoice_id = li.invoice_id JOIN general_ledger_accounts gl ON li.account_number = gl.account_number WHERE invoice_total - payment_total - credit_total > 0 ORDER BY vendor_name, line_item_amount DESC; OUTER JOINSReturns all of the rows from one of the tables involved in the join, plus unmatched rows in the LEFT or RIGHT table, regardless of whether the join condition is true LEFT or RIGHT keyword
The following joins the vendors and invoices tables, and includes rows from the vendor column even if no matching invoices are found. If none are found, null vbalues are returned for those columns. SELECT vendor_name, invoice_number, invoice_total FROM vendors LEFT JOIN invoices ON vendors.vendor_id = invoices.vendor_id ORDER BY vendor_name; Combination of SELECT department_name, last_name, project_number FROM departments d JOIN employees e ON d.department_number = e.department_number LEFT JOIN projects p ON e.employee_id = p.employee_id ORDER BY department_name, last_name; USING keywordUse the USING clause instead of an ON clause during an equijoin to specify the join. To join tables by multiple columns, put the multiple columns in the parentheses, separated by a column equijoin/equi-join - When you use the equal operator to join two tables on a common column. Common for the columns that are being compared to have the same name.
SELECT invoice_number, vendor_name FROM vendors JOIN invoices USING (vendor_id) (1) ORDER BY invoice_number; (1) USING (vendor_id) replaced 'ON vendors.vendor_id = invoices.vendor_id'
SELECT department_name, last_name, project_number FROM departments JOIN employees USING (department_number) (1) LEFT JOIN projects USING (employee_id) (2) ORDER BY department_name; (1) INNER JOIN between departments and employees on the department_number column (2) LEFT JOIN between employees and projects using the employee_id column NATURAL keywordYou don't specific the column that's used to join the two tables. The db joins the two tables based on all columns in the two tables that have the same name.
SELECT department_name AS dept_name, last_name, project_number FROM departments NATURAL JOIN employees (1) LEFT JOIN projects USING (employee_id) (2) ORDER BY department_name; (1) JOINs departments and employees on columns where they intersect (2) Creates a LEFT JOIN between employees and projects on employee_id column CROSS JOINsProduces a result set that includes each row from the first table joined with each row from the second table. Result set is called a Cartesian product
SELECT departments.department_number, department_name, employee_id, last_name FROM departments CROSS JOIN employees (1) ORDER BY departments.department_number; (1) JOIN the 4 columns together in a result set from left to right, not based on where they intersect UNIONsUsed to connect two or more SELECT statements. The result set of each SELECT statement must have the same number of columns, and the data types of the corresponding columns in each table must be compatible. Combines data from two or more result sets, instead of base tables. To sort the result of a UNION, add an ORDER BY cluase after the last SELECT statement. By default, UNIONs eliminates duplicate rows. To include dupes, add ALL keyword. Column names in the final result set are taken from the first SELECT clause.
SELECT invoice_number, vendor_name, '33% Payment' AS payment_type, invoice_total AS total, invoice_total * 0.333 AS payment FROM invoices JOIN vendors ON invoices.vendor_id = vendors.vendor_id WHERE invoice_total > 10000 UNION SELECT invoice_number, vendor_name, '50% Payment' AS payment_type, invoice_total AS total, invoice_total * 0.5 AS payment FROM invoices JOIN vendors ON invoices.vendor_id = vendors.vendor_id WHERE invoice_total BETWEEN 500 AND 10000 UNION SELECT invoice_number, vendor_name, 'Full amount' AS payment_type, invoice_total AS total, invoice_total AS payment FROM invoices JOIN vendors ON invoices.vendor_id = vendors.vendor_id WHERE invoice_total < 500 ORDER BY payment_type, vendor_name, invoice_number;
SELECT 'Active' AS source, invoice_number, invoice_date, invoice_total (1) FROM invoices WHERE invoice_total - payment_total - credit_total > 0 UNION SELECT 'Paid' AS source, invoice_number, invoice_date, invoice_total (2) FROM invoices WHERE invoice_total - payment_total - credit_total <= 0 ORDER BY invoice_total DESC; (1) Rows that have a balance due. Adds column named 'source' to indicate that each row is 'Active' (2) Rows that are paid in full. Adds column named 'source' to indicate that each row is 'Paid' Simulate a FULL OUTER JOIN with a UNIONSimulate a FULL OUTER JOIN by creating a UNION that combines the result sets for a left outer join and a right outer join. NULL is displayed in rows with no values
SELECT department_name AS dept_name, d.department_number AS d_dept_no, e.department_number AS e_dept_no, last_name FROM departments d LEFT JOIN employees e ON d.department_number = e.department_number UNION SELECT department_name AS dept_name, d.department_number AS d_dept_no, e.department_number AS e_dept_no, last_name FROM departments d RIGHT JOIN employees e ON d.department_number = e.department_number ORDER BY dept_name; CreateCreate a copy of the table to do testing with the following statement: CREATE TABLE invoices_copy AS SELECT * FROM invoices; Whey you create tables like this, MySQL copies only the column defnitions and data, not the primary keys, foreign keys, or indexes. INSERTUsually use this statement to add a single row to a table. Can also add multiple rows. You name the table on the INSERT clause, then add an optional list of columns, then add the new row
INSERT INTO invoices (1) (vendor_id, invoice_number, invoice_total, terms_id, invoice_date, invoice_due_date) (2) VALUES (3) (97, '456789', 8344.50, 1, '2018-08-01', '2018-08-31');
Subquery in INSERT statementA subquery is a SELECT statement that is coded within another SQL statement.
INSERT INTO invoice_archive (invoice_id, vendor_id, invoice_number, invoice_total, credit_total, payment_total, terms_id, invoice_date, invoice_due_date) SELECT invoice_id, vendor_id, invoice_number, invoice_total, credit_total, payment_total, terms_id, invoice_data, invoice_due_date FROM invoices WHERE invoice_total - payment_total - credit_total = 0; When you include a column list,
you must list the columns in the same sequence as the SELECT clause of the subquery. UPDATEUse the UPDATE statement to modify the data in one or more rows in the table.
UPDATE invoices (1) SET payment_date = '2018-09-21', (2) payment_total = 19351.18 WHERE invoice_number = '97/522'; (3)
Subquery in UPDATE statementCreate a subquery in the WHERE clause of an UPDATE statement to identify the rows that you want to update.
UPDATE invoices SET terms_id = 1 WHERE vendor_id = (SELECT vendor_id FROM vendors WHERE vendor_name = 'Pacific Bell'); A subquery is used in the WHERE clause to identify the invoices that are updated. Returns the vendor_id value for the vendor in the vendors table with the name "Pacific Bell". DELETEUse the DELETE statement to remove one or more rows from a table. A foreign key constraint may prevent you from deleting a row. If that is the case, you can delete the row only if you delete all child rows for that row first. DELETE FROM invoice_line_items (1) WHERE invoice_id = 12; (2)
Subquery in DELETE statementTo delete a row from the vendors table that has related rows in the invoices table, you must start by deleting the rows in the invoice_line_items table for the vendor's invoices, using a subquery. DELETE FROM invoice_line_items (1) WHERE invoice_id IN (2) (SELECT invoice_id FROM invoices WHERE vendor_id = 115);
Summary queriesUse aggregate functions that operate on a series of values and return a singe summary value. Sometimes called column functions because they typically operate on the values in columns. A query that contains one or more aggregate functions is typically referred to as a summary query. Most common aggregate functions
NOTE: The expression in the Result column is typically just a column name. Examples SELECT COUNT(*) AS number_of_invoices, SUM(invoice_total - payment_total - credit_total) AS total_due (1) FROM invoices WHERE invoice_total - payment_total - credit_total > 0; (2)
SELECT 'After 1/1/2018' AS selection_date, COUNT(*) AS number_of_invoices, (1) ROUND(AVG(invoice_total), 2) AS avg_invoice_amt, (2) SUM(invoice_total) AS total_invoice_amt (3) FROM invoices WHERE invoice_date > '2018-01-01';
SELECT COUNT(DISTINCT vendor_id) AS number_of_vendors, (1) COUNT(vendor_id) AS number_of_invoices, (2) ROUND(AVG(invoice_total), 2) AS avg_invoice_amt, (3) SUM(invoice_total) AS total_invoice_amt (4) FROM invoices WHERE invoice_date > '2018-01-01';
GROUP BY and HAVINGThe GROUP BY clause determines how the selected rows are grouped, and the HAVING cluase determines which groups are included in the final results. In GROUP BY, you can list more than one expression, separated by commas, and they are grouped in ascending sequence. These clauses are included after the WHERE clause and before the ORDER BY clause. When you use GROUP BY, a single row is returned for each unique set of values in the grouped columns. For example, if a result set is grouped by the vendor_id column, only one row is returned for each vendor, and that vendor is summarized by the aggregate functions that are included in the SELECT clause. Examples SELECT vendor_id, ROUND(AVG(invoice_total), 2) AS average_invoice_amount FROM invoices GROUP BY vendor_id (1) HAVING AVG(invoice_total) > 2000 (2) ORDER BY average_invoice_amount DESC;
SELECT vendor_name, vendor_state, ROUND(AVG(invoice_total), 2) AS average_invoice_amount (1) FROM vendors JOIN invoices ON vendors.vendor_id = invoices.vendor_id GROUP BY vendor_name (2) HAVING AVG(invoice_total) > 2000 ORDER BY average_invoice_amount DESC;
SELECT vendor_state, vendor_city, COUNT(*) AS invoice_qty, (1) ROUND(AVG(invoice_total), 2) AS invoice_avg (2) FROM invoices JOIN vendors ON invoices.vendor_id = vendors.vendor_id (3) GROUP BY vendor_state, vendor_city (4) HAVING COUNT(*) >= 2 (5) ORDER BY vendor_state, vendor_city;
HAVING clause vs. WHERE clauseWHERE clauses are applied to every row. HAVING are applied to each group of rows. Example SELECT vendor_name, COUNT(*) AS invoice_qty, (1) ROUND(AVG(invoice_total), 2) AS invoice_avg (2) FROM vendors JOIN invoices ON vendors.vendor_id = invoices.vendor_id GROUP BY vendor_name (3) HAVING AVG(invoice_total) > 500 (4) ORDER BY invoice_qty DESC;
SELECT vendor_name, COUNT(*) AS invoice_qty, (1) ROUND(AVG(invoice_total), 2) AS invoice_avg (2) FROM vendors JOIN invoices ON vendors.vendor_id = invoices.vendor_id WHERE invoice_total > 500 (3) GROUP BY vendor_name (4) ORDER BY invoice_qty;
BEST PRACTICE You can use either the WHERE or HAVING clause to code non-aggregte clauses, but it makes more sense to include them all in the HAVING clause for readability. WITH ROLLUP operatorUse this operator at the end of the GROUP BY clause to add one or more summary rows to a result set that uses grouping and aggregates. Example SELECT vendor_id, COUNT(*) AS invoice_count, SUM(invoice_total) AS invoice_total FROM invoices GROUP BY vendor_id WITH ROLLUP; This query adds a summary row to the end of the result set that summarizes all of the aggregate columns in the result set. If a column cannot be summarized, it contains a NULL value. GROUPING functionSometimes it is difficult to distinguish between the null values that are due to grouping and the null values that are due to summarizing. This function is commonly used to replace the nulls that are generated by WITH ROLLUP with literal values. The GROUPING function returns a 1 if the expression is null because it's in a summary row, and returns 0 otherwise. SELECT IF(GROUPING(invoice_date) = 1, 'Grand totals', invoice_date) (1) AS invoice_date, IF(GROUPING(payment_date) - 1, 'Invoice date totals', payment_date) (2) AS payment_date, SUM(invoice_total) AS invoice_total, SUM(invoice_total - credit_total - payment_total) AS balance_due FROM invoices WHERE invoice_date BETWEEN '2018-07-24' AND '2018-07-31' GROUP BY invoice_date, payment_date WITH ROLLUP;
Window functionsSimilar to GROUP BY, except that the groups (or partitions), are not collapsed to a single row - all rows in the result set are returned. Think of this as exposing a window of rows to a function. To start, you code an aggregate window function by including the OVER clause. This clause defines the window that's used by the aggregate function. OVER() - uses a single parition. All the rows for that column will be the same value OVER(PARTITION BY [column_name]) - partitions the result set by the column parameter. The aggregate function is performed and grouped by each unique value in the specified column. A window consists of all the rows that are needed to evaluate the function for the current row. Example SELECT vendor_id, invoice_date, invoice_total, SUM(invoice_total) OVER() AS total_invoices, (1) SUM(invoice_total) OVER(PARTITION BY vendor_id) AS vendor_total (2) FROM invoices WHERE invoice_total > 5000;
SELECT vendor_id, invoice_date, invoice_total, SUM(invoice_total) OVER() AS total_invoices, SUM(invoice_total) OVER(PARTITION BY vendor_id ORDER BY invoice_total) AS vendor_total (1) FROM invoices WHERE invoice_total > 5000;
FramesThe number of rows before and after the current row (ROW) or a range of values based on the value of the current row (RANGE). A frame defines a subset of the current partition. Beacuse a frame is relateve to the current row, it can move within a partition as the current row changes. If you specify just the starting row for a frame, the ending row is the current row. To specify both a sstarting and ending rtow, you use the BETWEEN clause. TWhen you use BETWEEN, the starting row for a frame must not come after the ending row.
SELECT vendor_id, invoice_date, invoice_total, SUM(invoice_total) OVER() AS total_invoices, SUM(invoice_total) OVER(PARTITION BY vendor_id ORDER BY invoice_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) (1) AS vendor_total FROM invoices WHERE invoice_date BETWEEN '2018-04-01' AND '2018-04-30';
peer - a row that has the same value as other rows in the sort column. The following example
SELECT vendor_id, invoice_date, invoice_total, SUM(invoice_total) OVER() AS total_invoices, SUM(invoice_total) OVER(PARTITION BY vendor_id ORDER BY invoice_date RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) (1) AS vendor_total FROM invoices WHERE invoice_date BETWEEN '2018-04-01' AND '2018-04-30';
The following example calculates the 3-month avergage of the sum of invoice totals. SELECT MONTH(invoice_date) AS month, SUM(invoice_total) AS total_invoices, ROUND(AVG(SUM(invoice_total)) OVER(ORDER BY MONTH(invoice_date) RANGE BETWEEN 1 PRECEDING AND 1 FOLLOWING), 2) AS 3_month_avg (1) FROM invoices GROUP BY MONTH (invoice_date);
Named windowsIn some cases, you may need to code a SELECT statement with two or more aggregate functions that use the same window. If so, then you may want to use a named window so that you don't have to repeat the definition for the window for each function. Use a WINDOW clause after the HAVING clause and before the ORDER BY, when included. Similar to using an alias for a column name. SELECT vendor_id, invoice_date, invoice_total, SUM(invoice_total) OVER vendor_window AS vendor_total, (2) ROUND(AVG(invoice_total) OVER vendor_window, 2) AS vendor_avg, (2) MAX(invoice_total) OVER vendor_window AS vendor_max, (2) MIN(invoice_total) OVER vendor_window AS vendor_min (2) FROM invoices WHERE invoice_total > 5000 WINDOW vendor_window AS (PARTITION BY vendor_id); (1)
SubqueriesA SELECT statement that is coded within another SQL statement. It is used to create queries that work with two or more tables. A subquery cannot include an ORDER BY clause. 4 ways to introduce a subquery in a SELECT statement
Example The following statement returns all the invoices from the invoices table that have invoice totals greater than the average of all the invoices. SELECT invoice_number, invoice_date, invoice_total FROM invoices WHERE invoice_total > (2) (SELECT AVG(invoice_total) (1) FROM invoices) ORDER BY invoice_total;
When to use subqueries (vs JOINS)Most subqueries can be restated as JOINS, and vice versa. When you use a subquery in a WHERE clause, its results cannot ve included in the final set because it is not included in the FROM clause. JOIN
SELECT invoice_number, invoice_date, invoice_total FROM invoices JOIN vendors ON invoices.vendor_id = vendors.vendor_id WHERE vendor_state = 'CA' ORDER BY invoice_date; Subquery
SELECT invoice_number, invoice_datte, invoice_total FROM invoices WHERE vendor_id IN (SELECT vendor_id (1) FROM vendors WHERE vendor_state = 'CA') ORDER BY invoice_date;
Subqueries in the WHERE clauseUse the IN ooerator to test whetehr an expression is contained in a list of values. You can provide that list of values in a subquery. When you use the IN operator with a subquery, the subquery must return a single column that provides the list of values. Statements that use the IN operator with a subquery can usually be restated as an OUTER JOIN. SELECT vendor_id, vendor_name, vendor_state FROM vendors WHERE vendor_id NOT IN (2) (SELECT DISTINCT vendor_id (1) FROM invoices) ORDER BY vendor_id;
Comparison operators and subqueriesWhen you use a comparison operator to return a single value, you need to use an aggregate function. SELECT invoice_number, invoice_date, invoice_total - payment_total - credit_total AS balance_due FROM invoices WHERE invoice_total - payment_total - credit_total > 0 AND invoice_total - payment_total - credit_total < ( SELECT AVG(invoice_total - payment_total - credit_total) FROM invoices WHERE invoice_total - payment_total - credit_total > 0 ) ORDER BY invoice_total DESC; ALL keywordReturns a boolean. Used to modify the comparison operator so that the condition must be true for all the values retuned by a subquery. If no rows are returned by the subqeuery, a comparison that uses the ALL keyword is always true.
Example Return invoices larger than the largest invoice or vendor 34 SELECT vendor_name, invoice_number, invoice_total FROM invoices i JOIN vendors v ON i.vendor_id = v. vendor_id WHERE invoice_total > ALL (2) (SELECT invoice_total (1) FROM invoices WHERE vendor_id = 34) ORDER BY vendor_name;
ANY and SOME keywordsUsed to test whether a comparison is true for any of the values returned by a subquery. These keywords are interchangeable. Example SELECT vendor_name, invoice_number, invoice_total FROM invoices i JOIN vendors v ON i.vendor_id = v. vendor_id WHERE invoice_total < ANY (2) (SELECT invoice_total (1) FROM invoices WHERE vendor_id = 115)
This subquery could and should be rewritten using the MAX function: WHERE invoice_total < (SELECT MAX(invoice_total) FROM invoices WHERE vendor_id = 115) Correlated subqueriesUncorrelated subquery is executed once for the entire query. Example Retrieves rows from the invoices table for those invoices that have an invoice total that's greater than the average of all the
invoices for the same vendor. SELECT vendor_id, invoice_number, invoice_total FROM invoices i WHERE invoice_total > (SELECT AVG(invoice_total) (2) FROM invoices WHERE vendor_id = i.vendor_id) (1) ORDER BY vendor_id, invoice_total;
FIGURE THIS OUTEXISTS operatorTests whether the subquery returns a result set (if it exists or not). Typically used with a correlated subquery. SELECT vendor_id, vendor_name, vendor_state FROM vendors WHERE NOT EXISTS (2) (SELECT * (1) FROM invoices WHERE vendor_id = vendors.vendor_id)
Subqueries in the HAVING clauseSpecify a search condition just like the WHERE clause. Subqueries in the SELECT clauseReplace a column specification with a subquery. The result of a query must return a single value for that column. In most cases, you use a correlated subquery in the SELECT clause. SELECT vendor_name, (SELECT MAX(invoice_date) FROM invoices (1) WHERE vendor_id = vendors.vendor_id) AS latest_inv FROM vendors ORDER BY latest_inv DESC;
Restated as a JOIN SELECT vendor_name, MAX(invoice_date) AS latest_inv FROM vendors v LEFT JOIN invoices i ON v.vendor_id = i.vendor_id GROUP BY vendor_name ORDER BY latest_inv DESC; Subqueries in the FROM clauseCode a subquery in place of a table specification. The
result is sometimes referred to as an inline view. Example Returns the largest invoice total for the top vendor in each state. SELECT vendor_state, MAX(sum_of_invoices) AS max_sum_of_invoices (2) FROM ( SELECT vendor_state, vendor_name, (1) SUM(invoice_total) AS sum_of_invoices (2) FROM vendors v JOIN invoices i ON v.vendor_id = i.vendor_id GROUP BY vendor_state, vendor_name ) t (3) GROUP BY vendor_state ORDER BY vendor_state;
Complex queries with subqueriesThe following query retrieves the vendor from each state that has the largest invoice total. It uses 3 subqueries as outlined in the comments. SELECT t1.vendor_state, vendor_name, t1.sum_of_invoices FROM ( -- 1. invoice totals by vendor SELECT vendor_state, vendor_name, SUM(invoice_total) AS sum_of_invoices FROM vendors v JOIN invoices i ON v.vendor_id = i.vendor_id GROUP BY vendor_state, vendor_name ) t1 JOIN ( -- 2. top invoice totals by state SELECT vendor_state, MAX(sum_of_invoices) AS sum_of_invoices FROM ( -- 3. invoice totals by vendor SELECT vendor_state, vendor_name, SUM(invoice_total) AS sum_of_invoices FROM vendors v JOIN invoices i ON v.vendor_id = i.vendor_id GROUP BY vendor_state, vendor_name ) t2 GROUP BY vendor_state ) t3 ON t1.vendor_state = t3.vendor_state AND t1.sum_of_invoices = t3.sum_of_invoices ORDER BY vendor_state; Procedure for building complex queries
SELECT vendor_state, vendor_name, sum_of_invoices FROM (subquery returning vendor_state, vendor_name, sum_of_invoices) JOIN (subquery returning vendor_state, largest_sum_of_invoices) ON vendor_state AND sum_of_invoices ORDER BY vendor_state;
SELECT vendor_state, vendor_name, SUM(invoice_total) AS sum_of_invoices FROM vendors v JOIN invoices i ON v.vendor_id = i.vendor_id GROUP BY vendor_state, vendor_name
SELECT vendor_state, MAX(sum_of_invoices) AS sum_of_invoices FROM ( SELECT vendor_state, vendor_name, SUM(invoice_total) AS sum_of_invoices FROM vendors v JOIN invoices i ON v.vendor_id = i.vendor_id GROUP BY vendor_state, vendor_name ) t GROUP By vendor_state
Common table expressions (CTE)A CTE is a SELECT statement that creates one or more named temporary result sets that can be used by the query that follows. Use CTEs to simplify complex queries that use subqueries.
WITH summary as ( SELECT vendor_state, vendor_name, SUM(invoice_total) AS sum_of_invoices FROM vendors v JOIN invoices i ON v.vendor_id = i.vendor_id GROUP BY vendor_state, vendor_name ), top_in_state AS ( SELECT vendor_state, MAX(sum_of_invoices) AS sum_of_invoices FROM summary GROUP BY vendor_state ) SELECT summary.vendor_state, summary.vendor_name, top_in_state.sum_of_invoices FROM summary JOIN top_in_state ON summary.vendor_state = top_in_state.vendor_state AND summary.sum_of_invoices = top_in_state.sum_of_invoices ORDER BY summary.vendor_state; Recursive CTEsA recursive query is a query that is able to loop through a result set and perform processing to return a final result set.
WITH RECURSIVE employees_cte AS ( -- Nonrecursive query SELECT employee_id, CONCAT(first_name, ' ', last_name) AS employee_name, 1 AS ranking FROM employees WHERE manager_id is NULL UNION ALL -- Recursive query SELECT employees.employee_id, CONCAT(first_name, ' ', last_name), ranking + 1 FROM employees JOIN employees_cte ON employees.manager_id = employees_cte.employee.id ) SELECT * FROMM employees_cte ORDER BY ranking, employee_id; Data typesA data type specifies the kind of information the column is inteded to store. This determines the operations that can be performed on the column. Data type table
Character typesThe two most common character data types are CHAR and VARCHAR.
How the character types work with utf8mb4utf8mb4 character set uses up to 4 bytes to store each character, so its called a multiple-byte character set. When it is used with CHAR, it uses 4 bytes per character. With VARCHAR, it uses 1 byte per character and 1 byte to store its location.
CHAR
VARCHAR
Integer typesThese are positive or negative numbers that don't include a decimal point. The INT type is most commonly used because it can store a wide range of nunbers and only requires 4 bytes of storage.
UNSIGNED - attribute for an integer type to prevent negative values from being stored in the column. ZEROFILL - attribute that sets the UNSIGNED attribute automatically, and the integer is displayed with zeros padded from the left, up to the maximum display size. (max display size for INT is 10) BOOLEAN and BOOL - synonym for TINYINT(1). 0 is FALSE, 1 is TRUE. Fixed-point and floating-point typesFixed-point type
Floating-point type
Date and time typesTypically use TIMESTAMP to track when a row was inserted or last updated.
ENUM and SET typesBoth types allow you to restrict the values for a column to a limited set of strings.
The large object typesThese are designed to store large amounts of binary or character data. Data conversionAutomatic conversions are called implicit conversions. CAST AND CONVERT functionsUse the CAST and CONVERT functions to conver an expression to the data type that you specify.
CASTSELECT invoice_id, invoice_date, invoice_total, CAST(invoice_date AS CHAR(10)) AS char_date, CAST(inoice_total AS SIGNED) AS integer_total FROM invoices; Explicitly cast string as an integerSELECT * FROM string_example ORDER BY CAST(emp_id AS SIGNED); Implicity cast string as an integerSELECT * FROM string_example ORDER BY emp_id + 0; ### CONVERT ```sql SELECT invoice_id, invoice_date, invoice_total, CONVERT(invoice_date AS CHAR(10)) AS char_date, CONVERT(inoice_total AS SIGNED) AS integer_total FROM invoices; FORMAT and CHAR functionsFORMAT converts a number to a string of characters. This makes large numbers easier to read. Additionally, it rounds the last number. CHAR returns a binary string for each specified integer. This is typically used to output ASCII control characters that cannot be typed on your keyboard.
FunctionsThis section contains common functions and their uses. Parsing a stringUse SUBSTRING_INDEX to parse a stringSELECT emp_name SUBSTRING_INDEX(emp_name, ' ', 1) AS first_name, 1) SUBSTRING_INDEX(emp_name, ' ', -1) AS last_name ( ) FROM string_sample;
Use LOCATE to find a character in a stringSELECT emp_name, LOCATE(' ', emp_name) AS first_space, (1) LOCATE(' ', emp_name, LOCATE(' ', emp_name) + 1) AS second_space (2) FROM string_sample
Use SUBSTRING to parse a stringSELECT emp_name, SUBSTRING(emp_name, 1, LOCATE(' ', emp_name) -1) AS first_name (1) SUBSTRING(emp_name, LOCATE(' ', emp_name) +1) AS last_name (2) FROM string_sample
Numeric data
Searching for floating-point numbersYou don't/can't search for exact numbers because floats and decimals are approximate numbers. SELECT * FROM float_sample WHERE float_value BETWEEN 0.99 AND 1.01 Or, you can search for values that round to an exact value: SELECT * FROM float_sample WHERE ROUND(float_value, 2) = 1.00; Working with date/time data
Date/time parsing functions
EXTRACT function with date/timeUse EXTRACT with any of the date/time units followed by the FROM keyword and a date/time value to extract the specified unit from the date/time value and retun an integer value that corresponds to that unit.
Formatting dates and timesCommon codes for date/time format strings
Examples
Performing calculations with date/time
Searching for a dateSearch for a range of datesSELECT * FROM date_sample WHERE start_date >= '2018-02-28' AND start_date < '2018-03-01'; Search for month, day, and year integersSELECT * FROM date_sample WHERE MONTH(start_date) = 2 AND DAYOFMONTH(start_date) = 28 AND YEAR(start_date) = 2018; Search for a formatted dateSELECT * FROM date_sample WHERE DATE_FORMAT(start_date, '%m-%d-%Y') = '02-28-2018'; Searching for a timeSearch for a time that has been formattedSELECT * FROM date_sample WHERE DATE_FORMAT(start_date, '%T') = '10:00:00' Search for a time that hasn't been formattedSELECT * FROM date_sample WHERE EXTRACT(HOUR_SECOND FROM start_date) = 100000; Search for an hour of the daySELECT * FROM date_sample WHERE HOUR(start_date) = 9; Search for a range of timesSELECT * FROM date_sample WHERE EXTRACT(HOUR_MINUTE FROM start_date) BETWEEN 900 AND 1200; CASE functionReturns a value that's determined by the conditions you specify. When MySQL finds an expression in a WHEN clause that's equal to the input expression, it returns the expression specified in the matching THEN clause. Example The following example determines the status fo the invoices in the invoices table. SELECT invoice_number, invoice_total, invoice_date, invoice_due_date, CASE WHEN DATEDIFF(NOW(), invoice_due_date) > 30 THEN 'Over 30 days past due' WHEN DATEDIFF(NOW(), invoice_due_date) > 0 THEN '1 to 30 days past due' ELSE 'Current' END AS invoice_status FROM invoices WHERE invoice_total - payment_total - credit_total > 0; IF, IFNULL, COALESCE functionsIF functionUse the IF function to test a conditino and return one value if the condition is true or another value if the condition is false. SELECT vendor_name, IF(vendor_city = 'Fresno', 'Yes', 'No') AS is_city_fresno FROM vendors; IFNULL functionAllows you to substituted non-null values for null values. The following example returns the first expression if it isn't null. Otherwise, it returns the replacement value you specify. SELECT payment_date, IFNULL(payment_date, 'No Payment') AS new_date FROM invoices; COALESCE functionAllows you to substituted non-null values for null values from a list of values. Returns the first expression in the list that isn't null. If all of the expressions are null, this functionreturns a null value. SELECT payment_date, COALESCE(payment_date, 'No Payment') AS new_date FROM invoices; Regular expression functionsUse a string pattern to search a string expression.
REGEXP_INSTR functionSELECT DISTINCT vendor_city, REGEXP_INSTR(vendor_city, ' ') AS space_index FROM vendors WHERE REGEXP_INSTR(vendor_city, ' ') > 0 ORDER BY vendor_city; REGEXP_SUBSTR functionSELECT vendor_city, REGEXP_SUBSTR(vendor_city, '^SAN|LOS') AS city_match FROM vendors WHERE REGEXP_SUBSTR(vendor_city, '^SAN|LOS') IS NOT NULL; REGEXP_REPLACE and REGEXP_LIKE functionsSELECT vendor_name, vendor_address1, REGEXP_REPLACE(vendor_address1, 'STREET', 'St') AS new_address1 FROM Vendors WHERE REGEXP_LIKE(vendor_address1, 'STREET'); Ranking functionsThese are non-aggregate window functions, sometimes called specialiazed window functions. These functions are grouped into 2 groups: Ranking functions and analytical functions. ROW_NUMBER functionSELECT ROW_NUMBER() OVER(PARTITION BY vendor_state (2), (3) ORDER BY vendor_name) AS 'row_number', vendor_name, vendor_state (1) FROM vendors;
RANK and DENSE_RANK functionsBoth functions return the rank of each row within the partition of a result set. If there is a tie, both of these functions give the same rank to all rows that are tied. SELECT RANK() OVER(ORDER BY invoice_total) AS 'rank', (1) DENSE_RANK() OVER (ORDER BY invoice_total) AS 'dense_rank', (2) invoice_total, invoice_number FROM invoices;
NTILE functionThis function divides the rows in a partition into a specified number of groups and returns the group number of each row. SELECT terms_description, NTILE(2) OVER (ORDER BY terms_id) AS tile2, NTILE(3) OVER (ORDER BY terms_id) AS tile3, NTILE(4) OVER (ORDER BY terms_id) AS tile4, FROM terms; Analytic functionsThese functions let you perform calculations on ordered sets of data. The FIRST_VALUE, LAST_VALUE, and NTH_VALUE let you return the first, last, and nth values in an ordered set of values. When you use the PARTITION BY clause with LAST_VALUE or NTH_VALUE, you typically include the ROWS or RANGE clause as well to define a subset of the current partition. FIRST_VALUE, NTH_VALUE, and LAST_VALUE functionsSELECT sales_year, CONCAT(rep_first_name, ' ', rep_last_name) AS rep_name, sales total, FIRST_VALUE(CONCAT(rep_first_name, ' ', rep_last_name)) OVER (PARTITION BY sales_year ORDER BY sales_total DESC) AS highest_sales, NTH_VALUE(CONCAT(rep_first_name, ' ', rep_last_name), 2) OVER (PARTITION BY sales_year ORDER BY sales_total DESC RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS second_highest_sales, LAST_VALUE(CONCAT(rep_first_name, ' ', rep_last_name)) OVER(PARTITION BY sales_year ORDER BY sales_total DESC RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS lowest_sales FROM sales_totals JOIN sales_reps ON sales_totals.rep_id = sales_reps.rep_id; LAG functionSELECT rep_id, sales_year, sales_total AS current_sales, LAG(sales_total, 1, 0) OVER (PARTITION BY rep_id ORDER BY sales_year) (1), (2) AS last_sales, sales_total - LAG(sales_total, 1, 0) OVER (PARTITION BY rep_id ORDER BY sales_year) AS 'change' (1) FROM sales_totals;
PERCENT_RANK and CUME_DIST functionsThe PERCENT_RANK calculates the rank of the values in a sorted set of values as a percent. Calculates a percent that indicates the rank of each row within a partition. The result of this function is always 0 or 1. The CUME_DIST function calculates the percent of the values in a sorted set of values that are less than or equal to the current value. This represents the cumulative distribution - calculated by dividing the number of rows with the current value or a lower value by the total nubmer of rows in the partition. SELECT sales_year, rep_id, sales_total, PERCENT_RANK() OVER (PARTITION BY sales_year ORDER BY sales_total) AS pct_rank, CUME_DIST() OVER (PARTITION BY sales_year ORDER BY sales_total) AS 'cume_dist' FROM sales_totals; Working with databasesCreating a databaseCREATE DATABASE [IF NOT EXISTS] dbName Dropping a databaseCREATE DATABASE [IF EXISTS] dbName Use statementThe USE statement selects the specified database and makes it the current database. Creating a tableThe CREATE TABLE statement creates a new table in the current database. Alternately, you can qualify the table name with the correct database. For example, Common column attributes
NOTE: NULL and UNIQUE are types of constraints. Create a table without column attributesCREATE TABLE vendors ( vendor_id INT, vendor_name VARCHAR(50) ) Create a table with column attributesCREATE TABLE vendors ( vendor_id INT NOT NULL UNIQUE AUTO_INCREMENT, vendor_name VARCHAR(50) NOT NULL UNIQUE ) Coding a primary key constraintConstraints are used to enforce the integrity of the data in a table by defining rules about the values that can be stored in the columns of the table. When you identify a column as the primary key, the following happens:
Column-level constraintsCode a column-level constraint as part of the definition of the column it constrains. CREATE TABLE vendors ( vendor_id INT PRIMARY KEY AUTO_INCREMENT, vendor_name VARCHAR(50) NOT NULL UNIQUE ) Table-level constraintsCode a table-level constraint as if it is a separate column definition, and you name the columns it constrains within that definition. CREATE TABLE vendors ( vendor_id INT AUTO_INCREMENT, vendor_name VARCHAR(50) NOT NULL, CONSTRAINT vendors_pk PRIMARY KEY (vendor_id), CONSTRAINT vendor_name_uq UNIQUE (vendor_name) ) Foreign-key constraintsAlso known as a reference constraint. This constraint requires values in one tabvle to match values in another table. Used to define the relationship between tables and to enforce referential integrity. Column-level foreign key constraintTo create a foreign key constraing at the column level, you code the REFERENCES keyword followed by the name of the related table and the name of the related colum nin parentheses. CREATE TABLE invoices ( invoice_id INT PRIMARY KEY, vendor_id INT REFERENCES vendors (vendor_id), invoice_number VARCHAR(50) NOT NULL UNIQUE ) Table-level foreign key constraintTo create a foreign key constraing at the table level, include the CONSTRAINT keyword followed by a name, followed by the FOREIGN KEY keywords. This allows you to provide a name for the foreign key, which is a best practice. CREATE TABLE invoices ( invoice_id INT PRIMARY KEY, vendor_id INT NOT NULL, invoice_number VARCHAR(50) NOT NULL UNIQUE, CONSTRAINT invoices_fk_vendors FOREIGN KEY (vendor_id) REFERENCES vendors (vendor_id) ) Alter constraints of a tableUse the ALTER TABLE statement to add or drop the constraints of an existing table Adds a new columnALTER TABLE vendors ADD last_transaction_date DATE Drops a columnALTER TABLE vendors DROP COLUMN last_transaction_date Change the length of the columnALTER TABLE vendors MODIFY vendor_name VARCHAR(100) NOT NULL Change data typeALTER TABLE vendors MODIFY vendor_name CHAR(100) NOT NULL Change default value of a columnALTER TABLE vendors MODIFY vendor_name VARCHAR(100) NOT NULL DEFAULT 'New Vendor'; Change nameALTER TABLE vendors RENAME COLUMN vendor_name TO v_name; Alter the constraints of a tableTo drop a foreign key constraint, yoiu must know its name. If you don't, use a GUI tool to look it up. Add a primary key constraintALTER TABLE vendors ADD PRIMARY KEY (vendor_id) Add a foreign key constraintALTER TABLE invoices ADD CONSTRAINT invoice_fk_vendors FOREIGN KEY (vendor_id) REFERENCES vendors (vendor_id) Drop primary key constraintALTER TABLE vendors DROP PRIMARY KEY Drop foreign key constraintALTER TABLE invoices DROP PRIMARY KEY invoice_fk_vendors Rename, truncate, and drop a tableBe careful with these statements! Rename a tableRENAME TABLE vendors TO vendor Delete all data from a tableDelete table from the current databaseQualifies the table to be deletedIndexesAn index speeds up joins and searches by providing a way for a database management system to go directly to a row rather than having to search through all the rows until if finds the one that you want. By default, MySQL creates indexes for primary keys, foreign keys, and uique keys of a table. You may want to create indexes for other columns that are used frequently in search conditions or joins. Avoid creating indexes on columns that are updated frequently since this slows down insert, update, and delete operations. You can also drop indexes from a table. Standard index naming conventionCreate index based on single columnCREATE INDEX invoices_invoice_date_ix ON invoices (invoice_date) Creates an index based on two columnsCREATE INDEX invoices_vendor_id_invoice_number_ix ON invoices (invoice_id, invoice_number) Creates a unique indexCREATE UNIQUE INDEX vendors_vendor_phone_ix ON vendors (vendor_phone) Creates an index sorted in DESCCREATE INDEX invoices_invoice_total_ix ON invoices (vendor_phone DESC) Drops an indexDROP INDEX vendors_vendor_phone_ix ON vendors A script that creates an entire databaseA script is a file that contains one or more SQL statements. You must create the tables that don't have foreign keys ffirst. That way, the other tables can define foreign keys that refer to them. -- create the database DROP DATABASE IF EXISTS ap; CREATE DATABASE ap; -- select the database USE ap; -- create the tables CREATE TABLE general_ledger_accounts ( account_number INT PRIMARY KEY, account_description VARCHAR(50) UNIQUE ); CREATE TABLE terms ( terms_id INT PRIMARY KEY, terms_description VARCHAR(50) NOT NULL, terms_due_days INT NOT NULL ); CREATE TABLE vendors ( vendor_id INT PRIMARY KEY AUTO_INCREMENT, vendor_name VARCHAR(50) NOT NULL UNIQUE, vendor_address1 VARCHAR(50), vendor_address2 VARCHAR(50), vendor_city VARCHAR(50) NOT NULL, vendor_state CHAR(2) NOT NULL, vendor_zip_code VARCHAR(20) NOT NULL, vendor_phone VARCHAR(50), vendor_contact_last_name VARCHAR(50), vendor_contact_first_name VARCHAR(50), default_terms_id INT NOT NULL, default_account_number INT NOT NULL, CONSTRAINT vendors_fk_terms FOREIGN KEY (default_terms_id) REFERENCES terms (terms_id), CONSTRAINT vendors_fk_accounts FOREIGN KEY (default_account_number) REFERENCES general_ledger_accounts (account_number) ); CREATE TABLE invoices ( invoice_id INT PRIMARY KEY AUTO_INCREMENT, vendor_id INT NOT NULL, invoice_number VARCHAR(50) NOT NULL, invoice_date DATE NOT NULL, invoice_total DECIMAL(9,2) NOT NULL, payment_total DECIMAL(9,2) NOT NULL DEFAULT 0, credit_total DECIMAL(9,2) NOT NULL DEFAULT 0, terms_id INT NOT NULL, invoice_due_date DATE NOT NULL, payment_date DATE, CONSTRAINT invoices_fk_vendors FOREIGN KEY (vendor_id) REFERENCES vendors (vendor_id), CONSTRAINT invoices_fk_terms FOREIGN KEY (terms_id) REFERENCES terms (terms_id) ); CREATE TABLE invoice_line_items ( invoice_id INT NOT NULL, invoice_sequence INT NOT NULL, account_number INT NOT NULL, line_item_amount DECIMAL(9,2) NOT NULL, line_item_description VARCHAR(100) NOT NULL, CONSTRAINT line_items_pk PRIMARY KEY (invoice_id, invoice_sequence), CONSTRAINT line_items_fk_invoices FOREIGN KEY (invoice_id) REFERENCES invoices (invoice_id), CONSTRAINT line_items_fk_acounts FOREIGN KEY (account_number) REFERENCES general_ledger_accounts (account_number) ); -- create an index CREATE INDEX invoices_invoice_date_ix ON invoices (invoice_date DESC); Storage enginesA storage engine determines how MySQL stores data and which database features are available to you. View all the storage engines available on the serverView the default storage engine for a serverSHOW VARIABLES LIKE 'default_storage_engine'; View the storage engine for all the tables in a databaseSELECT table_name, engine FROM information_schema.tables WHERE table_schema = 'ap'; Specify a storage engine for a tableCREATE TABLE product_descriptions ( product_id INT PRIMARY KEY, product_description VARCHAR(200) ) ENGINE = MyISAM; Specify an engine for an existing tableALTER TABLE product_descriptions ENGINE = InnoDB; Set the default storage engine for the current sessionSET SESSION default_storage_engine = InnoDB; Create viewsA view is a SELECT statement that is stored in the database as a database object. Think of it as a virtyal table that consists of only the rows and columns specified in its CREATE VIEW statement. When you save queries, you can store them in a script or you can save them in a view. Views are not stored in files, they are stored as part of the database. So, they can be used by SQL programmers and by custom applications that have access to the database. Create view statement for view named vendors_minCREATE VIEW vendors_min AS SELECT vendor_name, vendor_state, vendor_phone FROM vendors; CREATE VIEW vendors_sw AS SELECT * FROM vendors WHERE vendor_state IN ('CA', 'AZ', 'NV', 'NM'); SELECT statement that uses vendors_min viewSELECT * FROM vendors_min WHERE vendor_state = 'CA' ORDER BY vendor_name; UPDATE statement that uses vendors_min viewUPDATE vendors_min SET vendor_phone = '(800) 555-3941' WHERE vendor_name = 'Register of Copyrights'; Drop a viewUsing REPLACEWhen you code a view, you can specify that you want to automatically drop a view that has the same name as the view that you're creating by using the OR REPLACE keywords after the CREATE keyword. CREATE OR REPLACE VIEW vendor_invoices AS SELECT vendor_name, invoice_number, invioce_date, invoice_total FROM vendors JOIN invoices ON vendors.vendor_id = invoices.vendor_id; Updatable viewTo be an updatable view, a view must meet the following requirements:
To INSERT rows into views, you must make sure that the INSERT statement includes the all the rows as the view. If a view is updatable, you can use the INSERT, UPDATE, or DELETE clauses with them. If a view isn't updatable, its a read-only view. WITH CHECK option clauseIf you specify a WITH CHECK OPTION clause when you creat a view, an error will occur if you try to modify a row in such a way that it would no longer be included in the
view. CREATE OR REPLACE VIEW vendor_payment AS SELECT vendor_name, invoice_number, invoice_date, payment_date, invoice_total, credit_total, payment_total FROM vendors JOIN invoices ON vendors.vendor_id = invoices.vendor_id WHERE invoice_total - payment_total - credit_total >= 0 WITH CHECK OPTION; Stored programsStored programs include procedural code that controls the flow of programs.
Script for stored procedure named testNOTE: The DELIMITER // statement changes the delimter from the default semicolon delimiter (;) to two slashed (//) USE ap; DROP PROCEDURE IF EXISTS test; -- Change statement delimiter from semicolon to double front slash DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE sum_balance_due_var DECIMAL(9, 2); SELECT SUM(invoice_total - payment_total - credit_total) INTO sum_balance_due_var FROM invoices WHERE vendor_id = 95; IF sum_balance_due_var > 0 THEN SELECT CONCAT('Balance due: $', sum_balance_due_var) AS message; ELSE SELECT 'Balance paid in full' AS message; END IF; END// -- Change statement delimiter from double front slash to semicolon DELIMITER ; CALL test(); Flow of execution keywords
Display data and debugDELIMITER // CREATE PROCEDURE test() BEGIN SELECT 'This is a test.' AS messages; END// How to declare and set variablesThe following sets a variable to a literal value or an expression: SET variable_name = {literal_value|expression}; SET vendor_id_var = 95; The following sets a variable to a selected value SELECT column_1[, column_2]... INTO variable_name_1[, variable_name_2]... SELECT MAX(invoice_total), MIN(invoice_total), COUNT(invoice_id) INTO max_invoice_total, min_invoice_total, count_invoice_id USE ap; DROP PROCEDURE IF EXISTS test; DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE max_invoice_total DECIMAL(9,2); DECLARE min_invoice_total DECIMAL(9,2); DECLARE percent_difference DECIMAL(9,4); DECLARE count_invoice_id INT; DECLARE vendor_id_var INT; SET vendor_id_var = 95; SELECT MAX(invoice_total), MIN(invoice_total), COUNT(invoice_id) INTO max_invoice_total, min_invoice_total, count_invoice_id FROM invoices WHERE vendor_id = vendor_id_var; SET percent_difference = (max_invoice_total - min_invoice_total) / min_invoice_total * 100; SELECT CONCAT('$', max_invoice_total) AS 'Maximum invoice', CONCAT('$', min_invoice_total) AS 'Minimum invoice', CONCAT('%', ROUND(percent_difference, 2)) AS 'Percent difference', count_invoice_id AS 'Number of invoices'; END// DELIMITER ; CALL test(); Stored procedure that uses an IF statementUSE ap; DROP PROCEDURE IF EXISTS test; DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE first_invoice_due_date DATE; SELECT MIN(invoice_due_date) INTO first_invoice_due_date FROM invoices WHERE invoice_total - payment_total - credit_total > 0; IF first_invoice_due_date < NOW() THEN SELECT 'Outstanding invoices are overdue!'; ELSEIF first_invoice_due_date = SYSDATE() THEN SELECT 'Outstanding invoices are due today!'; ELSE SELECT 'No invoices are overdue.'; END IF; -- the IF statement rewritten as a Searched CASE statement /* CASE WHEN first_invoice_due_date < NOW() THEN SELECT 'Outstanding invoices overdue!' AS Message; WHEN first_invoice_due_date = NOW() THEN SELECT 'Outstanding invoices are due today!' AS Message; ELSE SELECT 'No invoices are overdue.' AS Message; END CASE; */ END// DELIMITER ; CALL test(); Case statementsSimilar to a switch statement in Java. USE ap; DROP PROCEDURE IF EXISTS test; DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE terms_id_var INT; SELECT terms_id INTO terms_id_var FROM invoices WHERE invoice_id = 4; CASE terms_id_var WHEN 1 THEN SELECT 'Net due 10 days' AS Terms; WHEN 2 THEN SELECT 'Net due 20 days' AS Terms; WHEN 3 THEN SELECT 'Net due 30 days' AS Terms; ELSE SELECT 'Net due more than 30 days' AS Terms; END CASE; END// While loopsUSE ap; DROP PROCEDURE IF EXISTS test; DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE i INT DEFAULT 1; DECLARE s VARCHAR(400) DEFAULT ''; -- WHILE loop WHILE i < 4 DO SET s = CONCAT(s, 'i=', i, ' | '); SET i = i + 1; END WHILE; -- REPEAT loop /* REPEAT SET s = CONCAT(s, 'i=', i, ' | '); SET i = i + 1; UNTIL i = 4 END REPEAT; */ -- LOOP with LEAVE statement /* testLoop : LOOP SET s = CONCAT(s, 'i=', i, ' | '); SET i = i + 1; IF i = 4 THEN LEAVE testLoop; END IF; END LOOP testLoop; */ SELECT s AS message; END// DELIMITER ; CALL test(); Using a cursorUse a cursor when you need to work with the data in a result set one row at a time. Like a flag variable in Java. -- Declare the cursor DECLARE cursomr_name CURSOR FOR select_statement; -- Declare an error handler for when no rows are found in the cursor DECLARE CONTINUE HANDLER FOR NOT FOUND handler_statement; -- Open the cursor OPEN cursor_name; -- Get column values from the row and store them in series of variables FETCH cursor_name INTO variable1[, variable2][, variable3]... -- Close the cursor CLOSE cursor_name; Complete cursor exampleUSE ap; DROP PROCEDURE IF EXISTS test; DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE invoice_id_var INT; (1) DECLARE invoice_total_var DECIMAL(9,2); DECLARE row_not_found TINYINT DEFAULT FALSE; DECLARE update_count INT DEFAULT 0; DECLARE invoices_cursor CURSOR FOR (2) SELECT invoice_id, invoice_total FROM invoices WHERE invoice_total - payment_total - credit_total > 0; DECLARE CONTINUE HANDLER FOR NOT FOUND (3) SET row_not_found = TRUE; OPEN invoices_cursor; (4) WHILE row_not_found = FALSE DO (5) FETCH invoices_cursor INTO invoice_id_var, invoice_total_var; IF invoice_total_var > 1000 THEN UPDATE invoices SET credit_total = credit_total + (invoice_total * .1) WHERE invoice_id = invoice_id_var; SET update_count = update_count + 1; END IF; END WHILE; CLOSE invoices_cursor; (6) SELECT CONCAT(update_count, ' row(s) updated.'); (7) END// DELIMITER ; CALL test();
Error codesThe following are the most common error codes, but there are thousands of error codes.
Using condition handlersThis is error handling. You want to handle exceptions before you put your stored programs into production. CONTINUE error handlerUSE ap; DROP PROCEDURE IF EXISTS test; DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE duplicate_entry_for_key INT DEFAULT FALSE; DECLARE CONTINUE HANDLER FOR 1062 (1) SET duplicate_entry_for_key = TRUE; (2) INSERT INTO general_ledger_accounts VALUES (130, 'Cash'); IF duplicate_entry_for_key = TRUE THEN SELECT 'Row was not inserted - duplicate key encountered.' AS message; ELSE SELECT '1 row was inserted.' AS message; END IF; END// DELIMITER ; CALL test();
EXIT error handlerThis code exits the current block of code as soon as it encounters an error. USE ap; DROP PROCEDURE IF EXISTS test; DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE duplicate_entry_for_key INT DEFAULT FALSE; BEGIN DECLARE EXIT HANDLER FOR 1062 SET duplicate_entry_for_key = TRUE; INSERT INTO general_ledger_accounts VALUES (130, 'Cash'); SELECT '1 row was inserted.' AS message; END; IF duplicate_entry_for_key = TRUE THEN SELECT 'Row was not inserted - duplicate key encountered.' AS message; END IF; END// DELIMITER ; CALL test(); Named condition error handlerUse a named condition to handle the error that occurs when a row cannot be inserted. When this condition occurs, the stroed procedure displays a message that indicates that the row was not inserted because of a SQL exception. This is like using a language-defined exception in Java, like IOException. USE ap; DROP PROCEDURE IF EXISTS test; DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE sql_error INT DEFAULT FALSE; BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION SET sql_error = TRUE; INSERT INTO general_ledger_accounts VALUES (130, 'Cash'); SELECT '1 row was inserted.' AS message; END; IF sql_error = TRUE THEN SELECT 'Row was not inserted - SQL exception encountered.' AS message; END IF; END// DELIMITER ; CALL test(); Using multiple condition handlersIf you use multiple condition handlers, the most specific error handlers are executed first, and the least specific/most general are executed last. USE ap; DROP PROCEDURE IF EXISTS test; DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE duplicate_entry_for_key INT DEFAULT FALSE; DECLARE column_cannot_be_null INT DEFAULT FALSE; DECLARE sql_exception INT DEFAULT FALSE; BEGIN DECLARE EXIT HANDLER FOR 1062 (1) SET duplicate_entry_for_key = TRUE; DECLARE EXIT HANDLER FOR 1048 SET column_cannot_be_null = TRUE; DECLARE EXIT HANDLER FOR SQLEXCEPTION SET sql_exception = TRUE; INSERT INTO general_ledger_accounts VALUES (NULL, 'Test'); SELECT '1 row was inserted.' AS message; END; IF duplicate_entry_for_key = TRUE THEN SELECT 'Row was not inserted - duplicate key encountered.' AS message; ELSEIF column_cannot_be_null = TRUE THEN SELECT 'Row was not inserted - column cannot be null.' AS message; ELSEIF sql_exception = TRUE THEN SELECT 'Row was not inserted - SQL exception encountered.' AS message; END IF; END// DELIMITER ; CALL test();
Transactions and lockingTransactionsA transaction is a group of SQL statements that you combine into a single logical unit of work. Combining the SQL statements can prevent certain kinds of database errors. NOTE: Some storage engines do not support transactions. To start a transaction, code the START TRANSACTION statement. This turns off autocommit mode until the statements in the transaction are committed or rolled back. To commit changes, code a COMMIT statement. TO roll back the changes, use a ROLLBACK statment. USE ap; DROP PROCEDURE IF EXISTS test; DELIMITER // CREATE PROCEDURE test() BEGIN DECLARE sql_error INT DEFAULT FALSE; DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET sql_error = TRUE; START TRANSACTION; (1) INSERT INTO invoices VALUES (115, 34, 'ZXA-080', '2014-06-30', 14092.59, 0, 0, 3, '2014-09-30', NULL); INSERT INTO invoice_line_items VALUES (115, 1, 160, 4447.23, 'HW upgrade'); INSERT INTO invoice_line_items VALUES (115, 2, 167, 9645.36, 'OS upgrade'); IF sql_error = FALSE THEN COMMIT; (2) SELECT 'The transaction was committed.'; ELSE ROLLBACK; (3) SELECT 'The transaction was rolled back.'; END IF; END// DELIMITER ; CALL test();
Working with save pointsA SAVEPOINT statement is used to identify a save point a
statement that is included in the script. USE ap; START TRANSACTION; SAVEPOINT before_invoice; (1) INSERT INTO invoices VALUES (115, 34, 'ZXA-080', '2015-01-18', 14092.59, 0, 0, 3, '2015-04-18', NULL); SAVEPOINT before_line_item1; (1) INSERT INTO invoice_line_items VALUES (115, 1, 160, 4447.23, 'HW upgrade'); SAVEPOINT before_line_item2; (1) INSERT INTO invoice_line_items VALUES (115, 2, 167, 9645.36,'OS upgrade'); -- SELECT invoice_id, invoice_sequence FROM invoice_line_items WHERE invoice_id = 115; ROLLBACK TO SAVEPOINT before_line_item2; (2) -- SELECT invoice_id, invoice_sequence FROM invoice_line_items WHERE invoice_id = 115; ROLLBACK TO SAVEPOINT before_line_item1; (2) -- SELECT invoice_id, invoice_sequence FROM invoice_line_items WHERE invoice_id = 115; ROLLBACK TO SAVEPOINT before_invoice; (2) -- SELECT invoice_id, invoice_number FROM invoices WHERE invoice_id = 115; COMMIT;
Concurrency and lockingConcurrency is when two or more users have access to the same database and they are working on the same data at the same time. Concurrency is a problem when one user updates data that other users are also viewing or updating. Use START TRANSACTION and COMMIT to lock the data you are working to prevent concurrency issues. Setting the transaction isolation levelThe transaction isolation level controls the degree to which transactions are isolated from one another. At the more restrictive isolation levels, concurrency problems are reduced or eliminated. Use the following for levels:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE How to lock selected rowsIn some cases, the default isolation levels do not work the way you want. So, you can use the following statements:
USE ex; -- Transaction B START TRANSACTION; SELECT * FROM sales_reps WHERE rep_id < 5 FOR UPDATE; COMMIT; -- Transaction C START TRANSACTION; SELECT * FROM sales_reps WHERE rep_id < 5 FOR UPDATE NOWAIT; COMMIT; -- Transaction D START TRANSACTION; SELECT * FROM sales_reps WHERE rep_id < 5 FOR UPDATE SKIP LOCKED; COMMIT; DeadlocksA deadlock occurs when neither of two transactins can be committed because each transaction has a lock on a resource needed by the other transaction. Creating stored procedures and functionsTriggersA trigger is a named database object that is executed, or fired, automatically when a particular type of SQL statement is executed. Triggers can be used to enforce rules for data consistency that can't be enforced by constraints. AFTER triggers are used to store information about a statement after it executes. For example, an audit table. Backup and restoreYou should regularly back up the database in case the database becomes corrupted. Which of the following SELECT statement would you use to prevent duplicate rows from being returned?The DISTINCT keyword in the SELECT clause is used to eliminate duplicate rows and display a unique list of values.
How would you code a SELECT clause so it returns all columns from the base table?The SELECT clause specifies one or more columns to be retrieved; to specify multiple columns, use a comma and a space between column names. To retrieve all columns, use the wild card * (an asterisk). The FROM clause specifies one or more tables to be queried.
When you code a SELECT statement you must code the four main clauses in the following order SELECT from where order by?The four main clauses used with a SELECT statement are: SELECT, FROM, WHERE, ORDER BY. The correct order of each clause is also important to note, as shown to give proper results when executed.
When a column in a table is defined what determines the kind of data it can store?A field, also called a column in a relational database, is part of a table that is assigned a specific data type. The data type determines what kind of data the column is allowed to hold.
|