現(xiàn)在我們已經(jīng)掌握了創(chuàng)建數(shù)據(jù)庫(kù)和表的基礎(chǔ)知識(shí),我們可以開始深入了解 SQL 交互的核心內(nèi)容:選擇、更新和刪除數(shù)據(jù)。
我們將從這些查詢的基本結(jié)構(gòu)開始,然后深入介紹強(qiáng)大的操作,并提供足夠的細(xì)節(jié),讓您感到危險(xiǎn)。
從表中選擇數(shù)據(jù)
如前所述,SQL 操作具有相當(dāng)嚴(yán)格的操作順序,子句必須遵守這些操作順序才能進(jìn)行有效的查詢。我們首先剖析一個(gè)常見(jiàn)的SELECT陳述:
SELECT column_name_1, column_name_2 FROM schema_name.table_name WHERE column_name_1 = "Value";
這可能是最常見(jiàn)的SELECT查詢結(jié)構(gòu)。首先,我們列出要選擇的列的名稱,并用逗號(hào)分隔。要接收所有列,我們可以簡(jiǎn)單地說(shuō)SELECT *。
這些列需要來(lái)自某個(gè)地方,因此我們指定接下來(lái)要引用的表。FROM table_name它可以采用(non-PostgreSQL) 或(PostgreSQL)的形式FROM schema_name.table_name。理論上,這里的分號(hào)會(huì)產(chǎn)生有效的查詢,但我們通常希望選擇滿足特定條件的行。
這就是該WHERE子句的用武之地:僅返回條件返回“ true”WHERE的行。在上面的示例中,我們驗(yàn)證字符串是否完全匹配"Value"。
僅選擇不同的值
通常有用的方法是在列中選擇不同的值。換句話說(shuō),如果某個(gè)值存在于 100 行中的同一列中,則運(yùn)行DISTINCT查詢只會(huì)向我們顯示該值一次。這是查看專欄獨(dú)特內(nèi)容的好方法,而無(wú)需深入研究所述值的分布。其效果類似于美國(guó)參議院或選舉團(tuán):忘記群眾,支持懷俄明州 2020:
SELECT DISTINCT column_name FROM table_name;
抵消和限制查詢中的結(jié)果
OFFSET選擇數(shù)據(jù)時(shí),和的組合LIMIT有時(shí)很關(guān)鍵。如果我們從一個(gè)包含數(shù)十萬(wàn)行的數(shù)據(jù)庫(kù)中進(jìn)行選擇,我們會(huì)浪費(fèi)大量的系統(tǒng)資源來(lái)一次獲取所有行;相反,我們可以讓應(yīng)用程序或 API 對(duì)結(jié)果進(jìn)行分頁(yè)。
LIMIT后面跟著一個(gè)整數(shù),本質(zhì)上表示“返回不超過(guò) X 個(gè)結(jié)果”。
OFFSET后面還跟著一個(gè)整數(shù),它表示返回結(jié)果的數(shù)字起點(diǎn),又名:“返回第 X 個(gè)結(jié)果之后出現(xiàn)的所有結(jié)果:”
SELECT * FROM table_name LIMIT 50 OFFSET 0;
以上返回前 50 個(gè)結(jié)果。如果我們想在應(yīng)用程序端構(gòu)建分頁(yè)結(jié)果,我們可以像這樣構(gòu)建查詢:
from SQLAlchemy import engine, session # Set up a SQLAlchemy session Session = sessionmaker() engine = create_engine('sqlite:///example.db') Session.configure(bind=engine) sess = Session() # Appication variables page_number = 3 page_size = 50 results_subset = page_number * results limit # Query session.query(TableName).limit(page_size).offset(results_subset)
這樣的應(yīng)用程序可以在page_number用戶每次單擊下一頁(yè)時(shí)遞增 1,然后適當(dāng)?shù)匦薷奈覀兊牟樵円苑祷叵乱豁?yè)結(jié)果。
另一個(gè)用途OFFSET可能是從失敗的腳本停止的地方繼續(xù)。如果我們將整個(gè)數(shù)據(jù)庫(kù)寫入 CSV 并遇到失敗。我們可以通過(guò)設(shè)置OFFSET等于 CSV 中的行數(shù)來(lái)繼續(xù)腳本停止的地方,以避免再次運(yùn)行整個(gè)腳本。
對(duì)結(jié)果進(jìn)行排序
現(xiàn)在最后要考慮的是使用ORDER BY子句對(duì)結(jié)果進(jìn)行排序。我們可以按任何指定的列對(duì)結(jié)果進(jìn)行排序,并說(shuō)明我們希望結(jié)果升序 ( ASC) 還是降序 ( DESC):
SELECT * FROM schema_name.table_name WHERE column_name_1 = "Value" ORDER BY updated_date DESC LIMIT 50 OFFSET 10;
復(fù)雜的 SELECT 語(yǔ)句
當(dāng)然,我們可以選擇具有WHERE比精確匹配更深入的邏輯的行。這些操作中最通用的操作之一是LIKE.
將正則表達(dá)式與 LIKE 一起使用
LIKE可能是選擇具有字符串值的列的最強(qiáng)大方法。有了LIKE,我們可以利用正則表達(dá)式來(lái)構(gòu)建高度復(fù)雜的邏輯。讓我們從我最喜歡的一些開始:
SELECT * FROM people WHERE name LIKE "%Wade%";
傳遞一個(gè)LIKE兩邊都有百分號(hào)的字符串本質(zhì)上是一個(gè)“包含”語(yǔ)句。%相當(dāng)于通配符,因此%無(wú)論該人的名字、中間名還是姓氏是Wade ,放在字符串的任一側(cè)都會(huì)返回 true 。查看其他有用的組合%:
a%:查找以“a”開頭的任何值。
%a:查找以“a”結(jié)尾的任何值。
%or%:查找任意位置有“or”的值。
_r%:查找第二個(gè)位置有“r”的任何值。
a_%_%:查找以“a”開頭且長(zhǎng)度至少為 3 個(gè)字符的任何值。
a%o: 查找以“a”開頭并以“o”結(jié)尾的任何值。
尋找不相似的值
的相反LIKE當(dāng)然是NOT LIKE,它運(yùn)行相同的條件,但返回相反的真/假值LIKE:
SELECT * FROM people WHERE name NOT LIKE "%Wade%";
帶有日期時(shí)間列的條件
日期時(shí)間列對(duì)于選擇數(shù)據(jù)非常有用。MONTH(column_name)與普通字符串不同,我們可以分別使用、DAY(column_name)和輕松從 DateTime 中提取月、日和年的數(shù)值YEAR(column_name)。例如,MONTH()在包含 DateTime 的列上使用2019-01-26 05:42:34將返回1,即一月。由于值以整數(shù)形式返回,因此在日期范圍內(nèi)查找結(jié)果很簡(jiǎn)單:
SELECT * FROM posts WHERE YEAR(created_at) < 2018;
查找具有 NULL 值的行
NULL是一種特殊的數(shù)據(jù)類型,本質(zhì)上表示“某些東西的缺失”,因此沒(méi)有條件永遠(yuǎn)不會(huì)equal NULL。相反,我們找到包含以下值的行IS NULL:
SELECT * FROM posts WHERE author IS NULL;
對(duì)于熟悉驗(yàn)證數(shù)據(jù)類型的人來(lái)說(shuō),這應(yīng)該不會(huì)感到驚訝。
當(dāng)然,相反的情況是NOT NULL:
SELECT * FROM posts WHERE author IS NOT NULL;
插入數(shù)據(jù)
查詢INSERT創(chuàng)建一個(gè)新行,并且相當(dāng)簡(jiǎn)單:我們聲明要插入數(shù)據(jù)的列,然后是要插入到所述列中的值:
INSERT INTO table_name (column_1, column_2, column_3) VALUES ("value1", "value2", "value3");
許多事情都可能導(dǎo)致插入失敗。其一,值的數(shù)量必須與我們指定的列數(shù)相匹配;如果我們不這樣做,我們要么提供了太少的值,要么提供了太多的值。
其次,vales 必須尊重列的數(shù)據(jù)類型。如果我們嘗試將整數(shù)插入到DateTime列中,我們將收到錯(cuò)誤。
最后,我們必須考慮表的鍵和約束。如果存在指定某些列不能為空或必須唯一的鍵,則也必須遵守這些鍵。
作為一個(gè)速記技巧,如果我們將值插入到表的所有列中,我們可以跳過(guò)顯式列出列名稱的部分:
INSERT INTO table_name VALUES ("value1", "value2", "value3");
以下是使用真實(shí)數(shù)據(jù)的插入查詢的快速示例:
INSERT INTO friends (id, name, birthday) VALUES (1, 'Jane Doe', '1990-05-30');
更新記錄:基礎(chǔ)知識(shí)
更新行是事情變得有趣的地方。我們可以做的事情有很多,所以讓我們繼續(xù)努力:
UPDATE table_name SET column_name_1 = 'value' WHERE column_name_2 = 'value';
這很簡(jiǎn)單:行中與我們的條件匹配的列的值。請(qǐng)注意,SET總是出現(xiàn)在 之前WHERE。這是對(duì)真實(shí)數(shù)據(jù)的相同查詢:
UPDATE celebs SET twitter_handle = '@taylorswift13' WHERE id = 4;
更新記錄:有用的邏輯
使用 CONCAT 連接字符串
您會(huì)發(fā)現(xiàn)根據(jù)行中已存在的數(shù)據(jù)更新行是常見(jiàn)的做法:換句話說(shuō),清理或修改數(shù)據(jù)。一個(gè)很棒的字符串運(yùn)算符是CONCAT(). CONCAT("string_1", "string_2")將連接傳遞給單個(gè)字符串的所有字符串。
CONCAT()下面是一個(gè)結(jié)合使用NOT LIKE來(lái)確定哪些帖子摘錄不以標(biāo)點(diǎn)符號(hào)結(jié)尾的真實(shí)示例。如果摘錄不以標(biāo)點(diǎn)符號(hào)結(jié)尾,我們?cè)谀┪蔡砑右粋€(gè)句點(diǎn):
UPDATE posts SET custom_excerpt = CONCAT(custom_excerpt, '.') WHERE custom_excerpt NOT LIKE '%.' AND custom_excerpt NOT LIKE '%!' AND custom_excerpt NOT LIKE '%?';
使用替換
REPLACE()在 SQL 中的工作原理與在幾乎所有編程語(yǔ)言中的工作原理一樣。我們傳遞REPLACE()三個(gè)值:
要修改的字符串。
字符串中將被替換的子字符串。
更換的價(jià)值。
我們可以用 做很多聰明的事情REPLACE()。這是一個(gè)更改博客文章的特色圖像以包含“視網(wǎng)膜圖像”后綴的示例:
UPDATE posts SET feature_image = REPLACE(feature_image, '.jpg', '@2x.jpg');
場(chǎng)景:基于日期的文件夾結(jié)構(gòu)
前幾天,我在處理涉及更改 CDN 的噩夢(mèng)情況時(shí)遇到了一個(gè)有趣的練習(xí)。它涉及我們迄今為止所回顧的所有內(nèi)容,并很好地說(shuō)明了僅使用 SQL 即可實(shí)現(xiàn)的目標(biāo)。
為數(shù)百個(gè)帖子移動(dòng)數(shù)百?gòu)垐D像的挑戰(zhàn)來(lái)自于文件結(jié)構(gòu)的形式。Ghost 喜歡將圖像保存在過(guò)時(shí)的文件夾結(jié)構(gòu)中,例如2019/02/image.jpg。我們之前的 CDN 根本不遵守這一點(diǎn),因此將所有圖像轉(zhuǎn)儲(chǔ)在一個(gè)文件夾中。不理想。
值得慶幸的是,我們可以利用帖子的元數(shù)據(jù)來(lái)辨別此文件結(jié)構(gòu)。由于圖像是在創(chuàng)建帖子時(shí)添加到帖子中的,因此我們可以使用帖子表中的created_at列來(lái)找出正確的日期文件夾:
UPDATE postsSET feature_image = CONCAT("https://cdn.example.com/posts/", YEAR(created_at), "/", LPAD(MONTH(created_at), 2, '0'), "/", SUBSTRING_INDEX(feature_image, '/', - 1) );
讓我們分解一下我們的內(nèi)容CONCAT:
https://cdn.example.com/posts/:我們新 CDN 的基本 URL。
YEAR(created_at):從我們的帖子創(chuàng)建日期中提取年份(對(duì)應(yīng)于文件夾)。
LPAD(MONTH(created_at), 2, '0'):使用MONTH(created_at)返回前幾個(gè)月的一位數(shù),但我們的文件夾結(jié)構(gòu)希望始終有兩位數(shù)的月份(即:2018/01/而不是2018/1/)。我們可以使用LPAD()此處“填充”日期,以便月份始終為兩位數(shù)長(zhǎng),較短的日期將用數(shù)字 0 填充。
SUBSTRING_INDEX(feature_image, '/', - 1):我們通過(guò)查找現(xiàn)有圖像 URL 中最后一個(gè)斜杠后面的所有內(nèi)容來(lái)獲取每個(gè)帖子圖像的文件名。
現(xiàn)在每個(gè)圖像的結(jié)果將如下所示:文章來(lái)源:http://www.zghlxwxcb.cn/article/575.html
https://cdn.example.com/posts/2018/02/image.jpg
刪除記錄
讓我們以最后一種類型的查詢(刪除行)結(jié)束今天的內(nèi)容:文章來(lái)源地址http://www.zghlxwxcb.cn/article/575.html
DELETE FROM celebs WHERE twitter_handle IS NULL;
到此這篇關(guān)于從零開始學(xué)習(xí)SQL數(shù)據(jù)庫(kù)操作:選擇、更新和刪除數(shù)據(jù)的文章就介紹到這了,更多相關(guān)內(nèi)容可以在右上角搜索或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!