# 基本功
## [1757. Recyclable and Low Fat Products](https://leetcode.com/problems/recyclable-and-low-fat-products/?envType=study-plan-v2&envId=top-sql-50)
:::success
编写一个解决方案,找出既低脂又可回收的产品 ID。 按任意顺序返回结果表。
解題思路
就是篩選出 low_fats 和 recyclable 為Y的結果
:::
:::spoiler code
```
select product_id
from Products
where low_fats = 'Y' && recyclable ='Y'
```
:::
## [584. Find Customer Referee](https://leetcode.com/problems/find-customer-referee/?envType=study-plan-v2&envId=top-sql-50)
:::success
題目要求: 查找不是由 id = 2 的客户推荐的客户姓名。 按任意顺序返回结果表。
解題思路
篩選出 referee_id 不是2的結果
>**在使用 != 或 <> 的時會發現排除掉所有 NULL**
**這是因為 NULL代表缺失值 且 NULL又不等於NULL 也不等於 任何值**
**無法進行比較 因此 NULL 會直接被排除掉**
:::
:::spoiler code
```
select name
from Customer
where referee_id <> 2 or referee_id is NULL
```
:::
## [595. Big Countries](https://leetcode.com/problems/big-countries/?envType=study-plan-v2&envId=top-sql-50)
:::success
一个国家大的条件是
面积至少有 300 万(即 3000000 平方公里),或
人口至少有两千五百万(即 25000000)。
请写出求大国的名称、人口和面积的解法。
按任意顺序返回结果表。
解題思路
篩選出面積至少300萬 或是人口 至少兩千五百萬
:::
:::spoiler code
```
select name, population, area
from World
where area >= 3000000 || population >= 25000000
```
:::
## [1148. Article Views I](https://leetcode.com/problems/article-views-i/?envType=study-plan-v2&envId=top-sql-50)
:::success
该表没有主键(具有唯一值的列),因此可能有重复行。
该表中的每一行都表示某个查看者在某个日期查看了某篇文章(由某个作者撰写)。
请注意,相同的 author_id 和 viewer_id 表示同一个人。
编写一个解决方案,找出所有至少浏览过自己一篇文章的作者。
返回按 id 升序排序的结果表。
解題思路
篩選出 author_id = viewer_id 也就是 看過自己文章的作者 再按照id排列
考慮到重複值的部分 所以 印出需要加上 distinct 印出不重複的結果
:::
:::spoiler code
```
select DISTINCT author_id as id
from Views
where author_id = viewer_id
order by id
```
:::
## [1683. Invalid Tweets](https://leetcode.com/problems/invalid-tweets/description/?envType=study-plan-v2&envId=top-sql-50)
:::success
编写一个解决方案,找出无效推文的 ID。如果推文内容中使用的字符数严格大于 15,则该推文无效。
按任意顺序返回结果表。
解題思路
篩選出content欄位的字數大於15的結果
>LENGTH()、LEN() 函數是用來取得字串長度。
:::
:::spoiler code
```
select tweet_id
from Tweets
where LENGTH(content) > 15
```
:::
# Base Join
## [1378. Replace Employee ID With The Unique Identifier](https://leetcode.com/problems/replace-employee-id-with-the-unique-identifier/?envType=study-plan-v2&envId=top-sql-50)
:::success
编写一个解决方案,显示每个用户的唯一 ID,如果用户没有唯一 ID,则替换为显示空值。
以任意顺序返回结果表。
解題思路
Employees表 和 EmployeeUNI表 的關聯性是建立在Id欄位上
但是必須考慮到有些用戶沒有唯一ID的情況 所以使用Employees作為主表確保列出所有員工
即使沒有匹配的唯一ID 仍會印出 只是被轉換成NULL
:::
:::spoiler code
```
select u.unique_id , e.name
from Employees as e
left join EmployeeUNI as u on e.id = u.id
```
:::
## [1068. Product Sales Analysis I](https://leetcode.com/problems/product-sales-analysis-i/?envType=study-plan-v2&envId=top-sql-50)
:::success
编写一个解决方案,以报告销售表中每个 sale_id 的 product_name、年份和价格。
按任意顺序返回结果表。
解題思路
兩個表的關聯建立在 product_id欄位上
:::
:::spoiler code
```
SELECT p.product_name, s.year, s.price
from Sales as s
join Product as p on s.product_id = p.product_id
```
:::
## [1581. Customer Who Visited but Did Not Make Any Transactions](https://leetcode.com/problems/customer-who-visited-but-did-not-make-any-transactions/?envType=study-plan-v2&envId=top-sql-50)
:::success
编写一个解决方案,以查找未进行任何交易的访问用户的 ID 及其进行此类访问的次数。
返回按任意顺序排列的结果表。
解題思路
因為要找出沒有進行任何交易的訪問用戶
兩個表可以看出關聯性的基礎建立在visit_id上 然後以Visits當作主表使用 確定列出所有訪問用戶
如此沒有進行過交易用戶的用戶將會呈現null
然後以用戶進行分組進而計算訪問次數
:::
:::spoiler code
```
#因為要找出沒有進行任何交易的訪問ID 所以要確表印出所有訪問資訊 也就是使用 left join
select v.customer_id, count(v.visit_id) as count_no_trans
from Visits as v
left join Transactions as t on v.visit_id =t.visit_id
where t.transaction_id is null
group by v.customer_id
```
:::
## [197. Rising Temperature](https://leetcode.com/problems/rising-temperature/?envType=study-plan-v2&envId=top-sql-50)
:::success
请编写一个解决方案,以查找与前一天(昨天)相比温度较高的所有日期的 Id。
以任意顺序返回结果表。
解題思路
我們需要以自身合併, 但不是單純自身合併 而是與前一天的自己做合併
所以關聯性是建立在 當天=前一天(當天與前一天相匹配)的情況下 然後再透過篩選條件找出溫度比前天高的所有結果
:::
:::spoiler code
```
select w1.id from Weather as w1
join Weather as w2 on DATE_ADD(w1.recordDate,INTERVAL -1 DAY) = w2.recordDate
where w1.temperature > w2.temperature
```
:::
## [1661. Average Time of Process per Machine](https://leetcode.com/problems/average-time-of-process-per-machine/?envType=study-plan-v2&envId=top-sql-50)
:::success
有一个工厂网站,网站上有多台机器,每台机器都运行相同数量的进程。请编写一个解决方案,找出每台机器完成一个进程所需的平均时间。
完成一个进程的时间是 "结束 "时间戳减去 "开始 "时间戳。平均时间的计算方法是:机器上完成每个进程的总时间除以运行的进程数。
结果表中应包含机器 ID 和作为 processing_time 的平均时间,并四舍五入到小数点后 3 位。
以任意顺序返回结果表。
解題思路
很明顯的 我需要兩個欄位 一個叫做start 一個叫做end,相減後取得啟動時間,
所以必須把關聯建立在同個machine_id 以及 同個 process_id 再去篩選開始和結束
再依照machine_id 分組 計算平均時間
:::
:::spoiler code
```
select a1.machine_id , ROUND(AVG(a2.timestamp - a1.timestamp),3) as processing_time
from Activity as a1
join Activity as a2 on a1.machine_id = a2.machine_id AND a1.process_id = a2.process_id
where a1.activity_type = 'start' and a2.activity_type = 'end'
group by a1.machine_id
```
## 效率更高 CASE WHEN
```
SELECT machine_id,
ROUND(AVG(end_time - start_time), 3) AS processing_time
FROM (
SELECT process_id,
machine_id,
MAX(CASE WHEN activity_type = 'start' THEN timestamp END) AS start_time,
MAX(CASE WHEN activity_type = 'end' THEN timestamp END) AS end_time
FROM Activity
GROUP BY process_id, machine_id
) t
GROUP BY machine_id;
```
:::
## [577. Employee Bonus](https://leetcode.com/problems/employee-bonus/?envType=study-plan-v2&envId=top-sql-50)
:::success
编写一个解决方案,报告奖金少于 1000 的每位员工的姓名和奖金数额。
以任意顺序返回结果表。'
解題思路
Employee當作主表 確保每位員工都有列出
然後 篩選出獎金少於1000的員工 或是 獎金為 null的員工
> 因為null不等於任何值 所以無法進行比較 SQL會直接排除掉
:::
:::spoiler code
```
select e.name, b.bonus from Employee as e
left join Bonus as b on e.empId = b.empId
where b.bonus < 1000 || b.bonus is NULL
```
:::
## [1280. Students and Examinations](https://leetcode.com/problems/students-and-examinations/description/?envType=study-plan-v2&envId=top-sql-50)
:::success
返回按 student_id 和 subject_name 排序的结果表。
解題絲路
成绩表应包含所有学生和所有科目。
首先看到學生和科目之間沒有關聯性 但每個學生必須和科目進行組合 這邊就直接使用笛卡爾乘積
得到一個新表具有學生對應所有科目的資料 a-數學, a-物理, a-程式 , b-數學......
然後以學生和科目當作主表 匹配每個學生和每個科目的考試記錄,確保沒有參加考試仍會顯示
:::
:::spoiler code
```
select s.student_id,s.student_name,subj.subject_name, count(e.subject_name) as attended_exams
from Students as s
CROSS JOIN Subjects as subj
left join Examinations as e on
s.student_id = e.student_id
and
subj.subject_name = e.subject_name
group by s.student_name, subj.subject_name
order by s.student_id ,subj.subject_name
```
:::
## [570. Managers with at Least 5 Direct Reports](https://leetcode.com/problems/managers-with-at-least-5-direct-reports/description/?envType=study-plan-v2&envId=top-sql-50)
:::success
與自身當主表 id與 managerId進行關聯
然後按照e1.group
:::
:::spoiler code
```
SELECT
case
when e1.name is null then null
else e1.name
end as name
FROM Employee as e1
LEFT JOIN Employee as e2 ON e1.id = e2.managerId --列出每位員工的下屬
group by e1.id
having COUNT(e2.id) > 4 -- 篩選下屬超過五位的經理
```
:::
## [1934. Confirmation Rate](https://leetcode.com/problems/confirmation-rate/description/?envType=study-plan-v2&envId=top-sql-50)
:::success
用户的确认率是 "确认 "信息的数量除以请求确认信息的总数。没有请求任何确认信息的用户的确认率为 0。
写出求解方案,找出每个用户的确认率。
:::
:::spoiler code
```
select s.user_id,ROUND(IFNULL(SUM(action = 'confirmed ')/COUNT(1),0),2) as confirmation_rate FROM Signups as s
left join Confirmations as c on s.user_id = c.user_id
group by s.user_id
-----
ps 也可以直接使用avg
```
:::
# SQL SERVER 函數
類似 CASE WHER
# IIF(條件,True返回值,False返回值)
* MySQL可使用IF

# LEN(expression): 回傳字串長度, 不包含尾部空白

# CONCAT(str1, str2):拼接字串

# CAST(expression AS target_data_type): 型別轉換
# CONVERT(target_data_type, expression, [style])
* 常用樣式碼對照表(日期)
Style 輸出格式 說明
100 Mon dd yyyy hh:miAM (or PM) 美國預設格式
101 mm/dd/yyyy 美式日期格式
102 yyyy.mm.dd ISO 標準格式
103 dd/mm/yyyy 英國日期格式
111 yyyy/mm/dd 日本日期格式
120 yyyy-mm-dd hh:mi:ss ODBC 標準格式

# ISNULL(EXPRESSION,True返回值):為NULL時回傳替代值

# TRIM(String):去除字串左右空白

# CEILING(numeric_expression):將數值無條件進位到最接近的整數(即「向上取整數」)

# FLOOR(numeric_expression):將數值無條件捨去到最接近的整數(即「向下取整數」)

# ROW_NUMBER(): 排序函數
產生虛擬主鍵
使用情境:在設定view表時,缺少了流水號
這時可以使用 ROW_NUMBER() OVER (ORDER BY...)可以將查詢結果按照指定欄位排序,從1開始排序
* ROW_NUMBER():代表要產生流水號
* OVER:是SQL的窗口(Window)語法,用來定義計算範圍
* ORDER BY column:根據指定的欄位排序後產生流水號
* ==注意 ROW_NUMBER()是查尋執行時動態產生,不會寫入資料庫==

## 想產出「第幾組」、「第幾個」、「第幾輪」的資料, 保證相同值分在同一 GROUP_NO
| 函數名稱 | 排名重複時是否跳號? | 相同值是否會有相同排名? |
| -------- | -------------------- | -------- |
| RANK() | 跳號(1, 2, 2, 4) | 是 |
DENSE_RANK()| 不跳號(1,2,2,3) | 是 | |
| ROW_NUMBER() | 不跳號,每筆唯一(1, 2, 3, 4) | 否(每筆都不同) |
```
WITH CanCelKeyMinSeq AS (
-- 取同組ACCT_CANCEL_KEY最小SEQ
SELECT E.ACCT_CANCEL_KEY,
MIN(SEQ) AS MIN_SEQ
FROM HOAMS.HOAMS.HOAMS_TB_CUR_ENT E
WHERE E.ACCT_CANCEL_KEY LIKE
'[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][A-Z][A-Z][0-9][0-9][0-9][0-9][0-9][0-9]'
OR E.ACCT_CANCEL_KEY LIKE '[0-9][0-9][0-9][0-9][0-9][0-9]C[0-9][0-9][0-9][0-9][0-9][0-9]'
GROUP BY E.ACCT_CANCEL_KEY)
-- 計算 GROUP_NO
SELECT TRIM(
ISNULL(T.MEMO_NAME, '') +
CASE
WHEN T.MEMO_NAME IS NOT NULL AND T.MEMO1 IS NOT NULL THEN ' '
ELSE ''
END +
ISNULL(T.MEMO1, '')
) AS MEMO1,
E.SEQ,
E.ACCT_CANCEL_KEY,
CASE
WHEN E.ACCT_CANCEL_KEY LIKE
'[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][A-Z][A-Z][0-9][0-9][0-9][0-9][0-9][0-9]'
OR
E.ACCT_CANCEL_KEY LIKE
'[0-9][0-9][0-9][0-9][0-9][0-9]C[0-9][0-9][0-9][0-9][0-9][0-9]'
THEN
DENSE_RANK() OVER (ORDER BY KM.MIN_SEQ)
ELSE
CEILING(
ROW_NUMBER() OVER (
PARTITION BY T.TRAN_ID, T.ACCT_CD, T.DC, T.CURR_CD
ORDER BY T.TRAN_ID, E.SEQ
) / 5.0
)
END AS GROUP_NO
FROM HOAMS.HOAMS.HOAMS_V_RPT_PDF_D0300_DATASOURCE T
LEFT JOIN HOAMS.HOAMS_TB_CUR_ENT E ON T.PK = E.ID
LEFT JOIN CanCelKeyMinSeq KM ON E.ACCT_CANCEL_KEY = KM.ACCT_CANCEL_KEY
WHERE E.TRAN_ID = 1098
order by GROUP_NO
```


