以下的主要框架和內容都來自以上博主的文章,我主要是做一下記錄并且補充自己的理解。
SQL考察的方式
筆試,網上答題。在網上看面經的時候,網易,PDD之類的都會出現SQL來作為篩選條件,Sql是數據分析崗的必備技能之一。但有時候這些筆試題真的好變態,只能多練一些真題進行準備了。有些較難的題可以參考之前的這篇博文:面向業務的mysql筆試題筆記
現場面試寫。
在面試小紅書,網易的時候,面試官也會現場抽出幾道SQL題讓你寫代碼。現場寫代碼不會遇到太難的題目,一般窗口函數排個序可以搞定,再難點也就是求中位數、眾數這些。
- 遠程面試口述語法。我一直覺得口述代碼就是反人類的。。。
像蘑菇街,趣頭條,招銀網絡科技等等,是在遠程面試(或者電面)的時候隨便問起SQL常見的語法。以我的經驗來看,電面的考察側重于提問相似函數的不同點,畢竟讓你口述一段代碼也不現實……
我學習sql的路徑:
- 在B站上找了一個播放量高的mysql的視頻,邊看邊敲,對各自語法都有了一個大概的認識。
- 看《SQL必知必會》,對mysql有了比較清晰的認識。
- 然后就跑去leetcode刷題,發現自己太天真了,有好多語法都沒見過(窗口函數、定義變量、定義函數等等),花了一個月刷完之后感覺自己學到了很多,但還是需要反復鞏固,有些題目就算當時會了,現在返回去看也可能還是不太會。(吐槽一下牛客網上的數據庫題雖然我也做了,但是感覺題目質量真的好差啊,有些題目都都不明白,刷題體驗也是極差的)。
下面談一下我對以下各個問題的理解:
- 各種連接方式的區別?
[inner] join :內連接, 根據兩個表進行匹配,只有兩個表的記錄都滿足條件才會完成匹配。
left [outer] join/right [outer] join/all [outer] join:以左連接為例,與右表按照條件進行匹配,左表的所有記錄都會保留,右表中不滿足條件的記錄以空值NULL的形式進行返回。
cross join: 交叉連接,也叫作笛卡爾乘積。就是左右兩表所有可能的組合。以下方式也能實現。
select *
from a, b
索引的作用?
創建索引可以大大提高系統的性能。
- 通過創建唯一性索引,可以保證數據庫表中每一行數據的唯一性
- 可以大大加快 數據的檢索速度,這也是創建索引的最主要原因。
- 可以加速表與表之間的連接,特別是實現數據的參考完整性方面特別有意義。
數據完整性(Data Integrity)是指數據的精確性(Accuracy) 和可靠性(Reliability)。它是應防止數據庫中存在不符合語義規定的數據和防止因錯誤信息的輸入輸出造成無效操作或錯誤信息而提出的。
- 在分組和排序,用子句進行檢索時,同樣可以顯著減少查詢中 排序的時間。
- 通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。
索引主要建立在:
- 經常搜索的列
- 主鍵所在的列
- 外鍵所在的列
SQL 索引的作用(真的是超詳細)
聚集索引:
- 聚集索引將數據行的鍵值在表內排序并存儲對應的數據記錄,使得數據表物理順序與索引順序一致。
- 這意味著不論聚集索引里有表的哪個(或哪些)字段,這些字段都會按順序地被保存在表中。由于存在這種排序,所以每個表只會有一個聚集索引。
非聚集索引:
- 非聚集索引完全獨立于數據行的結構。
- 一個表中最多只能有一個聚集索引,但可有一個或多個非聚集索引。當在SQLServer上創建索引時,可指定是按升序還是降序存儲鍵。
語法格式:
CREATE[ UNIQUE ] /*指定索引是否唯一*/
[ CLUSTERED | NONCLUSTERED ] /*索引的組織方式*/
INDEX <索引名>
ON<表名>( <列名> [ ASC | DESC ] [ ,...n ] ) /*索引定義的依據*/
[ WHERE <列名> IN (<篩選值>,…)
| <列名><篩選謂詞> <篩選值>] /*篩選索引*/
[WITH ( <索引選項> [ ,...n ] ) ] /*索引選項*/
[ ; ]
排名函數與排序函數
如果以下五個同學的分數分別為:100、99、99、98
- ROW_NUMBER():順序排序——1、2、3、4
- RANK():并列排序,跳過重復序號——1、2、2、4
- DENSE_RANK():并列排序,不跳過重復序號——1、2、2、3
on與where的區別
SQL中過濾條件放在on和where中的區別
我以前對于where和on的理解是這樣的:
sql92語法是沒有 join on 的 是通過這樣連接的
select *
from a, b
where a.id = b.id
sql96語法中可以使用join on 是這樣連接的
select *
from a
join b
on a.id = b.id
我以前認為on后邊就是放置連接的條件的,而剩余的篩選條件則要放在where中,這樣的理解是沒有問題的,但是還是有些淺?。?/p>
join過程可以這樣理解:首先兩個表做一個笛卡爾積,on后面的條件是對這個笛卡爾積做一個過濾形成一張臨時表,如果沒有where就直接返回結果,如果有where就對上一步的臨時表再進行過濾。
因此,
對于內連接, where和on是沒有區別的。
對于外連接,以左連接為例:on是在生成臨時表時使用的條件,無論on的條件是否為真,都會返回左邊表中的全部記錄。
而where條件是在臨時表生成好后,再對臨時表進行過濾的條件。這時已經沒有left join的含義(必須返回左邊表的記錄)了,條件不為真的就全部過濾掉。
如何連接多個select子句:
并集:union / union all , 前者不允許重復值,后者允許重復值,而且合并的兩個數據需要有相同的列以及數據類型。
一般來說如果select 字段大于1個,用union all比用union速度快,因為union 會將多個結果中重復的數據合并,union all則是直接合并
交集:Intersect
差集:minus
從t1表的查詢中返回不同于t2表查詢結果中的值。
SELECT id FROM t1
MINUS
SELECT id FROM t2;
主鍵和外鍵
主鍵(PRIMARY KEY)是一張表中能夠確定一條記錄的唯一標志(數據庫中的一條記錄中有若干個屬性,若其中某一個屬性組(注意是組)能唯一標識一條記錄,該屬性組就可以成為一個主鍵 ),比如身份證號。
CREATE TABLE users(
user_id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(40),
password VARCHAR(255),
email VARCHAR(255)
);
外鍵用于和另一張表進行關聯。例如,A字段是A表的主鍵,那么出現在B表中的A字段能夠作為B表的外鍵,實現A,B表的連接查詢。
MySQL外鍵簡介
CONSTRAINT constraint_name
FOREIGN KEY foreign_key_name (columns)
REFERENCES parent_table(columns)
ON DELETE action
ON UPDATE action
向表中插入數據
刷牛客網的時候有注意到如下幾個語法:
普通插入模式
INSERT INTO tablename VALUES(...);
如果不存在(根據主鍵或者唯一索引判斷)則插入,如果存在則忽略
INSERT OR IGNORE INTO tablename VALUES(...);
如果不存在(根據主鍵或者唯一索引判斷)則插入,如果存在則替換
INSERT OR REPLACE INTO tablename VALUES(...);
刪除表中的數據
delete : 刪除表中數據,可以指定具體數據(where)
drop column和drop table: 刪除列數據,與delete 不同,drop函數會將數據以及表的結構全部刪除。
truncate: 僅刪除數據(保留數據結構),且默認刪除所有數據。和delete不同,truncate不能用where進行篩選,但刪除速度比delete快
字符串常見操作函數?
concat(): 將多個字符串連接成一個字符串,連接符用“”包起來
concat_ws():代表 CONCAT With Separator ,是CONCAT()的特殊形式。 第一個參數是其它參數的分隔符。分隔符的位置放在要連接的兩個字符串之間。分隔符可以是一個字符串,也可以是其它參數。如果分隔符為 NULL,則結果為 NULL。函數會忽略任何分隔符參數后的 NULL 值。
group concat(): 將group by產生的同一個分組中的值連接起來,返回一個字符串。
like(): 模糊查詢,需要與通配符一起使用('%'代表任意字符出現任意次數;'_'僅能匹配單個字符)
substr(): substr(string string,num start,num length); 用于從字段中提取相應位置的字符。
regexp() : 正則表達式匹配函數
-- 查找name字段中以元音字符開頭或以'ok'字符串結尾的所有數據:
WHERE name REGEXP '^[aeiou]|ok$';
In/exist的聯系與區別
- 當進行連接的兩個表大小相似,效率差不多;
- 當主表比從表大時,IN查詢的效率較高;
- 當從表比主表大時,EXISTS查詢的效率較高;
原因如下:
i- n是先執行子查詢,得到一個結果集,將結果集代入外層謂詞條件執行主查詢,子查詢只需要執行一次 - exists是先從主查詢中取得一條數據,再代入到子查詢中,執行一次子查詢,判斷子查詢是否能返回結果,主查詢有多少條數據,子查詢就要執行多少次
Exist的原理:使用exist時,若子查詢能夠找到匹配的記錄,則返回true,外表能夠提取查詢數據;使用 not exist 時,若子查詢找不到匹配記錄,則返回true,外表能夠提取查詢數據。
常用的時間函數
很多sql題目的考察都是基于業務的,比如每日新用戶數目、每日留存率這都涉及到一個我們避不開的量就是時間,但是時間有著不同的格式,有時候是年月日時間、有時候是年月日,有時候是時間戳。我們需要將其轉化為合理的格式進行運用。
比如我在猿輔導的筆試中就遇到了時間戳的轉化:
now(): 獲取當前日期+時間
current_timestamp():獲取當前時間戳
date_format(date,format)或time_format(time,format): 將時間或者日期轉化為字符串
str_to_date(str, format):將字符串轉化為日期
unix_timestamp(date):獲取時間戳
from_unixtime(unix_timestamp):時間戳轉為時間
from_unixtime(unix_timestamp,format):時間戳格式化
date_add(dt, interval 1 day):為日期增加一個時間間隔
date_sub(dt, interval 1 day):為日期減去一個時間間隔
datediff(date1,date2):計算兩個時間之差
注意:timediff(time1,time2) 函數的兩個參數類型必須相同。
時間戳(timestamp)轉換、增、減函數:
timestamp(date) -- date to timestamp
timestamp(dt,time) -- dt + time
timestampadd(unit,interval,datetime_expr) --
timestampdiff(unit,datetime_expr1,datetime_expr2) --
格式化的格式:
%M 月名字(January……December)
%W 星期名字(Sunday……Saturday)
%D 有英語前綴的月份的日期(1st, 2nd, 3rd, 等等。)
%Y 年, 數字, 4 位
%y 年, 數字, 2 位
%a 縮寫的星期名字(Sun……Sat)
%d 月份中的天數, 數字(00……31)
%e 月份中的天數, 數字(0……31)
%m 月, 數字(01……12)
%c 月, 數字(1……12)
%b 縮寫的月份名字(Jan……Dec)
%j 一年中的天數(001……366)
%H 小時(00……23)
%k 小時(0……23)
%h 小時(01……12)
%I 小時(01……12)
%l 小時(1……12)
%i 分鐘, 數字(00……59)
%r 時間,12 小時(hh:mm:ss [AP]M)
%T 時間,24 小時(hh:mm:ss)
%S 秒(00……59)
%s 秒(00……59)
%p AM或PM
%w 一個星期中的天數(0=Sunday ……6=Saturday )
%U 星期(0……52), 這里星期天是星期的第一天
%u 星期(0……52), 這里星期一是星期的第一天