一、通過排名
或者范圍條件連表
篩選特殊行
- 第一行
- 最后一行
- 區(qū)間(第一行到第二行或者連續(xù)區(qū)間)
- 找中位數(shù)
- 通過
排名
進行分組或者連續(xù)區(qū)間
1.使用條件篩選連表找區(qū)間
Employee 表保存了一年內(nèi)的薪水信息。
請你編寫 SQL 語句,來查詢每個員工每個月最近三個月的累計薪水(不包括當前統(tǒng)計月,不足三個月也要計算)。
結(jié)果請按 'Id' 升序,然后按 'Month' 降序顯示。
示例:
輸入:
Id | Month | Salary |
---|---|---|
1 | 1 | 20 |
2 | 1 | 20 |
1 | 2 | 30 |
2 | 2 | 30 |
3 | 2 | 40 |
1 | 3 | 40 |
3 | 3 | 60 |
1 | 4 | 60 |
3 | 4 | 70 |
輸出:
Id | Month | Salary |
---|---|---|
1 | 3 | 90 |
1 | 2 | 50 |
1 | 1 | 20 |
2 | 1 | 20 |
3 | 3 | 100 |
3 | 2 | 40 |
解釋:
員工 '1' 除去最近一個月(月份 '4'),有三個月的薪水記錄:月份 '3' 薪水為 40,月份 '2' 薪水為 30,月份 '1' 薪水為 20。
所以近 3 個月的薪水累計分別為 (40 + 30 + 20) = 90,(30 + 20) = 50 和 20。
Id | Month | Salary |
---|---|---|
1 | 3 | 90 |
1 | 2 | 50 |
1 | 1 | 20 |
員工 '2' 除去最近的一個月(月份 '2')的話,只有月份 '1' 這一個月的薪水記錄。
Id | Month | Salary |
---|---|---|
2 | 1 | 20 |
員工 '3' 除去最近一個月(月份 '4')后有兩個月,分別為:月份 '4' 薪水為 60 和 月份 '2' 薪水為 40。所以各月的累計情況如下:
Id | Month | Salary |
---|---|---|
3 | 3 | 100 |
3 | 2 | 40 |
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/find-cumulative-salary-of-an-employee
著作權(quán)歸領扣網(wǎng)絡所有。商業(yè)轉(zhuǎn)載請聯(lián)系官方授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
代碼中使用一個范圍連表,在使用最小月份去重最后一個月
# Write your MySQL query statement below
select d1.Id,d1.Month,d1.Salary
from
(select a.Id,a.Month,sum(b.Salary) Salary
from Employee a
left join Employee b
on a.Id = b.Id and a.month >= b.month and b.month >=a.month-2
group by a.Id,a.Month
) d1
left join
(select c.Id,Max(c.Month) maxMonth
from Employee c
group by c.Id) d2
on d1.id = d2.id
where d1.Month < d2.maxMonth
order by d1.id asc,d1.Month desc
2.使用排名來找中位數(shù)
Employee 表包含所有員工。Employee 表有三列:員工Id,公司名和薪水。
+-----+------------+--------+
|Id | Company | Salary |
+-----+------------+--------+
|1 | A | 2341 |
|2 | A | 341 |
|3 | A | 15 |
|4 | A | 15314 |
|5 | A | 451 |
|6 | A | 513 |
|7 | B | 15 |
|8 | B | 13 |
|9 | B | 1154 |
|10 | B | 1345 |
|11 | B | 1221 |
|12 | B | 234 |
|13 | C | 2345 |
|14 | C | 2645 |
|15 | C | 2645 |
|16 | C | 2652 |
|17 | C | 65 |
+-----+------------+--------+
請編寫SQL查詢來查找每個公司的薪水中位數(shù)。挑戰(zhàn)點:你是否可以在不使用任何內(nèi)置的SQL函數(shù)的情況下解決此問題。
+-----+------------+--------+
|Id | Company | Salary |
+-----+------------+--------+
|5 | A | 451 |
|6 | A | 513 |
|12 | B | 234 |
|9 | B | 1154 |
|14 | C | 2645 |
+-----+------------+--------+
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/median-employee-salary
著作權(quán)歸領扣網(wǎng)絡所有。商業(yè)轉(zhuǎn)載請聯(lián)系官方授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
下面先進行排名,然后使用in()進行偶數(shù)個數(shù)組的中位數(shù)和奇數(shù)個數(shù)組的中位數(shù)
# Write your MySQL query statement below
select c1.id,c1.company,c1.salary
from
(select a.id,a.Company,a.salary,
case @cp when a.Company then @rk:=@rk+1 else @rk:=1 end rk,
@cp:=a.Company
from Employee a,(select @rk:=0,@cp:='') b
order by a.Company,a.salary) c1
left join
(select company,count(1)/2 p
from Employee
group by company ) c2
on c1.company=c2.company
where c1.rk in (c2.p+0.5,c2.p+1,c2.p)
3.使用排名找特定的某一行
表: Users
+----------------+---------+
| Column Name | Type |
+----------------+---------+
| user_id | int |
| join_date | date |
| favorite_brand | varchar |
+----------------+---------+
user_id 是該表的主鍵
表中包含一位在線購物網(wǎng)站用戶的個人信息,用戶可以在該網(wǎng)站出售和購買商品。
表: Orders
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| order_id | int |
| order_date | date |
| item_id | int |
| buyer_id | int |
| seller_id | int |
+---------------+---------+
order_id 是該表的主鍵
item_id 是 Items 表的外鍵
buyer_id 和 seller_id 是 Users 表的外鍵
表: Items
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| item_id | int |
| item_brand | varchar |
+---------------+---------+
item_id 是該表的主鍵
寫一個 SQL 查詢確定每一個用戶按日期順序賣出的第二件商品的品牌是否是他們最喜愛的品牌。如果一個用戶賣出少于兩件商品,查詢的結(jié)果是 no 。
題目保證沒有一個用戶在一天中賣出超過一件商品
下面是查詢結(jié)果格式的例子:
Users table:
+---------+------------+----------------+
| user_id | join_date | favorite_brand |
+---------+------------+----------------+
| 1 | 2019-01-01 | Lenovo |
| 2 | 2019-02-09 | Samsung |
| 3 | 2019-01-19 | LG |
| 4 | 2019-05-21 | HP |
+---------+------------+----------------+
Orders table:
+----------+------------+---------+----------+-----------+
| order_id | order_date | item_id | buyer_id | seller_id |
+----------+------------+---------+----------+-----------+
| 1 | 2019-08-01 | 4 | 1 | 2 |
| 2 | 2019-08-02 | 2 | 1 | 3 |
| 3 | 2019-08-03 | 3 | 2 | 3 |
| 4 | 2019-08-04 | 1 | 4 | 2 |
| 5 | 2019-08-04 | 1 | 3 | 4 |
| 6 | 2019-08-05 | 2 | 2 | 4 |
+----------+------------+---------+----------+-----------+
Items table:
+---------+------------+
| item_id | item_brand |
+---------+------------+
| 1 | Samsung |
| 2 | Lenovo |
| 3 | LG |
| 4 | HP |
+---------+------------+
Result table:
+-----------+--------------------+
| seller_id | 2nd_item_fav_brand |
+-----------+--------------------+
| 1 | no |
| 2 | yes |
| 3 | yes |
| 4 | no |
+-----------+--------------------+
id 為 1 的用戶的查詢結(jié)果是 no,因為他什么也沒有賣出
id為 2 和 3 的用戶的查詢結(jié)果是 yes,因為他們賣出的第二件商品的品牌是他們自己最喜愛的品牌
id為 4 的用戶的查詢結(jié)果是 no,因為他賣出的第二件商品的品牌不是他最喜愛的品牌
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/market-analysis-ii
著作權(quán)歸領扣網(wǎng)絡所有。商業(yè)轉(zhuǎn)載請聯(lián)系官方授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
代碼中先進行排序,直接使用序號進行過濾
# Write your MySQL query statement below
select u.user_id seller_id,if(i.item_brand=u.favorite_brand,'yes','no') 2nd_item_fav_brand
from Users u left join
(select d.seller_id,d.order_date,d.rank
from
(select c.seller_id,c.order_date,case when @seller=c.seller_id then @rk:=@rk+1 else @rk:=1 end rank, @seller:=c.seller_id temp
from
(select * from Orders order by seller_id,order_date) c,(select @seller:=-1,@rk:=-1) b
) d
where d.rank =2) o
on u.user_id = o.seller_id
left join Orders t on o.seller_id = t.seller_id and o.order_date = t.order_date
left join Items i on t.item_id = i.item_id
order by u.user_id
二、通過排名
進行分組或者連續(xù)區(qū)間
為什么要使用這種方式分組,因為有時候你的分組維度是不存在的,你必須自己制造這個維度!比如如下這題:
Table: Failed
+--------------+---------+
| Column Name | Type |
+--------------+---------+
| fail_date | date |
+--------------+---------+
該表主鍵為 fail_date。
該表包含失敗任務的天數(shù).
Table: Succeeded
+--------------+---------+
| Column Name | Type |
+--------------+---------+
| success_date | date |
+--------------+---------+
該表主鍵為 success_date。
該表包含成功任務的天數(shù).
系統(tǒng) 每天 運行一個任務。每個任務都獨立于先前的任務。任務的狀態(tài)可以是失敗或是成功。
編寫一個 SQL 查詢 2019-01-01 到 2019-12-31 期間任務連續(xù)同狀態(tài) period_state 的起止日期(start_date 和 end_date)。即如果任務失敗了,就是失敗狀態(tài)的起止日期,如果任務成功了,就是成功狀態(tài)的起止日期。
最后結(jié)果按照起始日期 start_date 排序
查詢結(jié)果樣例如下所示:
Failed table:
+-------------------+
| fail_date |
+-------------------+
| 2018-12-28 |
| 2018-12-29 |
| 2019-01-04 |
| 2019-01-05 |
+-------------------+
Succeeded table:
+-------------------+
| success_date |
+-------------------+
| 2018-12-30 |
| 2018-12-31 |
| 2019-01-01 |
| 2019-01-02 |
| 2019-01-03 |
| 2019-01-06 |
+-------------------+
Result table:
+--------------+--------------+--------------+
| period_state | start_date | end_date |
+--------------+--------------+--------------+
| succeeded | 2019-01-01 | 2019-01-03 |
| failed | 2019-01-04 | 2019-01-05 |
| succeeded | 2019-01-06 | 2019-01-06 |
+--------------+--------------+--------------+
結(jié)果忽略了 2018 年的記錄,因為我們只關(guān)心從 2019-01-01 到 2019-12-31 的記錄
從 2019-01-01 到 2019-01-03 所有任務成功,系統(tǒng)狀態(tài)為 "succeeded"。
從 2019-01-04 到 2019-01-05 所有任務失敗,系統(tǒng)狀態(tài)為 "failed"。
從 2019-01-06 到 2019-01-06 所有任務成功,系統(tǒng)狀態(tài)為 "succeeded"。
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/report-contiguous-dates
著作權(quán)歸領扣網(wǎng)絡所有。商業(yè)轉(zhuǎn)載請聯(lián)系官方授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
代碼中使用一個排名的序號和最小值得差值,進行分組!只要是連續(xù)的區(qū)間的,這個差值一定是相同
/* Write your PL/SQL query statement below */
SELECT *
FROM (
SELECT 'failed' AS period_state, to_char(MIN(fail_date), 'yyyy-MM-dd') AS start_date
, to_char(MAX(fail_date), 'yyyy-MM-dd') AS end_date
FROM (
SELECT a.fail_date, fail_date - minDate - rn AS groupId
FROM (
SELECT a.fail_date, rownum AS rn, MIN(fail_date) OVER (ORDER BY fail_date) AS minDate
FROM Failed a
WHERE a.fail_date >= '2019-01-01'
AND a.fail_date <= '2019-12-31'
) a
) a
GROUP BY a.groupId
UNION ALL
SELECT 'succeeded' AS period_state, to_char(MIN(success_date), 'yyyy-MM-dd') AS start_date
, to_char(MAX(success_date), 'yyyy-MM-dd') AS end_date
FROM (
SELECT a.success_date, success_date - minDate - rn AS groupId
FROM (
SELECT a.success_date, rownum AS rn, MIN(success_date) OVER (ORDER BY success_date) AS minDate
FROM Succeeded a
WHERE a.success_date >= '2019-01-01'
AND a.success_date <= '2019-12-31'
) a
) a
GROUP BY a.groupId
) a
ORDER BY a.start_date
找連續(xù)區(qū)間
表:Logs
+---------------+---------+
| Column Name | Type |
+---------------+---------+
| log_id | int |
+---------------+---------+
id 是上表的主鍵。
上表的每一行包含日志表中的一個 ID。
后來一些 ID 從 Logs 表中刪除。編寫一個 SQL 查詢得到 Logs 表中的連續(xù)區(qū)間的開始數(shù)字和結(jié)束數(shù)字。
將查詢表按照 start_id 排序。
查詢結(jié)果格式如下面的例子:
Logs 表:
+------------+
| log_id |
+------------+
| 1 |
| 2 |
| 3 |
| 7 |
| 8 |
| 10 |
+------------+
結(jié)果表:
+------------+--------------+
| start_id | end_id |
+------------+--------------+
| 1 | 3 |
| 7 | 8 |
| 10 | 10 |
+------------+--------------+
結(jié)果表應包含 Logs 表中的所有區(qū)間。
從 1 到 3 在表中。
從 4 到 6 不在表中。
從 7 到 8 在表中。
9 不在表中。
10 在表中。
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/find-the-start-and-end-number-of-continuous-ranges
著作權(quán)歸領扣網(wǎng)絡所有。商業(yè)轉(zhuǎn)載請聯(lián)系官方授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
# Write your MySQL query statement below
select Min(b.log_id) start_id,Max(b.log_id) end_id
from
(select l.log_id,if(@last_vaule=-1 or l.log_id-@last_vaule<>1,@group_id:=@group_id+1,@group_id) group_id,@last_vaule:=l.log_id
from Logs l,(select @last_vaule:=-1,@group_id:=0) a) b
group by b.group_id