1. 事前準備
您之前已學到如何將網路資料整合到應用程式,也瞭解如何使用協同程式處理並行工作。在本課程中,您將瞭解 Android 開發的另一項基本技能,協助您打造優質應用程式:持續性。就算您從未聽過這個詞,也可能在先前使用應用程式時體驗過持續性。從編寫購物清單,到在相簿應用程式中捲動瀏覽數年前的相片,或是暫停和繼續遊戲,應用程式都是利用持續性提供流暢的使用者體驗。儘管使用者很容易將這些功能視為理所當然,但保存資料是開發人員打造優質應用程式的重要技能。
在本單元的後續章節中,您將進一步瞭解 Android 的持續性,以及名為「Room」的程式庫,其可讓應用程式從資料庫進行讀取及寫入。不過,在開始探索 Android 的持續性之前,必須先熟悉關聯資料庫的基本概念,以及如何使用 SQL (簡稱結構化查詢語言) 讀取及操控資料。如果您已熟悉這些概念,不妨將本課程當做複習,確保在您學習 Room 時,這些概念可以記憶猶新。如果還不熟悉,完全沒有問題!我們現在並不預期您對資料庫有任何的瞭解。完成本程式碼研究室後,您將具備開始學習在 Android 應用程式中使用資料庫所需的所有基礎知識。
必要條件
- 在 Android Studio 中瀏覽專案。
課程內容
- 關聯資料庫的結構:資料表、資料欄和資料列
- 包含
WHERE
、ORDER BY
、GROUP BY
和LIMIT
子句的SELECT
陳述式 - 如何使用 SQL 插入、更新及刪除資料列
軟硬體需求
- 已安裝 Android Studio 的電腦。
2. 關聯資料庫總覽
什麼是關聯資料庫?
在運算中,資料庫只是可用電子方式存取和寫入的結構化資料集合。資料庫可使用 Kotlin 儲存任何可在應用程式中呈現的相同資訊。在行動裝置上,資料庫通常會用於儲存執行中應用程式的資料,如此一來,下次開啟應用程式時即可存取該資料,而無須從網際網路等其他來源擷取資料。這就是所謂的資料持續性。
談論資料持續性時,通常會聽到「關聯資料庫」一詞。關聯資料庫是一種常見的資料庫,可將資料整理成資料表、資料欄和資料列。編寫 Kotlin 程式碼時,您會建立代表物件的類別。關聯資料庫中的資料表運作方式相同。除了呈現資料之外,資料表也可以參照其他資料表,以便為資料表建立關聯。常見的範例包括「學生」、「老師」和「課程」資料表。課程可能只有一位老師,但學生可能會有許多課程。資料庫可呈現這些資料表之間的關係,因此您會經常聽到「關聯資料庫」一詞。
資料表間的關係可呈現實際關係。
資料表、資料欄和資料列
建立關聯資料庫的第一步就是定義資料表,或您要呈現的資料。您也必須思考各資料表中要儲存哪些特定資訊。特定屬性會以資料欄表示。資料欄是由名稱和資料類型所組成。您已經熟悉在 Kotlin 中使用類別的屬性。您也可以用相同方式看待 SQL 資料表。資料表就如同類別定義,用於說明您想要呈現的「內容」類型。資料欄是資料表中每個項目所建立「內容」的特定屬性。
植物 | ||
id | 整數 | |
物種 | 文字 (字串) | |
名稱 | 文字 (字串) | |
顏色 | 文字 (字串) |
花園 | ||
id | 整數 | |
名稱 | 文字 (字串) | |
長度 | 整數 | |
寬度 | 整數 |
個別資料表項目稱為資料列。這就如同 Kotlin 中類別的執行個體。每個列的資料都會與各個欄相對應。資料表會提供範本,但這些資料列會定義資料表中實際儲存的資料。
id | 物種 | 名稱 | 顏色 |
1 | Camellia Sinensis | 茶樹 | 綠色 |
2 | Echinacea Purpurea | 紫錐花 | 紫色 |
3 | Ferula Foetida | 阿魏草 | 綠色 |
主鍵
在上述範例中,請留意 id 屬性的資料欄形式。自然界中的植物物種或您在資料庫中呈現的任何內容,可能沒有方便編號的 id,但資料表中的資料列必須具有某種唯一識別碼。這通常稱為主鍵,而且資料表中的每一列皆不得重複。當您需要參照其他資料表內其中一個資料表的資料列時,這項功能就非常實用。舉例來說,還有另一個名為「花園」的表格,您希望將花園與其中所有的植物物種建立關聯。您可以使用植物資料表中的主鍵,從花園資料表中的項目或資料庫中的其他資料表參照植物。
主鍵可在關聯資料庫中建立關係。在本課程中,雖然您不會使用含有多個資料表的資料庫,但具有專屬 ID 可協助查詢、更新及刪除資料表中的現有項目。
資料類型
如同定義 Kotlin 類別的屬性,資料庫中的資料欄可為多種資料類型的其中一種。資料欄可以呈現半形字元、字串、數字 (無論是否包含小數),或二進位資料。根據用途,其他資料 (例如日期和時間) 可以用數字或字串表示。使用 Room 時,您主要使用的是 Kotlin 類型,但是其會對應至幕後的 SQL 類型。
SQL
無論是自行或使用 Room 等程式庫存取關聯資料庫,您都需要使用 SQL。
什麼是 SQL?SQL (有時發音為「sequel」) 代表結構化查詢語言,可讓您讀取和操控關聯資料庫中的資料。但別擔心,您不需要為了在應用程式中實作持續性,而學習全新的程式設計語言。不同於 Kotlin 等程式設計語言,SQL 只包含幾種陳述式,用於讀取及寫入資料庫。瞭解基本格式後,只需在空格中填入您從資料庫讀取或寫入的特定資訊即可。
以下為最常見的 SQL 陳述式,以及您要使用的陳述式。
選取 | 從資料表取得特定資訊,並透過多種方式篩選及排序結果。 |
INSERT | 在資料表中新增資料列。 |
更新 | 更新資料表中的現有資料列。 |
刪除 | 從資料表中移除現有的資料列。 |
現在,您必須先具備資料庫,才能在 SQL 中執行任何操作。在下一個畫面中,您將會進行範例專案設定,其包含資料庫,可供您執行 SQL 查詢。
3. 範例程式碼 - Park 資料庫
將要下載的範例程式碼與先前程式碼研究室的程式碼略有不同。我們會提供簡易的 Android Studio 專案,其可建立資料庫讓您用於執行 SQL 查詢,而非在現有專案上進行建構。執行應用程式一次後,您即可使用名為資料庫檢查器的 Android Studio 工具存取資料庫。
如要取得本程式碼研究室的程式碼,並在 Android Studio 中開啟,請按照下列步驟操作。
取得程式碼
- 按一下上面顯示的網址。系統會在瀏覽器中開啟專案的 GitHub 頁面。
- 在專案的 GitHub 頁面中,按一下「Code」按鈕開啟對話方塊。
- 在對話方塊中,按一下「Download ZIP」按鈕,將專案儲存到電腦。等待下載作業完成。
- 在電腦中找到該檔案 (可能位於「下載」資料夾中)。
- 按兩下解壓縮 ZIP 檔案。這項操作會建立含有專案檔案的新資料夾。
在 Android Studio 中開啟專案
- 啟動 Android Studio。
- 在「Welcome to Android Studio」視窗中,按一下「Open an existing Android Studio project」。
注意:如果 Android Studio 已開啟,請依序選取「File」>「New」>「Import Project」選單選項。
- 在「Import Project」對話方塊中,前往解壓縮專案資料夾所在的位置 (可能位於「下載」資料夾中)。
- 按兩下該專案資料夾。
- 等待 Android Studio 開啟專案。
- 按一下「Run」按鈕 即可建構並執行應用程式。請確認應用程式的建構符合預期。
- 在「Project」工具視窗中瀏覽專案檔案,查看應用程式的設定方式。
在前往下一個部分之前,請先完成下列步驟,確保您已透過範例專案完成相關設定。
- 執行應用程式。應用程式應會顯示如下所示的單一畫面。
- 在 Android Studio 中,前往「View」>「Tool Windows」>「Database Inspector」,開啟資料庫檢查器。
- 您應該會在底部看到標示「資料庫檢查器」的新分頁標籤。載入可能需要幾秒鐘的時間,但您可在左側看到含有資料表的清單,您可選取以執行查詢。
4. 基本 SELECT 陳述式
針對下列練習,您必須在資料庫檢查器中執行查詢。請務必從左側窗格中選取正確的資料表 (park),然後按一下「Open New Query Tab」按鈕,接著就會看到可輸入 SQL 指令的文字方塊。
SQL 陳述式是用於存取 (讀取或寫入) 資料庫的指令,類似程式碼行。在 SQL 中,您可執行的最簡單操作為取得資料表中的所有資料。為此,請先使用 SELECT
字詞,代表您要讀取資料。接著加上星號 (*
)。您可以在此處指定要選取的資料欄,使用星號代表選取所有資料欄。然後使用 FROM
關鍵字,後面加上資料表的名稱 (park
)。在資料庫檢查器中執行下列指令,並觀察整個資料表的所有資料欄與資料列。
SELECT * FROM park
如果您只想選取特定資料欄,而非資料表中的所有資料欄,您可以指定資料欄名稱。
SELECT city FROM park
您也可以選取多個特定資料欄,並以半形逗號分隔。
SELECT name, established, city FROM park
有時候,您不一定要選取資料庫中的所有資料列。您可以新增 SQL 陳述式的子句部分,以進一步縮小結果範圍。
其中一個子句是 LIMIT
,可讓您限制傳回的資料列數量。因此,以下查詢只會傳回前五個,不會傳回全部 23 個結果。
SELECT name FROM park
LIMIT 5
最常見且實用的子句之一就是 WHERE
子句。WHERE
子句可讓您根據一或多個資料欄篩選結果。
SELECT name FROM park
WHERE type = "national_park"
此外,也有「不等於」(!=
) 運算子。以下查詢會列出不屬於 recreation_area
,且佔地超過 100,000 英畝的所有公園。使用 WHERE
子句時,您也可以使用 AND
或 OR
等布林值運算子,以新增多個條件。
SELECT name FROM park
WHERE type != "recreation_area"
AND area_acres > 100000
練習
SQL 查詢相當適合回答各種資料相關問題,而最佳的練習做法就是自行編寫查詢。接下來幾個步驟,您將可編寫查詢,以回答特定問題。請務必先在資料庫檢查器中進行測試,再繼續進行操作。
所有練習都會以先前章節的累積知識為基礎,並在程式碼研究室結束時提供逐步操作說明,以核對您的答案。
5. 常見的 SQL 函式
您編寫的第一個查詢只會傳回資料庫中的所有資料列。
SELECT * FROM park
不過,您可能不希望傳回冗長的結果清單。SQL 也提供匯總函式,可協助您將資料縮減為單一個有意義的值。舉例來說,假設您想知道 park
資料表中的資料列數。如果您不使用 SELECT * ...
,而是改用 COUNT()
函式並傳遞 *
(所有資料列) 或資料欄名稱,查詢將會傳回所有資料列的數量。
SELECT COUNT(*) FROM park
另一個實用的匯總函式為 SUM()
函式,可用於加總資料欄中的值。此查詢只會篩選國家公園 (由於這是 park_visitors
資料欄非空值的唯一項目),並將每個公園的總訪客數加總。
SELECT SUM(park_visitors) FROM park
WHERE type = "national_park"
值得注意的是,您仍然可以在空值中使用 SUM()
,但值將會視為零。以下查詢會傳回上述相同結果。但仍建議您盡量具體指定,以避免在應用程式中使用 SQL 時出現錯誤。
SELECT SUM(park_visitors) FROM park
除了匯總值之外,還提供其他實用的函式 (例如 MAX()
和 MIN()
),以分別取得最大或最小值。
SELECT MAX(area_acres) FROM park
WHERE type = 'national_park'
取得 DISTINCT 值
您可能會注意到,在某些資料列中,此欄與其他資料列的值相同。舉例來說,類型欄值的數量有限。您可以使用 DISTINCT
關鍵字,排除查詢結果中的重複值。例如,如要取得類型欄的所有不重複值,您可以使用以下查詢。
SELECT DISTINCT type FROM park
您也可以在匯總函式中使用 DISTINCT
,如此即可直接傳回計數,而不必列出不重複的 type
並自行計算。
SELECT COUNT(DISTINCT type) FROM park
練習
請花一些時間運用您所學到的知識,嘗試是否能編寫以下查詢。請務必使用資料庫檢查器驗證程式碼是否正常運作。
6. 排序及分組查詢結果
在先前的範例中,要找出特定項目並不容易。所幸,您也可以使用 ORDER BY
子句排序 SELECT
陳述式的結果。您可在 WHERE
子句後 (如果有) 的查詢結尾新增 ORDER BY
子句,並指定要做為排序依據的資料欄名稱。以下範例會取得資料庫中每個公園的名稱,並依字母順序排列結果。
SELECT name FROM park
ORDER BY name
根據預設,系統會按遞增順序排序結果,但您可以將 ASC
或 DESC
關鍵字新增至 Order by 子句,以遞增或遞減順序加以排序。您不需要指定 ASC
,因為第一個查詢會以遞增順序列出結果,但如要以遞減順序取得結果,請將 DESC
關鍵字新增至 ORDER BY
子句結尾。
SELECT name FROM park
ORDER BY name DESC
您還可以依資料欄將結果分組,以便閱讀。在 ORDER BY
子句 (如果有) 前,您可以視需要指定 GROUP BY
子句和資料欄。這會將結果分割為 GROUP BY
中資料欄特定的子集,每一欄的結果會根據查詢的其餘部分進行篩選及排序。
SELECT type, name FROM park
GROUP BY type
ORDER BY name
這最容易透過範例理解。您不需計算資料庫中的所有公園數量,即可得知每個類型的公園數量,並取得每種類型的個別計數。
SELECT type, COUNT(*) FROM park
GROUP BY type
ORDER BY type
練習
請花一些時間運用您所學到的知識,嘗試是否能編寫以下查詢。請務必使用資料庫檢查器驗證程式碼是否正常運作。
問題 4:針對排名前 5 的公園名稱,以及訪客最多的訪客計數,編寫 SQL 查詢 (依遞減順序)。
7. 插入及刪除資料列
您必須能夠寫入資料,才能使用 Room 完整發揮 Android 的持續性資料功能。除了查詢資料庫之外,您也可以使用 SQL 陳述式插入、更新及刪除資料列。稍後在課程 2 中學習使用 Room 寫入資料時,將會需要具備此基本知識。
INSERT 陳述式
如要新增資料列,請使用 INSERT
陳述式。INSERT
陳述式後方是 INTO
關鍵字,以及您想新增資料列的資料表名稱。在 VALUES 關鍵字之後,您必須將每個資料欄的值加上括號 (依序),每個值以半形逗號隔開。INSERT
陳述式的格式如下。
INSERT INTO table_name
VALUES (column1, column2, ...)
如要在 park
資料表中新增資料列,INSERT
陳述式將如下所示。這些值的順序會和為 park
資料表所定義的資料欄順序相符。請注意,系統不會指定部分資料。這目前沒關係,在您插入資料列後,可隨時更新資料列。
INSERT INTO park
VALUES (null, 'Googleplex', 'Mountain View', 12, null, 0, '')
另請注意,您針對 ID 傳入 null
。雖然可以提供特定編號,但這實際上並不便利,因為應用程式必須追蹤最新的 ID,確保無重複。但是,您可以設定資料庫,使主鍵自動增加,如此處操作。如此一來,當您傳入 null
時,系統就會自動選擇下一個 ID。
確認已使用 WHERE
子句建立項目,以指定名為 "Googleplex"
的公園。
SELECT * FROM park
WHERE name = 'Googleplex'
UPDATE 陳述式
資料列建立後,您可以隨時變更其中的內容。使用 UPDATE
陳述式即可進行變更。如同您看過的所有其他 SQL 陳述式,您必須先指定資料表名稱。在 SET
子句中,只需設定您想要變更為新值的資料欄即可。
UPDATE table_name
SET column1 = ...,
column2 = ...,
...
WHERE column_name = ...
...
如果是 Googleplex 項目,系統會更新現有的屬性,並在其他欄位填入內容 (這些欄位先前具有值,但為空白字串 ""
)。您可以使用 UPDATE
陳述式一次更新多個 (或全部) 欄位。
UPDATE park
SET area_acres = 46,
established = 1088640000,
type = 'office'
WHERE name = 'Googleplex'
查看查詢結果中反映的更新
SELECT * FROM park
WHERE name = 'Googleplex'
DELETE 陳述式
最後,您也可以使用 SQL 指令刪除資料庫中的資料列。請再次指定資料表名稱,如同使用 SELECT
陳述式,您可以使用 WHERE
子句為要刪除的資料列提供條件。由於 WHERE
子句可以比對多個資料列,因此只需一個指令就能刪除多個資料列。
DELETE FROM table_name
WHERE <column_name> = ...
由於 Googleplex 並非國家公園,請嘗試使用 DELETE
陳述式從資料庫中移除此項目。
DELETE FROM park
WHERE name = 'Googleplex'
進行驗證,確認使用 SELECT
陳述式刪除資料列。查詢不應傳回任何結果,代表所有名為「Googleplex」的資料列都已成功刪除。
SELECT * FROM park
WHERE name = 'Googleplex'
這些就是插入、更新及刪除資料的所有步驟。您只需瞭解要執行 SQL 指令的格式,並指定符合資料庫中資料欄的值。我們在下一個程式碼研究室中介紹 Room 時,重點在於從資料庫進行讀取。插入、更新及刪除資料的步驟將於課程 2 中詳細說明。
8. 要練習的解決方案
希望這些練習能幫助您增進自己對 SQL 概念的瞭解。如果無法順利解決或想查看答案,歡迎參閱下方答案
問題 1:請編寫 SQL 查詢,取得訪客少於 1,000,000 人的所有公園名稱。
此問題要求訪客人數少於 1,000,000 人的公園名稱 (單一資料欄),您可以在 WHERE
子句中指定。
SELECT name FROM park
WHERE park_visitors < 1000000
問題 2:請編寫 SQL 查詢,取得 park
資料表中不同城市的數量
您可使用 COUNT()
函式計算資料欄總數,但因為只需要不同城市 (因為有些城市有多個公園),您可以在 COUNT()
函式的資料欄名稱前使用 DISTINCT
關鍵字。
SELECT COUNT(DISTINCT city) FROM park
問題 3:請編寫 SQL 查詢,取得臺北市公園的總訪客數。
您可以使用 SUM()
函式計算訪客總數。此外,您也需要使用 WHERE
子句指定位於臺北市的公園。
SELECT SUM(park_visitors) FROM park
WHERE city = "San Francisco"
問題 4:針對排名前 5 的公園 (僅限名稱),以及訪客最多的訪客計數,編寫 SQL 查詢 (依遞減順序)。
查詢必須同時取得 name 和 park_visitors 資料欄。這些結果在 park_visitors
資料欄中會使用 ORDER BY
子句遞減排序。由於您不希望將結果分組至另一個資料欄中,並在此類分組中進行排序,因此不必使用 GROUP BY
子句。
SELECT name, park_visitors FROM park
ORDER BY park_visitors DESC
LIMIT 5
9. 恭喜
摘要:
- 關聯資料庫可讓您儲存資料、將資料整理成資料表、資料欄和資料列。
- 您可以使用 SQL
SELECT
陳述式從資料庫擷取資料。 - 您可以在
SELECT
陳述式中使用各種子句,包括WHERE
、GROUP BY
、ORDER BY
和LIMIT
,使查詢條件更具體。 - 您可以使用匯總函式,將多個資料列中的資料合併至單一資料欄。
- 您可以分別使用 SQL
INSERT
、UPDATE
和DELETE
陳述式,在資料庫中新增、更新及刪除資料列。