HackerRank Simply SQL Contest参加記

www.hackerrank.com

SQLについては素人なのでググりながらやりました。

RDBMSMySQLを選択しました(本当はSQLitePostgreSQLを選びたかったのですが,HackerRankがDB2, MySQL, Oracle, SQL Serverしか対応していなかったので)。

The Blunder

お給料の平均値を計算したかったのですが,Samanthaさんはお給料を入力するときに0を入力できませんでした。正しい平均値と,Samanthaが計算した平均値の差を求めてceilしてください。

最初の問題からかなりググってました。Samanthaさんの入力を計算する (10203 → 123) には,SalaryをCASTで文字列にして,REPLACEで'0'を消せばよさそうですね。

今気づいたんですが,AVGに文字列を渡すと勝手に数値型に変換されるんですね…(こわい)

SELECT CEIL(
  ABS(AVG(REPLACE(CAST(Salary AS CHAR), '0', '')) - AVG(Salary))
  ) FROM Employees;

Type of Triangle

三角形の三辺の長さが与えられるので,どんな三角形か出力してください。

CASE WHENをゴリゴリ書きました

[Mysql] SQLでif文を使う - Qiita

SELECT (CASE 
        WHEN A >= B + C OR B >= C + A OR C >= A + B THEN 'Not A Triangle'
        WHEN A = B AND B = C THEN 'Equilateral'
        WHEN A = B OR B = C OR C = A THEN 'Isosceles'
        ELSE 'Scalene'
        END) FROM Triangles;

Higher Than 75 Marks

点数が75点よりも大きい人を,名前の後ろの三文字で並べ替えてください。

このあたりから,SQL予約語を大文字で書くのが面倒になってきたので小文字で書き始めました。

部分文字列を取得する関数はRDBMSによってSUBSTRだったりSUBSTRINGだったりするようです。MySQLではSUBSTRINGでした。

SUBSTRING:文字列から位置を指定して文字列を取り出す

文字列長はCHAR_LENGTHで取れるようです。たぶん文字コード絡みで面倒なことがあるんだろうな…

CHAR_LENGTH:文字列の文字数を調べる

select Name
  from Students
  where Marks > 75
  order by substring(Name, char_length(Name)-2), Id;

The PADS

名前+職業の頭文字を表示した後に,それぞれの職業の人が何人いるか表示してください。

printfみたいな関数がないので,CONCAT関数で原始的に繋げるしかなくて残念。

select concat(Name, '(', substring(Occupation, 1, 1), ')')
  from Occupations
  order by Name;
select concat('There are total ', count(*), ' ', lower(Occupation), 's.')
  from Occupations
  group by Occupation
  order by count(*);

Occupations

例に示すような表を作ってください

全くわからない… FOR文使えないとダメなんでしょうか?

The Report

点数 (Marks) と,それに対応するGradeのMin_MarkとMax_Markの情報が与えられるので,Gradeが8以上の人はGradeで降順,Nameで昇順に,Gradeが8未満の人はGradeで降順,Marksで昇順に並べて,名前はNULLでマスクしてください。

オリジナルの問題文からは,Gradeが8割未満の人もGradeで降順に並べないといけないのかどうか読み取りにくいです。

同じJOIN句を繰り返して書いてしまったので反省。おそらく繰り返し書かずに済む方法があると思うのですが,どうなんでしょうか?

select Name, Grade, Marks
  from Students join Grades on Marks between Min_Mark and Max_Mark
  where Grade >= 8
  order by Grade desc, Name;
select NULL, Grade, Marks
  from Students join Grades on Marks between Min_Mark and Max_Mark
  where Grade < 8
  order by Grade desc, Marks;

Binary Search Tree

木のノードの情報が (ノード番号, 親のノード番号) のリストとして与えられるので,それぞれのノードが葉, 根, それ以外のどれであるか表示してください。

特に指定はありませんが,ノード番号で降順に並べないとダメです。

select N, (case 
        when isnull(P) then 'Root'
        when (select count(*) from BST as BST2 where BST2.P = BST.N) = 0 then 'Leaf'
        else 'Inner'
        end) from BST order by N;

Symmetric Pairs

ある関数Fがあり,Y=F(X)です。X_1=Y_2 && Y_1=X_2 となるような組をSymmetric Pairと呼ぶことにします。Symmetric Pairを列挙してください。

テストケースが合いませんでした。

サンプルに{x=20, y=20}というタプルが2つあるのだけど,RDBって全属性が同じタプルを持っていてはいけなかったのでは?

Projects

プロジェクトがStart_DateとEnd_Dateの組として与えられます。one.End_Date=other.Start_Dateとなっている2つのプロジェクトは同じプロジェクトとみなします。そのようなプロジェクトをまとめあげて表示してください。

one.End_Date=other.Start_Date という条件でグループ分けする方法が分からなくて解けませんでした。誰か教えてください。

Placements

その人のbest friendよりも給料が低い人を列挙してください。

IDという属性名があまり考えられずに乱用されている例ですよね。

select Students.Name
  from Students
  join Friends on Students.ID = Friends.ID
  join Packages as P1 on Students.ID = P1.ID
  join Packages as P2 on Friends.Friend_ID = P2.ID
  where P1.Salary < P2.Salary
  order by P2.Salary;