sql解决问题思路

表模型

image.png

1.逆向思维

Not IN

Example:
查询没学过"张三"老师授课的同学的信息

SELECT * FROM student a 
WHERE a.sid NOT IN(
      SELECT a.sid 
      FROM student a, sc b, course c , teacher d 
      WHERE a.sid = b.sid AND b.cid = c.cid AND c.tid = d.tid AND d.tname = '张三'
     );

2.在....但不在

IN + NOT IN

Example:
查询学过编号为"01"但是没有学过编号为"02"的课程的同学的信息

SELECT * FROM student a
WHERE a.sid NOT IN(SELECT sid FROM sc b WHERE b.cid = '02')
AND  a.sid  IN(SELECT sid FROM sc c WHERE c.cid = '01' );
SELECT Student.* FROM Student , SC 
WHERE Student.sid = SC.sid AND SC.cid = '01' 
               AND EXISTS 
               (SELECT 1 FROM SC SC_2 WHERE SC_2.sid = SC.sid AND SC_2.cid = '02') 
ORDER BY Student.sid

3.完全词语(双重否定,去除部分存在)

NOT IN + NOT IN

Example:
查询和"01"号的同学学习的课程完全相同的其他同学的信息

 SELECT * FROM student WHERE sid IN(  
      SELECT sid FROM sc 
     WHERE sid NOT IN (SELECT sid FROM sc WHERE cid NOT IN(SELECT cid 
                        FROM sc 
                        WHERE sid = '01'))
  GROUP BY sid HAVING COUNT(cid) = (SELECT COUNT(cid) FROM sc WHERE sid = '01') AND sid != '01'
 )
拆解sql分析
image.png
SELECT sid FROM sc WHERE cid NOT IN(SELECT cid 
                        FROM sc 
                        WHERE sid = '01')

这部分就表示图中 2,4

SELECT * FROM student WHERE sid IN(  
      SELECT sid FROM sc 
     WHERE sid NOT IN (SELECT sid FROM sc WHERE cid NOT IN(SELECT cid 
                        FROM sc 
                        WHERE sid = '01'))

这部分表示图中1,3,所以最后分组求和判断课程数相同就得到完全一致的。
这题用正向的思维是,不好做的。总是会出现部分相同,使用这种反向双重否定的方式去处理“完全”这种sql最好

4.将列转为行

case when ...then .... else .....end

Example:
显示所有学生的所有课程的成绩以及平均成绩,按平均成绩从高到低排序

SELECT * ,
  MAX(CASE c.cname WHEN '语文' THEN b.score ELSE  0 END) 语文,
  MAX(CASE c.cname WHEN '数学' THEN b.score ELSE  0 END) 数学,
  MAX(CASE c.cname WHEN '英语' THEN b.score ELSE  0 END) 英语,
  CAST(AVG(b.score) AS DECIMAL(10,2)) avg_score
  FROM student a
  LEFT JOIN sc b ON  a.sid = b.sid 
  LEFT JOIN course c  ON b.cid = c.cid
  GROUP BY a.sid
  ORDER BY avg_score DESC

max函数一定要加,使用学生id分组后的语文,数学,英语在一行上

5.求某个范围的总人数

Sum(condition)

Example:
查询各科成绩最高分、最低分和平均分:以如下形式显示:课程ID,课程name,最高分,最低分,平均分,及格率,中等率,优良率,优秀率
-- 及格为>=60,中等为:70-80,优良为:80-90,优秀为:>=90

  SELECT * ,
     MAX(b.score) 最高分,
     MIN(b.score) 最低分,
     CAST(AVG(b.score)  AS DECIMAL(10,2))平均分,
     CAST(SUM(b.score >= 60) /COUNT(*) AS DECIMAL(10,2)) * 100 及格率,
     CAST(SUM(b.score >= 70 AND b.score <= 80) / COUNT(*) AS DECIMAL(10,2)) * 100 中等率
  FROM course a, sc b
  WHERE a.cid = b.cid 
  GROUP BY a.cid

6.计算排名的方式

1> 通过表自关联+关键字段筛选条件+左外连接

Example:
按各科成绩进行排序,并显示排名

   SELECT a.*,COUNT(b.score)+1 FROM sc a 
   LEFT JOIN sc b 
   ON a.cid = b.cid AND (a.score < b.score)
   GROUP BY a.cid,a.sid
   ORDER BY a.cid,COUNT(b.score) 

自关联:通过自己与本组的人相比,有多少人小于我并且加一就是排名,这是个数学规律,可以自验证
左外连接:必须使用外连接,如果使用内连接,第一名右边为空,就会被过滤掉

2>定义变量+ case when方式

思想:首先排序好,那么从第一个开始比较,不相等就加+,最后数字就是排名

SELECT cid ,score,rk1,rk2,rk3
from
 (SELECT 
  * ,

    IF(@p=b.cid,
       CASE
     WHEN @刚才=b.score THEN @排名
     WHEN @刚才!=b.score THEN @排名:=@排名+1
    END,
    @排名 := 1
    ) as rk1, 

  IF(@p=b.cid,
    case
    when 1=1 then @排:=@排+1
    end,
    @排:=1
    ) as rk2, 

    IF(@p=b.cid,
       CASE
     WHEN @刚=b.score THEN @名
     WHEN @刚!=b.score THEN @名:=@排
    END,
    @名 := 1
    ) as rk3,  

    @p:=b.cid,
    @刚才:=b.score,
    @刚:=b.score
    FROM sc b,(SELECT @p:=NULL,@刚才:=NULL,@才:=NULL,@排名:=0,@排:=0,@名:=0)r
    ORDER BY b.cid ,b.score DESC
 )s;

结果

image.png

rk1 为 1 ,1,2 —— 重复不空缺
rk2 为 1 ,2,3 —— 不重复不空缺
rk3 为 1 1,3 —— 重复空缺

3>hive 开窗函数(doing)

SELECT ref_host,ref_host_cnts,concat(month,day,hour),
row_number() OVER (PARTITION BY concat(month,day,hour) ORDER BY ref_host_cnts DESC) as rk1,
rank() OVER (PARTITION BY concat(month,day,hour) ORDER BY ref_host_cnts DESC) as rk2,
dense_rank() OVER (PARTITION BY concat(month,day,hour) ORDER BY ref_host_cnts DESC) as rk3
FROM dw_pvs_refererhost_everyhour

image.png

7.查询前几名的

思路:先排名然后筛选即可
Example:

SELECT *
FROM (
SELECT s1.*,COUNT(s1.SID) as top
FROM sc s1 LEFT JOIN
sc s2 ON s1.CID = s2.CID AND s1.score < s2.score
GROUP BY s1.CID,s1.SID
ORDER BY s1.CID,top
) AS top_t
WHERE top_t.top < 4

8.多字段交叉去重

去重方式:distinct or group by
思路:自关联+使用条件+NOT IN
多字段交叉去重

9.预测生日,计算年龄

使用函数:
week() year() day() month() now() datediff()

Example:
查询各学生的年龄

SELECT s.*,FLOOR(DATEDIFF(NOW(),s.sage)/365.2422) age
FROM student s

查询下周过生日的学生

SELECT *,WEEK(NOW()) as now_week, WEEK(s.sage) as s_week
FROM student s
WHERE WEEK(s.sage) = WEEK(NOW())+1

总结:

写sql,多使用函数,如果普通思维完成不了使用集合逆向求反的思维

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容