问题
有些时候希望spark计算完的统计数据直接已邮件的形式发送。在这其中有一些点和技巧记录一下
1.拼接邮件HTML
调取公司发邮件接口的时候,传入的是html格式的字符传,一开始这个地方最繁琐麻烦的可能就是拼接html,但是scala支持直接写html,这个比较实用,写起来很方便。
包括标签,样式都可以直接写。
/**
* 拼接HTML Table
* @param data
* @return
*/
def generateTable(columns:List[String],data: Array[Row]) = {
<table cellpadding="4" style="border: 1px solid #000000; border-collapse: collapse;" border="1">
{
<TR style="background-color:#BDD6EE;">{
columns.map(column =>
<TH align="center" style="text-align:center; margin:0;border:solid #000 1px;">{column}</TH>)
}</TR>
<TR>{data.map{row=>
<tr>{row.toSeq.toList.map(cell =>
<th align="center" style="text-align:center; margin:0;border:solid #000 1px;">{cell}</th>)}</tr>}
}</TR>
}
</table>
}
def generateTable(data: List[List[Any]]) = {
<table cellpadding="4" style="border: 1px solid #000000; border-collapse: collapse;" border="1">
{data.zipWithIndex.map{ case (row,rownum) =>
if((rownum == 0)){
<tr>
{row.map(cell =>
<th align="center" style="font-size:large">
{cell}
</th>)}
</tr>
}else{
<tr>
{row.map(cell =>
<td>
{cell}
</td>)}
</tr>}
}
}
</table>
}
2.日期处理
这个地方记录下这次使用的结果日期处理函数吧,省的下次到处找,找不到反复写工具类
/**
* 生成前N天
*/
public static String getDateBeforeOrAfter(int num) {
return genYmdFromTs(System.currentTimeMillis() + (num * DateTimeUtil.TIME_DAY_MILLISECOND));
}
public static String genLastDayStr() {
return getDateBeforeOrAfter(-1);
}
/**
* 获取当前日期对应的上周日
*/
public static String getLastSunday(String yyyyMMdd) throws ParseException {
return getAfterYymmdd(yyyyMMdd, -1);
}
3.计算TopN
这个很常识,使用row_number() over ,但是使用的 时候经常写错,还得百度谷歌,这个地方记一下
// 各品牌销售top 15
select brand,marketName,row_number() over ( partition by brand order by cnt desc) rank from ( select brand,marketName,count(*) as cnt from new_user_df_days where firstDate = '${yyyyMMdd}' and marketName!= '' group by brand,marketName ) a having rank <= ${topN}
4.dataframe的join
没啥说的 写两个示例,分别是on 一个字段和多个字段。
// 默认inner join,可以参数选择leftjoin等
val df_result = df_l.join(df_r,df_l("col1")===df_r("col2")).drop("col2")
// 这个on两个字段,字段名要相同,join完后自动保留一列
val df_result = df_l.join(df_r,Seq("colA","colB"))
5.dataframe 增加一列
增加一列,并写下增加的这一列的计算逻辑,可以用udf
val df_result = df_1.withColumn("new_col_name",df_1("col_1") / (df_1("col_2") + 1))
6.计算月均
//计算月均,这里是上月月均,头疼的是对分母的处理
var (year, month) = (yyyyMMdd.substring(0, 4).toInt, yyyyMMdd.substring(4, 6).toInt)
if (month == 12) year = year -1
val last_month = if(month == 12) 1 else month -1
val fds = if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) 29 else 28
val month2Days: Map[Int, Int] = Map(1 -> 31, 2 -> fds, 3 -> 31, 4 -> 30, 5 -> 31, 6 -> 30,
7 -> 31, 8 -> 31, 9 -> 30, 10 -> 31, 11 -> 30, 12 -> 31)
println(month2Days(last_month))
7.显示百分比
需要显示成 99.8%的形式
concat(cast(round(day_ratio * 100,1) as string),'%') as d_ratio
8. repartition(1) 和 orderby
如果是存csv的话需要repartition(1),但是oderby的效果就没了,再orderby就又默认200分区,我还没找到好的办法。
不过确认的一点是orderby后collect是保留顺序的。返回一个array也相当于一个文件了。