牛客網sql刷題解析-完結


查找最晚入職員工的所有信息

 

 

  解題步驟:

   題目:查詢最晚入職員工的所有信息
        目標:查詢員工的所有信息
   篩選條件:最晚入職
           答案:

 1 SELECT
 2     *--查詢所有信息就用*
 3 FROM
 4     employees
 5 WHERE
 6     hire_date = (--這里是一個子查詢,因為要和hire_date匹配,所以只能是一個值,注意max函數使用規則
 7         SELECT
 8             MAX(hire_date)
 9         FROM
10             employees
11     )

 

查找入職員工時間排名倒數第三的員工所有信息

     

 

  解題步驟:

   題目:查找入職員工時間排名倒數第三的員工所有信息
        目標:查詢員工的所有信息
   篩選條件:入職時間到第三
           答案:

 1 SELECT
 2     *--所有信息,用*省事
 3 FROM
 4     employees
 5 where hire_date = ( SELECT DISTINCT--子查詢,注意hire_date是一個值,子查詢的返回值一定要是一個
 6                         hire_date
 7                     FROM
 8                         employees
 9                     ORDER BY--這里有一個小技巧,倒序排,從第三條取,去一條
10                         hire_date DESC
11 limit 2,1--分頁語法要仔細看看,limit m,n=> 從m+1開始取,取n條
12 )

 

查找各個部門當前(to_date='9999-01-01')領導當前薪水詳情以及其對應部門編號dept_no

 

解題步驟:

   題目:查找各個部門當前(to_date='9999-01-01')領導當前薪水詳情以及其對應部門編號dept_no
        目標:查詢領導薪水詳情,對應部門編號
   篩選條件:部門表當前時間,隱藏的條件(薪水表當前時間)
           答案:

1 SELECT
2     s.*,
3     d.dept_no
4 FROM
5     salaries       s--左聯到部門表,薪水表人全,所以做主表比較好,不會出現關聯出空的情況
6     LEFT JOIN dept_manager   d ON s.emp_no = d.emp_no
7 WHERE
8     s.TO_DATE = '9999-01-01'--篩選條件
9     AND d.TO_DATE = '9999-01-01'

 

 查找所有已經分配部門的員工的last_name和first_name

 

 

 

解題步驟:

   題目:查找所有已經分配部門的員工的last_name和first_name
        目標:查詢員工的 last_name,first_name,題目隱藏要顯示dept_no
   篩選條件:已分配部門的員工
           答案:

1 SELECT
2     e.last_name,
3     e.first_name,
4     d.dept_no
5 FROM
6     employees   e--通過左聯,確認員工已分配部門
7     LEFT JOIN dept_emp    d ON d.emp_no = e.emp_no
8 WHERE
9     d.dept_no != ''--防止關聯為空

 查找所有員工的last_name和first_name以及對應部門編號dept_no,也包括展示沒有分配具體部門的員工

 

解題步驟:

   題目:查找所有員工的last_name和first_name以及對應部門編號dept_no,也包括展示沒有分配具體部門的員工
        目標:查詢員工的 last_name,first_name,題目隱藏要顯示dept_no,沒有分配具體部門的員工
   篩選條件:已分配部門的員工
           答案:

1 SELECT
2     ep.last_name,
3     ep.first_name,
4     dp.dept_no
5 FROM
6     employees   ep --人員信息表為主表,左聯,一位部門可能為空,所以關聯后就會包含未分配部分的人
7     LEFT JOIN dept_emp    dp ON ep.emp_no = dp.emp_no

 查找所有員工入職時候的薪水情況,給出emp_no以及salary, 並按照emp_no進行逆序

 

解題步驟:

   題目:查找所有員工入職時候的薪水情況,給出emp_no以及salary, 並按照emp_no進行逆序
        目標: 查詢薪水情況,顯示emp_no以及salary(select 要顯示的字段)
   篩選條件:員工入職時間,並按照emp_no進行逆序
           答案:

1 SELECT --顯示字段
2     e.emp_no,
3     s.salary
4 FROM
5     employees   e
6     LEFT JOIN salaries    s ON e.emp_no = s.emp_no --確定是同一個人
7                             AND e.hire_date = s.from_date --確定是入職時間
8 ORDER BY
9     e.emp_no DESC --倒序

 查找薪水漲幅超過15次的員工號emp_no以及其對應的漲幅次數t

 

 

解題步驟:

   題目:查找薪水漲幅超過15次的員工號emp_no以及其對應的漲幅次數t
        目標: 查找員工號,漲幅次數(select 要顯示的字段)
   篩選條件:漲幅超過15次
           答案:

1 SELECT
2     emp_no,
3     SUM(1) --統計次數;
4 FROM
5     salaries
6 GROUP BY
7     emp_no --對每一個員工進行分組,然后統計其漲幅次數
8 HAVING
9     COUNT(1) > 15 --進行次數過濾

 

如果想詳細了解 sum(1),可以看這個文章:https://blog.csdn.net/qq_39313596/article/details/80623495

 

找出所有員工當前(to_date='9999-01-01')具體的薪水salary情況,對於相同的薪水只顯示一次,並按照逆序顯示

 

 

解題步驟:

   題目:找出所有員工當前(to_date='9999-01-01')具體的薪水salary情況,對於相同的薪水只顯示一次,並按照逆序顯示
        目標: 查找薪水(select 要顯示的字段)
   篩選條件:當前時間(to_date),相同的僅顯示一次,逆序顯示
           答案:

SELECT DISTINCT --去重
    salary
FROM
    salaries
WHERE
    TO_DATE = '9999-01-01'
ORDER BY
    salary DESC --倒序

 

獲取所有部門當前manager的當前薪水情況,給出dept_no, emp_no以及salary,當前表示to_date='9999-01-01'

 

 

解題步驟:

   題目:獲取所有部門當前manager的當前薪水情況,給出dept_no, emp_no以及salary,當前表示to_date='9999-01-01'
        目標: 查找dept_no, emp_no以及salary(select 要顯示的字段)
   篩選條件:當前表示to_date='9999-01-01'
           答案:

 1 SELECT
 2     d.dept_no,
 3     s.emp_no,
 4     s.salary
 5 FROM --注意左關聯條件
 6     dept_manager   d
 7     LEFT JOIN salaries       s ON d.emp_no = s.emp_no
 8                             AND d.TO_DATE = s.TO_DATE
 9 WHERE
10     d.TO_DATE = '9999-01-01'

 

獲取所有非manager的員工emp_no

 

 

 

解題步驟:

   題目:獲取所有非manager的員工emp_no
        目標: 查找emp_no(select 要顯示的字段)
   篩選條件:非manager的員工
           答案:

 1 SELECT
 2     em.emp_no
 3 FROM
 4     employees em
 5 WHERE
 6     NOT EXISTS ( --此處用了一個exists表達式,是管理者的員工,然后將這些排除就時非管理員的員工,此處也可以用not in,但是效率會降低一些
 7         SELECT
 8             1
 9         FROM
10             dept_manager dm
11         WHERE
12             em.emp_no = dm.emp_no
13     )

 

題外閑談:exists,in;語法上區別,效率上區別;沒有覺得效率高低,看實際場景

這倆篇文章說的不錯,再次就不再贅述,唯一要注意的是exists的返回值是真或者假:https://blog.csdn.net/baidu_37107022/article/details/77278381https://www.cnblogs.com/xuyufengme/p/9175929.html

 獲取所有員工當前的manager,如果當前的manager是自己的話結果不顯示,當前表示to_date='9999-01-01'。

 

 

解題步驟:

   題目:獲取所有員工當前的manager,如果當前的manager是自己的話結果不顯示,當前表示to_date='9999-01-01'
        目標: 查找員工,當前的manager(select 要顯示的字段)
   篩選條件:當前表示to_date='9999-01-01',如果當前的manager是自己的話結果不顯示
           答案:

 1 SELECT
 2     de.emp_no,
 3     dm.emp_no AS manager_no
 4 FROM
 5     dept_emp       de
 6     LEFT JOIN dept_manager   dm ON de.dept_no = dm.dept_no
 7 WHERE
 8     de.emp_no != dm.emp_no
 9     AND de.TO_DATE = '9999-01-01'
10     AND dm.TO_DATE = '9999-01-01'

 

獲取所有部門中當前員工薪水最高的相關信息,給出dept_no, emp_no以及其對應的salary

 

 

 

解題步驟:

   題目:獲取所有部門中當前員工薪水最高的相關信息,給出dept_no, emp_no以及其對應的salary
        目標: 查找部門,員工,薪水(select 要顯示的字段)
   篩選條件:當前表示to_date='9999-01-01',
           答案:

 1 SELECT
 2     de.dept_no,
 3     de.emp_no,
 4     MAX(sa.salary) --注意函數使用時機,什么時候需要group by,什么時候不需要
 5 FROM
 6     dept_emp   de
 7     LEFT JOIN salaries   sa ON de.emp_no = sa.emp_no
 8 WHERE
 9     de.TO_DATE = '9999-01-01'  --題目中默認為當前時間
10     AND sa.TO_DATE = '9999-01-01'
11 GROUP BY
12     de.dept_no --每個部門中最高的薪水的人,所以需要按照部門分組

 

 

從titles表獲取按照title進行分組,每組個數大於等於2,給出title以及對應的數目t。

 

 

 

解題步驟:

   題目:從titles表獲取按照title進行分組,每組個數大於等於2,給出title以及對應的數目t。
        目標: 查找title以及對應的數目t(select 要顯示的字段)
   篩選條件:按照title進行分組,每組個數大於等於2

1 SELECT
2     title,
3     COUNT(1) AS t
4 FROM
5     titles
6 GROUP BY
7     title --分組
8 HAVING --配合分組使用,作用和where差不多
9     COUNT(1) >= 2 

 

從titles表獲取按照title進行分組,注意對於重復的emp_no進行忽略。

 

 

 

解題步驟:

   題目:從titles表獲取按照title進行分組,每組個數大於等於2,給出title以及對應的數目t。
注意對於重復的title進行忽略。
        目標: 查找title以及對應的數目t(select 要顯示的字段)
   篩選條件:按照title進行分組,每組個數大於等於2,注意對於重復的title進行忽略。

1 SELECT
2     title,
3     COUNT(DISTINCT(emp_no)) AS t --本題關鍵是對於重復的title進行忽略。也就是計數的時候要去重,注意 函數和distinct的使用方法
4 FROM
5     titles
6 GROUP BY
7     title
8 HAVING
9     COUNT(1) >= 2

 

關於count(distinct)的延伸

sql-按條件統計非重復值,count(distinct case when)使用
背景

項目中,遇到一個統計需求,從某張表中按照條件分別統計。剛開始想到用union all的寫法,太臃腫,后來使用count(distinct case when)解決此問題

count

數據統計中,count出現最頻繁

最簡單的用法

select count(*) from table where ....

select count(distinct xx) from table where ...

但最簡單的用法也有其深刻的地方,比如這里其實有3種寫法,count(1)、count(*)、count(字段),它們有什么區別呢?

count(1) 和 count(*)
count(1)和count(*)差別不大,使用count(*)時,sql會幫你自動優化,指定到最快的字段。所以推薦使用count(*)

count(*) 和 count(字段)
count(*)會統計所有行數,count(字段)不會統計null值

count(case when)

條件統計,即對某個table分條件統計,比如表test_db,有一個字段user_id(可能重復), gender(man、women),需要統計man和women的人數

可以使用where分別統計

select count(distinct user_id) as man_cnt from test_db where gender = 'man'

select count(distinct user_id) as women_cnt from test_db where gender = 'women'

也可以使用按條件統計

select count(distinct case gender = 'man' then user_id end) as man_cnt  --至於case when,本人沒用過,不過也很少有這個使用場景吧

, count(distinct case gender = 'women' then user_id end) as woman_cnt

from test_db

 

 

查找employees表所有emp_no為奇數,且last_name不為Mary的員工信息,並按照hire_date逆序排列

 

 

解題步驟:

   題目:查找employees表所有emp_no為奇數,且last_name不為Mary的員工信息,並按照hire_date逆序排列
        目標: 查找所有員工信息(select 要顯示的字段)
   篩選條件:ast_name不為Mary,emp_no為奇數

1 SELECT
2     *
3 FROM
4     employees
5 where emp_no%2 != 0 --如果emp_no為索引列,會導致索引失效
6              AND last_name != 'Mary'
7 order by hire_date desc

 拓展:索引列上計算引起的索引失效及優化措施以及注意事項索引失效的情況有哪些?索引何時會失效?(全面總結)

 

兩個示例
例子一

表結構

DROP TABLE IF EXISTS `account`;
CREATE TABLE IF NOT EXISTS `account` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `account` int(10) unsigned NOT NULL,
  `password` char(32) NOT NULL,
  `ip` char(15) NOT NULL,
  `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `time` (`time`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
比如要統計2012年08月15日注冊的會員數:

SELECT count(id) FROM account WHERE DATEDIFF("2012-08-15",time)=0
 例子二

表結構

DROP TABLE IF EXISTS `active`;
CREATE TABLE IF NOT EXISTS `user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `userid` int(10) unsigned NOT NULL,
  `lastactive` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `lastactive` (`lastactive`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
統計最近3分鍾的活躍用戶

SELECT count(id) FROM user WHERE unix_timstamp()-lastactive < 180

以上兩個例子中,雖然都建有索引,但是SQL執行中卻不走索引,而采用全表掃描。

原因揭密
SQL語句where中如果有functionName(colname)或者某些運算,則MYSQL無法使用基於colName的索引。使用索引需要直接查詢某個字段。

索引失效的原因是索引是針對原值建的二叉樹,將列值計算后,原來的二叉樹就用不上了;

為了解決索引列上計算引起的索引失效問題,將計算放到索引列外的表達式上。

解決辦法
例子一:SELECT count(id) FROM account WHERE time between "2012-08-15 00:00:00" and "2012-08-15 23:59:59"

例子二:SELECT count(id) FROM user WHERE lastactive > unix_timstamp() - 180

相關內容
1、如果對時間字段進行查找,可以將時間設置為int unsigned類型,存取UNIX時間戳。因為整型比較速度快
2、當我們執行查詢的時候,MySQL只能使用一個索引。
3、MySQL只有對以下操作符才使用索引: <<==>>=BETWEENIN ,以及某些時候的LIKE。可以在LIKE操作中使用索引的情形是指另一個操作數不是以通配符( % 或者 _ )開頭的情形。例如, “SELECT peopleid FROM people WHERE firstname LIKE 'Mich%';” 這個查詢將使用索引,但 “SELECT peopleid FROM people WHERE firstname LIKE '%ike';” 這個查詢不會使用索引。

不得不說
創建索引、優化查詢以便達到更好的查詢優化效果。但實際上,MySQL有時並不按我們設計的那樣執行查詢。MySQL是根據統計信息來生成執行計划的,這就涉及索引及索引的刷選率,表數據量,還有一些額外的因素。

Each table index is queried, and the best index is used unless the optimizer believes that it is more efficient to use a table scan. At one time, a scan was used based on whether the best index spanned more than 30% of the table, but a fixed percentage no longer determines the choice between using an index or a scan. The optimizer now is more complex and bases its estimate on additional factors such as table size, number of rows, and I/O block size.

簡而言之,當MYSQL認為符合條件的記錄在30%以上,它就不會再使用索引,因為mysql認為走索引的代價比不用索引代價大,所以優化器選擇了自己認為代價最小的方式。事實也的確如此

實例檢測
表結構

DROP TABLE IF EXISTS `active`;
CREATE TABLE IF NOT EXISTS `active` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `userid` int(10) unsigned NOT NULL,
  `lastactive` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  KEY `lastactive` (`lastactive`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
插入數據

insert into active values
(null,10000, unix_timestamp("2012-08-20 15:10:02")),
(null,10001, unix_timestamp("2012-08-20 15:10:02")),
(null,10002, unix_timestamp("2012-08-20 15:10:03")),
(null,10003, unix_timestamp("2012-08-20 15:10:03")),
(null,10004, unix_timestamp("2012-08-20 15:10:03")),
(null,10005, unix_timestamp("2012-08-20 15:10:04")),
(null,10006, unix_timestamp("2012-08-20 15:10:04")),
(null,10007, unix_timestamp("2012-08-20 15:10:05")),
(null,10008, unix_timestamp("2012-08-20 15:10:06"))
explain select * from active where lastactive > unix_timestamp()-3;

上面這句索引起作用。


但是我在測試中,因為插入的日期與我測試的當前日期相差不少時間。所以我改寫為以下內容:

explain select * from active where lastactive > unix_timestamp("2012-08-20 15:10:06") - 3;

但是數據顯示,TYPE為ALL,key為NULL。也就是說索引不起作用。

我在改寫以下語句測試:

explain select * from active where lastactive > unix_timestamp("2012-08-20 15:10:06");

上面這個語句,索引又起作用了。

一個疑惑
正好手頭上有一個12016條記錄的數據,證實一下“當MYSQL認為符合條件的記錄在30%以上,它就不會再使用索引”的結論。經過測試,在總記錄12016條記錄的表中,查詢小於1854條記錄時走索引,大於該記錄時不走索引。符合條件的記錄在15.4%。這....,30%的數據可能有待確認,正如上面說的那樣,MySQL的優化器是考慮多方面因素,並選擇自己認為代價最小的方式。

mysql自己判斷是否使用索引,如果你自己確信使用索引可以提高效率,你也可以強行實用索引force index(index_name)

 

統計出當前各個title類型對應的員工當前(to_date='9999-01-01')薪水對應的平均工資。結果給出title以及平均工資avg。

 

 

解題步驟:

   題目:統計出當前各個title類型對應的員工當前(to_date='9999-01-01')薪水對應的平均工資。結果給出title以及平均工資avg。
        目標: 查找結果給出title以及平均工資avg。(select 要顯示的字段)
   篩選條件:默認為當前時間

 1 SELECT
 2     t.title,
 3     AVG(s.salary) AS avg
 4 FROM
 5     salaries   s
 6     LEFT JOIN titles     t ON s.emp_no = t.emp_no
 7 WHERE
 8     s.TO_DATE = '9999-01-01'
 9     AND t.TO_DATE = '9999-01-01'
10 GROUP BY
11     t.title

 

獲取當前(to_date='9999-01-01')薪水第二多的員工的emp_no以及其對應的薪水salary

 

      題目:獲取當前(to_date='9999-01-01')薪水第二多的員工的emp_no以及其對應的薪水salary
        目標: 查找emp_no以及其對應的薪水salary(select 要顯示的字段)
   篩選條件:薪水第二多

 1 SELECT
 2     emp_no,
 3     salary
 4 FROM
 5     salaries
 6 WHERE
 7     TO_DATE = '9999-01-01'
 8 ORDER BY
 9     salary DESC --本題精髓,第二多,可以理解成倒序排第二的人
10 limit
11     1,1 --從第二條開始,去一條;也就是取得第二條

 查找員工編號emp_no為10001其自入職以來的薪水salary漲幅值growth

 

 

      題目:查找員工編號emp_no為10001其自入職以來的薪水salary漲幅值growth
        目標: 查找漲幅值growth(select 要顯示的字段)
   篩選條件:自入職以來,號emp_no為10001

1 SELECT
2     MAX(salary) - MIN(salary) AS growth --函數的使用,計算,以及起別名 as的語法
3 FROM
4     salaries
5 WHERE
6     emp_no = '10001';

 

 針對actor表創建視圖actor_name_view,只包含first_name以及last_name兩列,並對這兩列重新命名,first_name為first_name_v,last_name修改為last_name_v:

 

 

     終於到了聽起來牛逼點的題目啦!!!,其實也沒啥~~~,還是查詢

     題目:針對actor表創建視圖actor_name_view,只包含first_name以及last_name兩列,並對這兩列重新命名,first_name為first_name_v,last_name修改為last_name_v:
        目標: 創建這個視圖
   篩選條件:

1 CREATE VIEW actor_name_view AS
2     SELECT
3         first_name   AS first_name_v,
4         last_name    AS last_name_v
5     FROM
6         actor

 

 針對salaries表emp_no字段創建索引idx_emp_no,查詢emp_no為10005, 使用強制索引。

 

        題目:針對salaries表emp_no字段創建索引idx_emp_no,查詢emp_no為10005, 使用強制索引。
        目標:使用強制索引
   篩選條件:

1 SELECT
2     *
3 FROM
4     salaries indexed
5 by idx_emp_no --使用強制索引
6 WHERE
7              emp_no = 10005

 

拓展:使用強制索引的案例

 

在last_update后面新增加一列名字為create_date

        題目:修改表結構,用sql(這樣比手寫顯得牛逼)
        目標:alter用法
   篩選條件:

1 ALTER TABLE actor ADD 'create_date' datetime NOT NULL DEFAULT '0000-00-00 00:00:00'

構造一個觸發器audit_log,在向employees_test表中插入一條數據的時候,觸發插入相關的數據到audit中。

 

        題目: 構造一個觸發器audit_log,在向employees_test表中插入一條數據的時候,觸發插入相關的數據到audit中。(聽着就很高級,雖然實際中從未用過)
        目標:
   篩選條件:

1 CREATE TRIGGER audit_log AFTER 
2     INSERT ON employees_test
3 BEGIN
4     INSERT INTO audit VALUES (
5         new.id,
6         new.name
7     );
8 
9 END;

 

使用含有關鍵字exists查找未分配具體部門的員工的所有信息。

 

        題目: 使用含有關鍵字exists查找未分配具體部門的員工的所有信息。
        目標:查找員工的所有信息。
   篩選條件:未分配具體部門

 1 SELECT
 2     *
 3 FROM
 4     employees e
 5 WHERE
 6     NOT EXISTS ( --就是exists的使用,上面有詳細講解過的,注意返回值是邏輯真假
 7         SELECT
 8             1
 9         FROM
10             dept_emp de
11         WHERE
12             e.emp_no = de.emp_no
13     )

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM