tags:
- SQL
视图是虚拟的
Materialized View 物化视图
JOIN
command in SQL to combine rows from two or more tables based on a related column between them. Here is a visual representation of how these tables could be joined in order to line up authors and their books.
longlist.db
on SQLite and run the .schema
command to verify that the three tables we saw in the previous example are created: authors
, authored
and books
.SELECT "title" FROM "books"
WHERE "id" IN (
SELECT "book_id" FROM "authored"
WHERE "author_id" = (
SELECT "id" FROM "authors"
WHERE "name" = 'Fernanda Melchor'
)
);
SELECT
queries in the nested query. To simplify this, let us first use JOIN
to create a view containing authors and their books.longlist.db
again, and run the following query.
SELECT "name", "title" FROM "authors"
JOIN "authored" ON "authors"."id" = "authored"."author_id"
JOIN "books" ON "books"."id" = "authored"."book_id";
CREATE VIEW "longlist" AS
SELECT "name", "title" FROM "authors"
JOIN "authored" ON "authors"."id" = "authored"."author_id"
JOIN "books" ON "books"."id" = "authored"."book_id";
The view created here is called longlist
. This view can now be used exactly as we would use a table in SQL.SELECT * FROM "longlist";
SELECT "title" FROM "longlist" WHERE "name" = 'Fernanda Melchor';
Can we manipulate views to be ordered, or displayed differently?
longlist
view, ordered by the book titles.
SELECT "name", "title"
FROM "longlist"
ORDER BY "title";
ORDER BY
clause in the query used to create the view.也就是创建视图的时候就进行排序longlist.db
we have a table containing individual ratings given to each book. In previous weeks, we saw how to find the average rating of every book, rounded to 2 decimal places.
SELECT "book_id", ROUND(AVG("rating"), 2) AS "rating"
FROM "ratings"
GROUP BY "book_id";
books
table.
SELECT "book_id", "title", "year", ROUND(AVG("rating"), 2) AS "rating"
FROM "ratings"
JOIN "books" ON "ratings"."book_id" = "books"."id"
GROUP BY "book_id";
JOIN
to combine information from the ratings
and books
tables, joining on the book ID column.GROUP BY
operation at the end of the query after the two tables are joined.CREATE VIEW "average_book_ratings" AS
SELECT "book_id" AS "id", "title", "year", ROUND(AVG("rating"), 2) AS "rating"
FROM "ratings"
JOIN "books" ON "ratings"."book_id" = "books"."id"
GROUP BY "book_id";
SELECT * FROM "average_book_ratings";
ratings
table, to obtain an up-to-date aggregate, we need to simply requery the view using a SELECT
command like the above!.schema
to observe that longlist
and average_book_ratings
are now part of this database’s schema.CREATE TEMPORARY VIEW
. This command creates a view that exists only for the duration of our connection with the database.SELECT "year", ROUND(AVG("rating"), 2) AS "rating"
FROM "average_book_ratings"
GROUP BY "year";
Notice that we select the rating
column from average_book_ratings
, which already contains the average ratings per book. Next, we group these by year and calculate the average ratings again, which gives us the average rating per year!CREATE TEMPORARY VIEW "average_ratings_by_year" AS
SELECT "year", ROUND(AVG("rating"), 2) AS "rating" FROM "average_book_ratings"
GROUP BY "year";
.quit之后,在进入sqlite3,就不能够用 temp view 了
Can temporary views be used to test whether a query works or not?
average_book_ratings
.
DROP VIEW "average_book_ratings";
WITH "average_book_ratings" AS (
SELECT "book_id", "title", "year", ROUND(AVG("rating"), 2) AS "rating" FROM "ratings"
JOIN "books" ON "ratings"."book_id" = "books"."id"
GROUP BY "book_id"
)
SELECT "year" ROUND(AVG("rating"), 2) AS "rating" FROM "average_book_ratings"
GROUP BY "year";
CREATE VIEW "2022" AS
SELECT "id", "title" FROM "books"
WHERE "year" = 2022;
SELECT * FROM "2022";
Can views be updated?
cannot modify the data in the view
rides
that looks like the following.
rideshare.db
in our terminal. Running .schema
should reveal one table called rides
in this database.rider
column altogether. But we will go one step further here, and create a rider
column to display an anonymous rider for each row in the table. This will indicate to the analyst that while we have rider names in the database, the names have been anonymized for security.
CREATE VIEW "analysis" AS
SELECT "id", "origin", "destination", 'Anonymous' AS "rider"
FROM "rides";
SELECT * FROM "analysis";
rides
table and see all the rider names we went to great lengths to omit in the analysis
view.collections
table by changing the value in the deleted
column from 0 to 1.
mfa.db
in our terminal. The collections
table does not have a deleted
column yet, so we need to add it. The default value here will be 0, to indicate that the row is not deleted.
ALTER TABLE "collections"
ADD COLUMN "deleted" INTEGER DEFAULT 0;
deleted
column.
UPDATE "collections"
SET "deleted" = 1
WHERE "title" = 'Farmers working at dawn';
CREATE VIEW "current_collections" AS
SELECT "id", "title", "accession_number", "acquired"
FROM "collections"
WHERE "deleted" = 0;
SELECT * FROM "current_collections";
collections
, it will be removed from the current_collections
view on any further querying.INSTEAD OF
trigger allows us to do this.
CREATE TRIGGER "delete"
INSTEAD OF DELETE ON "current_collections"
FOR EACH ROW
BEGIN
UPDATE "collections" SET "deleted" = 1
WHERE "id" = OLD."id";
END;
deleted
column of the row in the underlying table collections
, thus completing the soft deletion.OLD
within our update clause to indicate that the ID of the row updated in collections
should be the same as the ID of the row we are trying to delete from current_collections
.current_collections
view.
DELETE FROM "current_collections"
WHERE "title" = 'Imaginative landscape';
We can verify that this worked by querying the view.
SELECT * FROM "current_collections";
CREATE TRIGGER "insert_when_exists"
INSTEAD OF INSERT ON "current_collections"
FOR EACH ROW
WHEN NEW."accession_number" IN (
SELECT "accession_number" FROM "collections"
)
BEGIN
UPDATE "collections"
SET "deleted" = 0
WHERE "accession_number" = NEW."accession_number";
END;
WHEN
keyword is used to check if the accession number of the artwork already exists in the collections
table. This works because an accession number, as we know from previous weeks, uniquely identifies every piece of art in this table.deleted
value to 0, indicating a reversal of the soft deletion.CREATE TRIGGER "insert_when_new"
INSTEAD OF INSERT ON "current_collections"
FOR EACH ROW
WHEN NEW."accession_number" NOT IN (
SELECT "accession_number" FROM "collections"
)
BEGIN
INSERT INTO "collections" ("title", "accession_number", "acquired")
VALUES (NEW."title", NEW."accession_number", NEW."acquired");
END;
collections
, it inserts the row into the table.