# SQL Server 語法 ###### tags: `SQL` `自學` `程式語言` [TOC] ## SELECT ### 從資料表中取得整列資料 - SELECT 命令也稱為「查詢」或「Query」。 - SELECT 命令會從資料表中==選擇==資料,是SQL中最基本的命令。 ```sql= SELECT * FROM [資料表名稱]; ``` >若想取得資料表中**所有欄位**的資料時,可以用「*」符號來指定。 - 利用**半形逗號**「,」指定多個欄位或資料表。 ```sql= SELECT 欄位名, 欄位名,... FROM [資料表名稱, 資料表名稱,...]; ``` ### 決定要取得的欄位 - SELECT之後,接著寫想要取得的欄位名。 ```sql= SELECT [ALL|DISTINCT] 欄位名 FROM [資料表名稱]; ``` - 指定DISTINCT ```sql= SELECT DISTINCT 欄位名 FROM [資料表名稱]; ``` >表中若有內容重複的資料列並非好事,可能會造成查詢資料時,得到重複的資料;因此,會使用「DISTINCT」除去重複的資料。 - 指定前N筆資料 ```sql= SELECT TOP 數字 * FROM [資料表名稱]; ``` ### FROM 子句 :::info 決定要查詢的資料表。 ::: > 在FROM 子句中需要寫上資料表名:table_name - 資料表的別名:table_alias ```sql= SELECT * FROM [資料表名稱] [別名1], [資料表名稱] [別名2]; WHERE 別名1.A = 別名2.A ``` - 用AS設定別名(可以省略) ```sql= SELECT * FROM [資料表名稱] AS [別名1], [資料表名稱] AS [別名2]; WHERE 別名1.A = 別名2.A ``` - 寫在FROM 子句裡的==結合條件== > 分為內結合(INNER JOIN)及外結合(LEFT/RIGHT JOIN)。 > 條件式則皆在「ON」之後寫, >> LEFT與RIGHT的差別在於,要讓左側資料表的資料留下,還是右側資料表的資料。 [![圖片](https://www.dofactory.com/img/sql/sql-inner-join.png "INNER JOIN")](https://www.dofactory.com/sql/inner-join) `範例`:資料表 A 和 B ,以 A.t = B.t 的條件式進行內結合 ```sql= SELECT * FROM A INNER JOIN B ON A.t = B.t; ``` > 3個(含)以上的資料表結合 >> 用**括號**將**INNER JOIN... ON...** 括號起來,使他被當成是一個資料表並進行結合。 `範例`:將資料表 A、B 和 C 結合 ```sql= SELECT * FROM (A INNER JOIN B ON A.t = B.t) INNER JOIN C ON A.t = C.t; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/tZVwKM6.png) ```sql= select sum(city.population) from city join country on city.countrycode = country.code where continent = 'Asia'; ``` ![](https://i.imgur.com/C6ZKTno.png) `選自 HackerRank 範例`: ![](https://i.imgur.com/cxkyWzi.png) ```sql= select city.name from city join country on city.countrycode = country.code where continent = 'Africa'; ``` ![](https://i.imgur.com/Xyk93Nr.png) > 使用 LEFT JOIN 及 RIGHT JOIN 時,要注意資料表寫的順序。 >> 如範例,「A RIGHT JOIN B」會把寫在右側的 B 資料表的所有資料留下。 >> 因為屬於外結合,所以亦可用代表外部的「OUTER」來寫。 [![圖片](https://www.dofactory.com/img/sql/sql-left-join.png "LEFT JOIN")](https://www.dofactory.com/sql/left-join) [![圖片](https://www.dofactory.com/img/sql/sql-right-join.png "RIGHT JOIN")](https://www.dofactory.com/sql/right-join) `範例`:省略 OUTER 和不省略的寫法 ```sql= SELECT * FROM A RIGHT JOIN B ON A.t = B.t; SELECT * FROM A RIGHT OUTER JOIN B ON A.t = B.t; ``` >「FULL OUTER JOIN」類似合併使用LEFT、RIGHT JOIN,也是外結合的一種。 >> [![圖片](https://www.dofactory.com/img/sql/sql-full-join.png "FULL OUTER JOIN")](https://www.dofactory.com/sql/full-join) `範例`:用 FULL OUTER 進行結合 ```sql= SELECT * FROM A FULL OUTER JOIN B ON A.t = B.t; ``` > 「CROSS JOIN」能進行「交叉結合」,沒有結合條件。 >> 若把 A 和 B 做交叉結合,就會得到 A 資料表之全部資料再接上 B 資料表之全部資料。 >> [![圖片](https://static.javatpoint.com/mysql/images/mysql-cross-join.png "CROSS JOIN")](https://www.javatpoint.com/mysql-cross-join) `範例`:將 A 和 B 資料表做交叉結合 ```sql= SELECT * FROM A CROSS JOIN B; ``` #### SUMMARY 1. 兩個table之間找出完整資料時,可以用INNER JOIN;會消除1對0(落單)的資料。 2. 當其中一個table的資料是必須完整保留時,可以用LEFT JOIN/RIGHT JOIN。 3. 想找出所有可能的==排列==時,用CROSS JOIN。 4. FULL OUTER JOIN (完全外連結): LEFT JOIN 與 RIGHT JOIN 的==連集==。 - USING > 若結合之條件是同名的欄位,可以用「USING」指定結合條件。 `範例`:將 A 和 B 資料表依 a 欄位進行內結合 ```sql= SELECT * FROM A INNER JOIN B USING (a); ``` ### WHERE 子句 :::info 指定抽出或結合條件。 ::: > 寫上選擇列資料的條件式;如此只有條件符合的列資料才會作為結果被傳回。 > 除了欄位名,亦可用運算子和函式來寫。 ```sql= SELECT * FROM [資料表名稱] WHERE 任意邏輯判斷式; ``` - 篩選條件 > 想要取得符合某種條件的列資料。 `範例`:將商品資料表中,單價為100的商品列出 ```sql= SELECT * FROM 商品 WHERE 單價 = 100; ``` - 結合 ### GROUP BY 子句 > 可以指定要群組化的欄位名,或是含有欄位名的陳述式。 ```sql= GROUP BY 任意陳述式 [, 任意陳述式] ``` `範例`:將資料表 foo 中的 a 欄位進行群組化,在統計其列數 ```sql= SELECT COUNT(*) FROM foo GROUP BY a; ``` `範例`:合計投票結果 ```sql= SELECT 姓氏, COUNT(*) FROM 投票結果 GROUP BY 姓氏; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/cWiKbHi.png) ```sql= select country.continent, round(avg(city.population), -0.5) from city join country on city.countrycode = country.code group by country.continent; ``` ![](https://i.imgur.com/ryrCcXb.png) ### GROUPING SETS ROLLUP CUBE > OLAP 合計選項 ```sql= GROUP BY 任意陳述式 [, 任意陳述式] WITH ROLLUP GROUP BY 任意陳述式 [, 任意陳述式] WITH CUBE ``` - SQL Server 無法利用 GROUPING SETS 來指定加入任意群組的合計列。 ### HAVING 子句 > 用統計函式的結果值作為條件來篩選 > 含有統計函式的條件式,不能使用WHERE子句的條件式來寫 ```sql= HAVING [任意邏輯判斷式] ``` `範例`:群組合計值大於100者,才傳回作為查詢結果。 ```sql= SELECT x, sum(i) FROM foo GROUP BY x HAVING sum(i) > 100; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/jbMdNd8.png) ```sql= select h.hacker_id, h.name from submissions s join challenges c on s.challenge_id = c.challenge_id join difficulty d on c.difficulty_level = d.difficulty_level join hackers h on s.hacker_id = h.hacker_id where s.score = d.score group by h.hacker_id, h.name having count(s.hacker_id) > 1 order by count(s.hacker_id) desc, s.hacker_id; ``` ![](https://i.imgur.com/qHoHIMu.png) ### ORDER BY 子句 > 指定取得之資料的順序 ```sql= SELECT * FROM [資料表名稱] ORDER BY 任意陳述式1 [ASC|DESC], 任意陳述式2 [ASC|DESC], ...; ``` - ASC(由小至大,升冪)/DESC(由大至小,降冪) `範例`:將資料表 A 中的所有資料依 a 欄位之值,==由小到大排序== ```sql= SELECT * FROM A ORDER BY a ASC; ``` `範例`:將資料表 A 中的所有資料依 a 欄位之值,==由大到小排序== ```sql= SELECT * FROM A ORDER BY a DESC; ``` - NULL值的排序 > 依資料庫種類不同而異。 >> 在 **SQL Server、MySQL** 和 **Access**中,會被當作**最小值**排序。 >> 在 **Oracle、DB2、PostgrSQL**中,NULL則會被當作**最大值**來排序。 `範例`:將資料表 A 中的所有資料依 a 欄位之值,==由大到小排序,NULL值排在最後== `Oracle` ```sql= SELECT * FROM A ORDER BY a DESC NULLS LAST; ``` - 依欄位編號來作ORDER BY的動作 > 可以指定欄位的編號,欄位編號是從1開始的任意數值。 `範例`:依要選取之欄位名清單中的第2個欄位作排序 ```sql= SELECT a, b FROM A ORDER BY 2; ``` `範例`:依要選取之欄位名清單中的第2個運算是作排序 ```sql= SELECT a, c*b FROM A ORDER BY 2; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/h66663L.png) ```sql= SELECT CITY, length(CITY) FROM STATION ORDER BY length(CITY), CITY ASC LIMIT 1; SELECT CITY, length(CITY) FROM STATION ORDER BY length(CITY) DESC LIMIT 1; ``` ![](https://i.imgur.com/GpGMQm8.png) ### LIMIT 子句 `MySQL` > 限制查詢結果 > 讓SELECT查詢到的資料不全部全回,而只傳回特定的部分。 `語法` ```sql= LIMIT [start,] count ``` > count:取得的資料筆(列)數 > start:從第幾筆(列)資料開始使用 `範例`:只取得所有查詢結果中,第5列開始的3列資料。 ```sql= SELECT * from foo limit 5, 3; ``` `範例`:只取得查詢結果排序後的最前頭3筆資料。 ```sql= SELECT * from foo order by a limit 3; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/otuteU6.png) ```sql= select salary * months as earnings, count(*) from employee group by earnings order by earnings desc limit 1; ``` ![](https://i.imgur.com/NiuO3ol.png) > `相關語法` [Group by](https://hackmd.io/LtqRKErKTTGeoWOVU7r0NA?both#GROUP-BY-%E5%AD%90%E5%8F%A5)、[Order by](https://hackmd.io/LtqRKErKTTGeoWOVU7r0NA?both#ORDER-BY-%E5%AD%90%E5%8F%A5) ### 子查詢(SUN QUERY) > [color=#3b75c6] SELECT 中的 SELECT 命令 `語法` ```sql= (select_statement) ``` > 利用子查詢能進行複雜的查詢動作。 `範例`:將bar資料表中b欄位之最大值拿來與foo資料表之a值表較,然後列出foo資料表中兩值相同的資料。 ```sql= SELECT * from foo where a = (select max(b) from bar); ``` `範例`:將select 命令所得的結果存到變數中後,再於下一個select命中使用。 ```sql= maxvalue = select max(b) from bar select * from foo where a = maxvalue; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/ZLaxf6H.png) ```sql= select round(long_w, 4) from station where lat_n = (select max(lat_n) from station where lat_n < 137.2345); ``` ![](https://i.imgur.com/So0khp5.png) `範例`:子查詢依存於主查詢的情況。 ```sql= select * from foo where a = (select max(b) from bar where bar.c = foo.c); ``` > 指定了bar之b欄位最大值,必須從bar和foo資料表中的c欄位值相同的資料裡找出。 `選自 HackerRank 範例`: ![](https://i.imgur.com/syaVO0Q.png) ```sql= select w.id, p.age, w.coins_needed, w.power from wands w join wands_property p on w.code = p.code where p.is_evil = 0 and w.coins_needed = (select min(coins_needed) from wands w1 join wands_property p1 on (w1.code = p1.code) where w1.power = w.power and p1.age = p.age) order by w.power desc, p.age desc; ``` ![](https://i.imgur.com/3T5yILb.png) `選自 HackerRank 範例`: ![](https://i.imgur.com/A1rIkrc.png) ```sql= select h.hacker_id, h.name, sum(score) from ( select hacker_id, challenge_id, max(score) as score from submissions group by hacker_id, challenge_id) t join hackers h on t.hacker_id = h.hacker_id group by h.hacker_id, h.name having sum(score) > 0 order by sum(score) desc, hacker_id; ``` ![](https://i.imgur.com/msQrvsp.png) - 使用了**EXISTS**的子查詢 想確認與某資料表有關聯的一資料表中,是否有某筆特定資料,並進一步據此確認結果作為查詢條件時使用。 `範例`:在foo資料表中,若有1筆以上的資料其a欄位值和bar資料表中的a欄位值相同,就把該筆資料傳回。 ```sql= select * from foo where exists (select * from bar where bar.a = foo.a); ``` - 在要選取之欄位名清單中使用子查詢 ```sql= select (select count(*) from foo), (select count(*) from bar); ``` - 在FROM子句中使用子查詢 ```sql= select * from (select a, b from bar) as subq; ``` --- ## PIVOT 面對數筆有意義資料要匯總成橫式資料時,可以考慮使用 PIVOT 來扭轉資料,讓資料呈現上更貼近人性。 `語法` ```sql= SELECT <column/s> FROM ( <source_query> ) as src PIVOT ( <aggr_function> ( <aggr_column> FOR <spreading_column> IN (<spreading_elements>) ) ) as ret ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/eFz9aKR.png) ```sql= select [doctor], [professor], [singer], [actor] from( select row_number() over (partition by occupation order by name) [rownumber], * from occupations ) as temptable pivot( max(name) for occupation in ([doctor], [actor], [singer], [professor]) ) as pivottable; ``` ![](https://i.imgur.com/jP7ifdW.png) ## 比較運算子 | 運算子 | 意思 | | -------- | -------- | | < | 小於 | | > | 大於 | | < | 小於 | | >= | 大於等於 | | <= | 小於等於 | | <> | 不相等 | | != | 不相等 | > 主要用在SELECT命令的WHERE子句中。 > 運算式若含有NULL值,就無法獲得確實的結果。 `選自 HackerRank 範例`: ![](https://i.imgur.com/ePJVT1z.png) ```sql= SELECT NAME FROM EMPLOYEE WHERE SALARY > 2000 AND MONTHS < 10 ORDER BY EMPLOYEE_ID ASC; ``` ![](https://i.imgur.com/isKXJ1S.png) `選自 HackerRank 範例`: ![](https://i.imgur.com/sMX7qXV.png) ```sql= select count(name) from city where population > 100000; ``` ![](https://i.imgur.com/q1TbrBF.png) > `相關語法`:[COUNT](https://hackmd.io/LtqRKErKTTGeoWOVU7r0NA?both#COUNT-%E5%87%BD%E5%BC%8F) ## 邏輯運算子 ### LIKE 運算子 > 比對搜尋 ```sql= SELECT [欄位名] FROM [資料表名稱] WHERE [欄位名] LIKE 字串陳述式; SELECT [欄位名] FROM [資料表名稱] WHERE [欄位名] LIKE 字串陳述式 ESCAPE 跳脫文字; ``` - 萬用字元表 | | Oracle | SQL Server | DB2 | MySQL | | -------------------- | ------ |:----------:| --- | ----- | | 任意字串 | % | % | % | % | | 任意單一文字 | _ | _ | _ | _ | | 指定範圍中的單一文字 | 無 | [a-f] | 無 | 無 | | 指定範圍外的單一文字 | 無 | [^a-f] | 無 | 無 | `選自 HackerRank 範例`: ![](https://i.imgur.com/QpMykNe.png) ```sql= SELECT DISTINCT CITY FROM STATION WHERE CITY LIKE '[aeiou]%'; ``` ![](https://i.imgur.com/NOq9Lnv.png) - 跳脫萬用字元 > 檢索 % 或 _ 萬用字元符號時,要進行「跳脫」的動作。 1. 跳脫文字為`\`,寫成`LIKE '%\%'`條件,便可用來查詢 100% 或 25% 此類的字串資料。 2. 亦可以指定跳脫文字,像是`LIKE '%e%' ESCAPE 'e'`,是指定將e作為跳脫文字。 `選自 HackerRank 範例`: ![](https://i.imgur.com/4ewxTs2.png) ```sql= SELECT DISTINCT CITY FROM STATION WHERE CITY LIKE '[aeiou]%[aeiou]'; ``` ![](https://i.imgur.com/uw3Mj0T.png) ### NOT 運算子 > 否定(非) ```sql= SELECT [欄位名] FROM [資料表名稱] WHERE NOT 陳述式; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/A3FtEae.png) ```sql= SELECT DISTINCT CITY FROM STATION WHERE NOT CITY LIKE '[aeiou]%'; ``` ![](https://i.imgur.com/6ZPFyPT.png) ### AND 運算子 > 邏輯的且 >> 「AND」運算子在其==左右兩邊的值都為TRUE==,會傳回TRUE。 ```sql= SELECT [欄位名] FROM [資料表名稱] WHERE 陳述式 AND 陳述式; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/PtQWqSf.png) ```sql= SELECT DISTINCT CITY FROM STATION WHERE NOT CITY LIKE '[aeiou]%' AND NOT CITY LIKE '%[aeiou]'; ``` ![](https://i.imgur.com/eNfvM2y.png) ## 其他運算子 ### CASE 運算子 ```sql= CASE [欄位名] WHEN [陳述式] THEN [陳述式] [WHEN 陳述式 THEN 陳述式][ELSE 陳述式] END -> 值 CASE WHEN [陳述式] THEN [陳述式] [WHEN 陳述式 THEN 陳述式][ELSE 陳述式] END -> 值 ``` > **CASE** 後寫上作為轉換來源的陳述式 > **WHEN** 後寫上轉換來源實際上的值 > 當轉換來源之值符合WHEN後所寫的值,**THEN**後所記述的值就會作為結果回傳 > WHEN後之值不符合時,所要傳回的結果值,寫在**ELSE**後 > 最後寫上**END**,CASE的運算結束 `範例`:將通訊錄資料表的性別資料轉換成英文表示的SELECT查詢。 ```sql= SELECT 姓名, CASE 性別 WHEN '男' THEN 'male' WHEN '女' THEN 'female' END FROM 通訊錄; ``` `範例`:檢索出年齡在20歲以上的,標為成人,不滿20歲的標為未成年。 ```sql= SELECT 姓名, CASE WHEN 年齡 >= 20 THEN '成年' WHEN 年齡 < 20 THEN '未成年' END FROM 通訊錄; ``` `範例`:變換NULL值。 ```sql= SELECT 姓名, CASE WHEN 年齡 IS NULL THEN '不詳' ELSE CAST (年齡 AS CHAR(2)) END FROM 通訊錄; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/iDVcQa0.png) ![](https://i.imgur.com/PVirhEj.png) ```sql= select case when A+B > C and B+C > A and A+C > B then case when A=B and B=C then 'Equilateral' when A=B or B=C or A=C then 'Isosceles' else 'Scalene' end else 'Not A Triangle' end from triangles; ``` ![](https://i.imgur.com/QgSH4Hd.png) --- ## 統計函式 ### AVG 函式 > 求平均值 Average ```sql= AVG([ distinct | all ] 數值陳述式) ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/cMLb09K.png) ```sql= select avg(population) from city where district = 'California'; ``` ![](https://i.imgur.com/YSqjhbn.png) ### COUNT 函式 > 算出資料筆數 ```sql= count([ distinct | all ] 陳述式) ``` `範例`:計算通訊錄資料表的資料筆數。 ```sql= SELECT count(*) FROM 通訊錄; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/d01s6pT.png) ```sql= select c.company_code, c.founder, count(distinct l.lead_manager_code), count(distinct s.senior_manager_code), count(distinct m.manager_code),count(distinct e.employee_code) from Company c, Lead_Manager l, Senior_Manager s, Manager m, Employee e where c.company_code = l.company_code and l.lead_manager_code=s.lead_manager_code and s.senior_manager_code=m.senior_manager_code and m.manager_code=e.manager_code group by c.company_code, c.founder order by c.company_code; ``` ![](https://i.imgur.com/cKZa00Q.png) ### MAX & MIN 函式 > 求最大值 Maximum及最小值 Minimum `選自 HackerRank 範例`: ![](https://i.imgur.com/qqp2AS0.png) ```sql= select max(population) - min(population) from city; ``` ![](https://i.imgur.com/MALMeHA.png) `選自 HackerRank 範例`: ![](https://i.imgur.com/yGXAxEg.png) ```sql= select round(min(lat_n), 4) from station where lat_n > 38.7780; ``` ![](https://i.imgur.com/HjvCoPy.png) ### SUM 函式 > 求合計值 ```sql= sum([ distinct | all ] 陳述式) ``` `範例`:算出年齡欄位的合計值。 ```sql= SELECT sum(年齡) FROM 通訊錄; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/862wLLR.png) ```sql= select sum(population) from city where district = 'California'; ``` ![](https://i.imgur.com/obNz5t6.png) `選自 HackerRank 範例`: ![](https://i.imgur.com/t1zIhNx.png) ```sql= select sum(population) from city where countrycode = 'JPN'; ``` ![](https://i.imgur.com/qRcNGfQ.png) --- ## 字串函式 ### CONCAT 函式 > 結合字串 > 會將參數所指定的字串結合在一起,並傳回結果值。 ```sql= concat(s, t) concat(s, t [,u...]) ``` `範例`:利用CONCAT來結合a欄位和b欄位的字串值。 ```sql= SELECT a, b, concat(a, b) FROM foo; ``` - MySQL 將字串s, t, u結合後傳回結果。由於參數個數不拘,所以可以寫成concat('a' 'b' 'c' 'd'),指定多個字串結合。 `範例`:利用CONCAT來結合a,b,c各欄位的字串值。 ```sql= SELECT a, b, c, concat(a, b, c) FROM foo; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/hwDQZWx.png) ```sql= select concat (name, "(", left(occupation, 1), ")") from occupations order by name asc; select concat ("There are a total of ", count(occupation), " ", lower(occupation), "s.") from occupations group by occupation order by count(occupation) asc, occupation asc; ``` ![](https://i.imgur.com/SjVFsh3.png) `選自 HackerRank 範例`: ![](https://i.imgur.com/3NrjkRB.png) ```sql= SELECT CASE WHEN P IS NULL THEN CONCAT(N, 'Root') WHEN N IN (SELECT DISTINCT P FROM BST) THEN CONCAT(N, 'Inner') ELSE CONCAT(N, 'Leaf') END FROM BST ORDER BY N ASC; ``` ![](https://i.imgur.com/2XX50bQ.png) ### LOWER 函式 > 轉換成小寫 ```sql= lower(s) ``` > s:字串陳述式 只有英文字母可以做為轉換對象;反之,欲將小寫文字轉成大寫,就要用「UPPER」函式。 `範例`:將欄位a 之字串都轉換成小寫。 ```sql= select a, lower(s) from foo; ``` ### REPEAT/REPLICATE 函式 > 重複字串,會將指定給 s 參數的字串重複 n 此後傳回。 ```sql= repeat(s, n) replicate(s, n) ``` > s:字串陳述式 > n:數值陳述式 :::warning :star:「REPEAT」是在DB2、PostgreSQL、MySQL下使用;「REPLICATE」則是在SQL Server下使用。 ::: `範例`:將欄位a的字串值,重複欄位b之值的次數後,建立新字串。 ```sql= SELECT a, b replicate(a, b) FROM foo; ``` ### REPLACE 函式 > 置換字串,把字串s中含有的字串t,通通置換成字串u ```sql= replace(s, t, u) ``` > s:要進行置換的主要字串陳述式 > t:置換前要找出的字串陳述式 > u:要置換進去的字串陳述式 `範例`:將欄位a的字串值裡的欄位b之字串值,都換成字串c。 ```sql= SELECT a, b, c, replace(a, b, c) FROM foo; ``` ### REVERSE 函式 > 反轉字串,把s參數所提供的字串,左右反轉後,傳回新產生的字串 ```sql= reverse(s) ``` `範例`:將欄位a的字串資料值反轉。 ```sql= SELECT a, reverse(a) FROM foo; ``` ### SUBSTRING 函式 > 抽出字串的某部分 ```sql= substring(欄位名, n, m) substring(欄位名 FROM n FOR m) ``` > n:要抽出文字之起始位置 > m:要抽出之文字數 `選自 HackerRank 範例`: ![](https://i.imgur.com/FQplTCT.png) ```sql= SELECT NAME FROM STUDENTS WHERE MARKS > 75 ORDER BY substring(NAME, len(name)-2, 3), ID ASC; ``` ![](https://i.imgur.com/O1C2jcp.png) ### LEN/LENGTH 函式 > 取得字串長度 ```sql= len(欄位名) length(欄位名) ``` > 可以算出字串的「文字數」或是字串的「位元組數」 - SQL Server、Access > 當字串資料為固定長度的CHAR資料類型,SQL Server中的LEN函式會忽略右側多餘的空白數,指傳回有效文字數。 `範例`:算出欄位 a 的字串長度 ```sql= SELECT a, len(a) FROM A; ``` - MySQL > SQL Server中的LENGTH函式會傳回字串的位元組數。 `範例`:算出欄位 a 的字串長度 ```sql= SELECT a, length(a) FROM A; ``` --- ## 轉換函式 ### CAST 函式 > 轉換資料類型,是由ANSI所規定的。 `語法` ```sql= CAST(e AS t) ``` > e:要轉換的值 > t:轉換成的類型 - SQL Server 和「CONVERT」函式相同。 `範例`:將數值資料轉換成字串資料類型。 ```sql= SELECT CAST(0.245 AS varchar); ``` `範例`:將字串資料轉換成日期資料類型。 ```sql= SELECT CAST('99/01/25' AS datetime); ``` --- ## 數學函式 ### ABS 函式 > 求絕對值 Absolute `語法` ```sql= ABS( n ) ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/pOn7ywu.png) ```sql= select round(abs(max(lat_n) - min(lat_n)) + abs(max(long_w) - min(long_w)), 4) from station; ``` ![](https://i.imgur.com/DRNqG5u.png) ### CEIL/CEILING 函式 > 傳回無條件==進位==後的整數值 ```sql= CEILING(n) ``` > 會算出參數n 無條件進位後之整數值(大於等於參數n之最小整數)。 > :warning: 為無條件進位,而非小數點後四捨五入。 `選自 HackerRank 範例`: ![](https://i.imgur.com/r6AiUlV.png) ```sql= select ceil(avg(salary)-avg(replace(salary,'0',''))) from employees; ``` ![](https://i.imgur.com/f9K2YI7.png) > `相關語法`:[AVG](https://hackmd.io/LtqRKErKTTGeoWOVU7r0NA?both#AVG-%E5%87%BD%E5%BC%8F)、[REPLACE](https://hackmd.io/LtqRKErKTTGeoWOVU7r0NA?both#REPLACE-%E5%87%BD%E5%BC%8F) ### FLOOR 函式 > 傳回無條件==捨去==後的整數值 ```sql= FLOOR(n) ``` > 會算出參數n 無條件捨去後之值(小於等於參數n之最大整數)。 `範例`:將欄位 a 之值小數點以下無條件捨去。 ```sql= SELECT a, floor(a) FROM foo; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/2OvDHeX.png) ```sql= select floor(avg(population)) from city; ``` ![](https://i.imgur.com/UpQD43q.png) > `相關語法`:[AVG](https://hackmd.io/LtqRKErKTTGeoWOVU7r0NA?both#AVG-%E5%87%BD%E5%BC%8F) ### MEDIAN 函式 > 取中位數,**ORACLE適用**。 ```sql= MEDIAN([欄位名]) ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/p6PSQLS.png) ```sql= select round(median(lat_n), 4) from station; ``` ![](https://i.imgur.com/Koashz8.png) ### MOD 函式 > 取得餘數 `語法` ```sql= MOD( n, m ) ``` > n:被除數之值 > m:除數之值 `範例`:求出欄位a / 欄位b之餘數。 ```sql= SELECT a, b, MOD(a, b) FROM foo; ``` :::danger 在SQL Sercer中,沒有MOD函式可用,要用「%」來計算餘數。 ::: ### ROUND 函式 > 四捨五入 ```sql= ROUND(n, m [, t]) ``` > n:任意數值陳述式 > m:要在小數點以下保留幾位數 > t:執行之形式(四捨五入或無條件捨去) - SQL Server 1. 一定要指定參數m,以決定要保留小數點以下幾位數。 指定正數時,表要保留小數點以下m位;指定負數時,則表保留小數點之前幾位。 2. 參數t是用來指定四捨五入還是無條件捨去。 指定為0(或省略不指定)時,表四捨五入;指定0以外的值,表無條件捨去。 `範例`:把欄位a之值小數點以下的部分四捨五入。 ```sql= SELECT a, round(a, 0) from foo; ``` `範例`:把欄位a之值到小數點以下第2位四捨五入,保留到小數點以下第1位。 ```sql= SELECT a, round(a, 1) from foo; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/itVMWEe.png) ```sql= select round(sum(lat_n), 2) ,round(sum(long_w), 2) from STATION; ``` ![](https://i.imgur.com/sEgpmZH.png) `選自 HackerRank 範例`: ![](https://i.imgur.com/tGfBwIp.png) ```sql= select round(sum(lat_n), 4) from station where 38.7880 < lat_n and lat_n < 137.2345; ``` ![](https://i.imgur.com/bVq7Pki.png) `選自 HackerRank 範例`: ![](https://i.imgur.com/kcmnCao.png) ```sql= select max(round(lat_n, 4)) from station where lat_n < 137.2345; ``` ![](https://i.imgur.com/bzUrPnT.png) `選自 HackerRank 範例`: ![](https://i.imgur.com/yPSFxXJ.png) ```sql= select round(long_w, 4) from station where lat_n = (select min(lat_n) from station where lat_n > 38.7780); ``` ![](https://i.imgur.com/x5AMz9V.png) ### SQRT > 平方根 `語法` ```sql= SQRT( n ) ``` > n 不能是負數。 ### SQUARE > 平方 `語法` ```sql= SQUARE( n ) ``` > 也可以用「POWER(n, 2)」 `選自 HackerRank 範例`: ![](https://i.imgur.com/g2aa2Ux.png) ```sql= DECLARE @a FLOAT; DECLARE @b FLOAT; DECLARE @c FLOAT; DECLARE @d FLOAT; DECLARE @distance FLOAT; SET @a = (SELECT MIN(lat_n) FROM station); SET @b = (SELECT MAX(lat_n) FROM station); SET @c = (SELECT MIN(long_w) FROM station); SET @d = (SELECT MAX(long_w) FROM station); SET @distance = SQRT(SQUARE(@a - @b) + SQUARE(@c - @d)); SELECT FORMAT(@distance, 'F4'); ``` ![](https://i.imgur.com/wTAMCm2.png) --- ## 定義變數 ### DECLARE - SQL Server 定義變數要用「DECLARE命令」。變數名稱==最前面要加「@」符號==;連續加上2個@符號,就代表「全域(Global)變數」。 全域變數並不是用來定義使用者資料,而是在參照系統資訊時使用:寫成@@FETCH_STATUS、@@VERSION等。 > 要代入值到變數中,可以寫成「SELECT @variable_name = 值」,以**SELECT**命令來代入;或者,「SET @variable_name = 值」,利用**SET**命令來代入。 `範例`:定義i, j兩個變數,並把1代入i,把2代入j。 ```sql= DECLARE @i int, @j int SELECT @i = 1 SET @j = 2; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/phRnVS6.png) ```sql= DECLARE @i int=2, @prime int = 0, @result nvarchar(1000) = '' WHILE (@i<=1000) begin DECLARE @j int = @i-1; SET @prime = 1; WHILE(@j > 1) begin IF @i % @j = 0 begin SET @PRIME = 0; end set @j = @j - 1; end IF @PRIME = 1 BEGIN set @result += cast(@i as nvarchar(1000)) + '&'; END set @i = @i + 1; end set @result = SUBSTRING(@result, 1, LEN(@result) - 1) select @result ``` ![](https://i.imgur.com/fITjFTg.png) --- ## 迴圈處理 ### WHILE `語法` ```sql= WHILE [迴圈執行之條件式] [要重複執行之命令]; ``` - SQL Server 在指定重複執行的條件式之後,要接著想重複執行之命令句。 想重複執行多個命令的話,要用 **BEGIN、END** 把這些命令句都包起來,此範圍就稱「命令句區塊」。 `範例`:用WHILE將變數i從0到100為止,進行迴圈處理,並以print來輸出i之值。 ```sql= declare @i int set @i = 0 while @i < 100 begin set @i = @i + 1 print @i end ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/ZX5pSnq.png) ```sql= declare @i int = 20 while (@i > 0) begin print replicate(' * ', @i) set @i = @i - 1 end; ``` ![](https://i.imgur.com/j2FGsjh.png) `選自 HackerRank 範例`: ![](https://i.imgur.com/6s6ze7j.png) ```sql= declare @i int = 1 while (@i < 21) begin print replicate(' * ', @i) set @i = @i + 1 end; ``` ![](https://i.imgur.com/angvGfN.png) > `相關語法`:[REPLICATE](https://hackmd.io/LtqRKErKTTGeoWOVU7r0NA?both#REPEATREPLICATE-%E5%87%BD%E5%BC%8F)、[DECLARE](https://hackmd.io/LtqRKErKTTGeoWOVU7r0NA?both#DECLARE) --- ## 條件判斷 ### IF `語法` ```sql= IF [條件式] then_statement ELSE else_statement; ``` `選自 HackerRank 範例`: ![](https://i.imgur.com/gVprirW.png) ```sql= select if(grade < 8, null, name), grade, marks from students join grades where marks between min_mark and max_mark order by grade desc, name; ``` ![](https://i.imgur.com/RHLkZRy.png) --- ## 外部連結/參考文獻 - [HackerRank](https://www.hackerrank.com/dashboard) 線上練習(需註冊帳號) - SQL 語法範例辭典 (2013版)