DSL代码风格使用

学习日期: 3.2

所学内容概述

看小白学苑的解法,发现DSL是能解决很多sql语句繁琐需要写字段名的代码。学习了spark的sql方法

sparksql常用方法

withColumn

自己使用一般只用到两种用法

  • 添加列
  • 修改列
1
withColumn("列名","数据")

如上如果列名存在,则是对该列修改。如果列名不存在,则是添加新列

drop

使用过一次,删除一列

1
drop("etl_date")

lit

创建一个常量列 如下lit代表数据为常量结合withColumn使用

1
withColumn("etl_date",lit("20230302"))

col

col中选中为列的数据,如下col内的字段表示该列 oerate_time列

1
withColumn("operate_time", coalesce(col("operate_time"), col("create_time")))

coalesce

该函数和mysql和hive的用法一样,返回第一个不为null的值

1
2
coalesce(col("operate_time"), col("create_time"))
//如果operate_time为null 取create_time为值

createOrReplaceTempView

方法是创建一个临时表和with as 临时表名差不多

  • 如果两次建立的临时表名字一样第一次的会被第二次的数据覆盖
1
spark.sql("select * from ods.user_info").createOrReplaceTempView("tempTable")

partitionBy

设置分区字段,如果表中有该字段自动设置为分区

1
partitionBy("etl_date")

write

写入表的方法,将df.wirte开始写入表,修改参数即可

mode

修改写入表的形式

“overwrite” “append”

saveAsTable

保存DataFrame数据到指定hive表中。分两种情况:表已经存在表不存在

  1. 如果表不存在,则会自动创建表结构。

  2. 如果表已经存在,则此函数的行为取决于由mode函数指定的保存模式(默认情况下抛出异常)。
    a.mode=Overwrite时

    1. 当dataframe的schema与已存在的schema个数相同:DataFrame中的列顺序不需要与现有表的列顺序相同,与insertInto不同,saveAsTable将使用列名称查找正确的列位置。(与insertInto区别点)
    2. 当dataframe的schema与已存在的schema个数不同:会撇弃原有的schema,按照dataframe的schema重新创建并插入。

    b.mode=Append时

    1. 当dataframe的schema与已存在的schema个数相同:DataFrame中的列顺序不需要与现有表的列顺序相同。
    2. 当dataframe的schema与已存在的schema个数不同:会报错。

insertInto

保存DataFrame数据到指定hive表中,但要求满足以下两点:

  1. 指定的hive表是存在的;
  2. DataFrame的schema结构顺序与指定Hive表的schema结构顺序是一致的。

日总结

今天看小白的七月清洗答案,自己豁然开朗了,可以不全使用纯sql写清洗的代码了,之前自己纯sql写完七月的清洗部分代码量是十分巨大的,而且需要写字段,字段名还要一一对齐,不然无法加入表中,今天的任务把dsl风格的代码中数据清洗常用的代码都理解,就花了不少的时间。明天自己写写试。七月的清洗纯sql的无法在规定时间完成的,必须结合DSL。

使用本地VM镜像

学习日期: 3.3

所学内容概述

台式机安装VM,并导入镜像,ssh顺利连接

使用asbru中sftp

使用sftp导出文件到本地,get 需要的文件 导入本地的地址

BUG点

一直报错,看报错信息大致原因是没有一个什么jar包,本地的镜像好像使用的并不是mysql还是org.mariadb库,但是支持的上mysql,就不去搞原理了,把连接jar包放入spark的jars目录下,修改driver为org.mariadb.jdbc.Driver,BUG顺利解决

image-20230303142630541

日总结

今天大量的时间在安装本地虚拟机,h3c服务器坏了,就只能在本地安装了,上午装好了虚拟机,下午使用的时候出了BUG,差不多到晚上才解决,然后进去把环境和sql文件导入了一下,今天的任务就完成了,本来想今天使用新方法做七月的数据清洗的,只能放到明天了。

SQL结合DSL表清洗

学习日期: 3.4

所学内容概述

因为对清洗部分中user表是最难的,后面的表的操作模仿着user就能写完所以只放user表的代码了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def odsClearUser(sparkSession: SparkSession,odsName:String,dwdName:String): Unit ={
//创建df 加入需要的新的四列 只去昨天的数据
val ods_user_info_df: DataFrame = sparkSession.sql(
s"""
|select *,
|'user1' as dwd_insert_user,date_format(current_timestamp(),"yyyy-MM-dd HH:mm:ss") as dwd_insert_time,
|'user1' as dwd_modify_user,date_format(current_timestamp(),"yyyy-MM-dd HH:mm:ss") as dwd_modify_time
|from $odsName
|where
|etl_date = $date
|""".stripMargin)
// 如果operate_time为null 使用create_time的值
.withColumn("operate_time", coalesce(col("operate_time"), col("create_time")))
//删除分ods的分区列 再加入 目的是为了和dwd表合并 并加以处理
.drop("etl_date")
.withColumn("etl_date",lit(date))
//建立临时表
ods_user_info_df.createOrReplaceTempView("ods_data")
//查看表数据
ods_user_info_df.show()
//提取dwd表原有的数据并建立临时表
sparkSession.sql(
s"""
|select * from $dwdName
|""".stripMargin)
// 如果operate_time为null 使用create_time的值
.withColumn("operate_time", coalesce(col("operate_time"), col("create_time")))
.createOrReplaceTempView("dwd_data")
//合并后的表 建立临时表
sparkSession.sql(
"""
|select *
|from ods_data
|union
|select *
|from dwd_data
|""".stripMargin).createOrReplaceTempView("unionTable")
//添加row_number列 根据operate_time排序
sparkSession.sql(
"""
|select *,row_number() over(partition by id order by operate_time desc) as _row_number
|from unionTable
|""".stripMargin)
//只去_row_number为1的
.where("_row_number=1")
//删除排顺序的这列
.drop("_row_number")
//修改dwd_modify_time为当前时间
/*TODO !因为在第一步取ods数据的时候添加的dwd_insert_time dwd_modify_time
都是当前时间 所以题目要求的dwd_modify_time都是要最新值的直接修改合并后的就到达了题目要求*/
.withColumn("dwd_modify_time", date_format(current_timestamp(), "yyyy-MM-dd HH:mm:ss"))
//先放入临时表 防止dwd表又读取又写入的报错
.write.mode("overwrite").partitionBy("etl_date").saveAsTable("tempTable") //建立临时表
//将临时表的内容覆盖写入dwd中的表
sparkSession.table("tempTable")
.write.mode("overwrite").partitionBy("etl_date")
.saveAsTable(dwdName)
//删除临时表
sparkSession.sql("drop table tempTable")
}

BUG点

BUG1

无法将数据导入该表

img

写进dwd的dim_user_info的时候,就会一直报这个错误,我直接从ods抽取前20行数据的时候,写入方法也是这样就是正常的。 找了一上午的原因没解决,没办法只能问小白的老师了。原因是:被写入的表,正在被读取,所以不能写入。先把数据写到一个中间表,再读取中间表写入目标表 。解决办法是先直接写入saveAsTable一个临时表,通过spark.table读取该表数据导入dwd层的表中,再删除表

BUG2

数据导入无法查询,之前就有这样的情况 ,发现除了decimal类型的,其他都能正常查询,搜索以后 说是hive和spark类型需要一致性

解决办法:

创建spark的df 时候加入配置

1
config("spark.sql.parquet.writeLegacyFormat", "true")

image-20230304145810981

日总结

今天的任务是使用DSL结合SQL风格的spark代码,完成任务7中的数据清洗部分。参考了别人的思路,结合自己的想法将代码优化,写了能使用的方法,将表名封装为参数,能直接使用,如果需要的关键字不一样,只要复制方法略微修改一下就好了, 上午解决BUG浪费了很多时间,在晚上才完成一次成功的清洗。

7月任务1指标计算

学习日期: 3.6

所学内容概述

指标计算3题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package gz07.real.subject1

import org.apache.spark.sql.SparkSession

object dataMath {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().master("local[*]").appName("subject1Extract")
.enableHiveSupport()
.config("hive.exec.dynamic.partition", "true")
.config("hive.exec.dynamic.partition.mode", "nonstrict")
.config("spark.sql.parquet.writeLegacyFormat", "true")
.getOrCreate()
import spark.sql
//TODO T1
sql(
s"""
|with t1 as (
|select
|province_id,final_total_amount,
|date_format(operate_time,'yyyy') as year,
|date_format(operate_time,'MM') as month
|from dwd.fact_order_info
|),t2 as (
|select
|province_id as provinceid,sum(final_total_amount) as totalconsumption,
|count(1) as totalorder,
|year,month
|from t1
|group by province_id,year,month)
|select
|t2.provinceid,
|bp.name as provincename,
|br.id as regionid,
|br.region_name as regionname,
|totalconsumption,
|totalorder,
|t2.year,
|t2.month
|from t2
|left join dwd.dim_province bp on t2.provinceid = bp.id
|left join dwd.dim_region br on br.id = bp.region_id
|""".stripMargin).write.mode("overwrite")
.saveAsTable("dws.provinceeverymonth")
//TODO T2
sql(
"""
|select
|province_id as provinceid,
|dp.name as provincename,
|avg(final_total_amount) as provinceavgconsumption
|from dwd.fact_order_info foi
|left join dwd.dim_province dp on dp.id = foi.province_id
|group by province_id,dp.name
|""".stripMargin).createOrReplaceTempView("everyProvince")
val d: java.math.BigDecimal = sql(
"""
|select avg(provinceavgconsumption)
|from
|everyProvince
|""".stripMargin).first().getDecimal(0)
sql(
s"""
|with t1 as (
|select
|provinceid,
|provincename,
|provinceavgconsumption,
|$d as allprovinceavgconsumption
|from everyProvince)
|select t1.*,
|(case
| when t1.provinceavgconsumption > t1.allprovinceavgconsumption then '高'
| when t1.provinceavgconsumption = t1.allprovinceavgconsumption then '相同'
| when t1.provinceavgconsumption < t1.allprovinceavgconsumption then '低'
| end) as comparison
|from t1
|""".stripMargin).write.mode("overwrite").saveAsTable("dws.provinceavgcmp")


//TODO T3
sql(
s"""
|with t1 as (
|select
|user_id as userid,
|cast(date_format(operate_time,'yyyyMMdd') as int) as day,
|final_total_amount as consumption
|from
|dwd.fact_order_info), t2 as (
|select userid,day,sum(consumption) as totalconsumption,count(1) as totalorder
|from t1
|group by userid,day
|)
|select
|a.userid,
|dui.name,
|concat(a.day,'_',b.day) as day,
|a.totalconsumption+b.totalconsumption as totalconsumption,
|a.totalorder+b.totalorder as totalorder
|from
|t2 a
|left join dwd.dim_user_info dui on dui.id = a.userid
|left join t2 b on a.userid = b.userid
|where
|a.day + 1=b.day
|and
|a.totalconsumption<b.totalconsumption
|""".stripMargin).write.mode("overwrite")
.saveAsTable("dws.usercontinueorder")

sql(
"""
|with t1 as (
|select
|user_id as userid,
|cast(date_format(operate_time,'yyyyMMdd') as int) as day,
|final_total_amount as consumption
|from
|dwd.fact_order_info)
|select userid,day,sum(consumption) as totalconsumption,count(1) as totalorder
|from t1
|group by userid,day
|""".stripMargin)
spark.stop()
}
}

BUG点

BUG没有截图,就是在用spark提交jar包的时候,跑起来,没有报错,但是会莫名其妙的被杀死。也没找到原因,整了一上午有时候好,有时候就不行。试着重启了hadoop和hive的元数据,就很长一段时间没有再报错了,估计是spark中我存了临时表,spark没杀死,导致缓存多了,超出内存,就自动杀死了。

日总结

今天的任务其实主要是为了熟悉7月份的表结构,每个字段代表什么,以及多表连接的公共字段,选择去做了指标计算,认为难度一般,7月份的表其实是比4月好一点的,订单表里面就有省份id,在求省份或者地区的订单金额的时候会方便很多,就不需要像4月份一样,订单表连接user表再通过user找province和region。总得来说理清楚表结构,指标计算部分还是很简单的。

7月任务3

学习日期: 3.7

所学内容概述

完整版七月任务书 2小时40分钟完成

抽取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package gz07.real.subject3

import org.apache.spark.sql.SparkSession

import java.sql.Timestamp

object dataExtract {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().master("local[*]").appName("t1")
.enableHiveSupport()
.config("hive.exec.dynamic.partition", "true")
.config("hive.exec.dynamic.partition.mode", "nonstrict")
.config("spark.sql.parquet.writeLegacyFormat", "true")
.getOrCreate()
import spark.sql
val date = "20230306"
val maxUserTime: Timestamp = sql("select greatest(max(create_time),max(operate_time)) from ods.user_info").first().getTimestamp(0)
val maxOrderTime: Timestamp = sql("select greatest(max(create_time),max(operate_time)) from ods.order_info").first().getTimestamp(0)
mysqlTempTable(spark,"user_info")
mysqlTempTable(spark,"sku_info")
mysqlTempTable(spark,"base_province")
mysqlTempTable(spark,"base_region")
mysqlTempTable(spark,"order_info")
mysqlTempTable(spark,"order_detail")
sql(
s"""
|insert into table ods.user_info partition (etl_date=$date)
|select * from mysql_user_info
|where
|create_time > '$maxUserTime' or operate_time > '$maxUserTime'
|""".stripMargin)
sql(
s"""
|insert into table ods.sku_info partition (etl_date=$date)
|select * from mysql_sku_info
|where
|create_time >
|(select max(create_time) from ods.sku_info)
|""".stripMargin)
sql(
s"""
|insert into table ods.base_province partition (etl_date=$date)
|select *,
|current_timestamp()
|from mysql_base_province
|where
|id >
|(select max(id) from ods.base_province)
|""".stripMargin)

sql(
s"""
|insert into table ods.base_region partition (etl_date=$date)
|select *,
|current_timestamp()
|from mysql_base_region
|where
|id >
|(select max(id) from ods.base_region)
|""".stripMargin)
sql(
s"""
|insert into table ods.order_info partition (etl_date=$date)
|select * from mysql_order_info
|where
|create_time > '$maxOrderTime' or operate_time > '$maxOrderTime'
|""".stripMargin)
sql(
s"""
|insert into table ods.order_detail partition (etl_date=$date)
|select * from mysql_order_detail
|where
|create_time >
|(select max(create_time) from ods.order_detail)
|""".stripMargin)
def mysqlTempTable(sparkSession: SparkSession,mysqlName:String): Unit = {
val url = "jdbc:mysql://172.16.42.128/dspub?useSSL = false"
val driver = "org.mariadb.jdbc.Driver"
val user = "root"
val password = "admin"
sparkSession.read.format("jdbc")
.option("url", url)
.option("driver", driver)
.option("user", user)
.option("password", password)
.option("dbtable", mysqlName).load().createTempView(s"mysql_$mysqlName")
}
}
}

清洗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package gz07.real.subject3

import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.functions._

object dataClear {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().master("local[*]").appName("t2")
.enableHiveSupport()
.config("hive.exec.dynamic.partition", "true")
.config("hive.exec.dynamic.partition.mode", "nonstrict")
.config("spark.sql.parquet.writeLegacyFormat", "true")
.getOrCreate()
import spark.sql
val date = "20230306"
odsUserClear(spark,"ods.user_info","dwd.dim_user_info")
odsSkuClear(spark,"ods.sku_info","dwd.dim_sku_info")
odsSkuClear(spark,"ods.base_province","dwd.dim_province")
odsSkuClear(spark,"ods.base_region","dwd.dim_region")
sql(
s"""
|select *,
|'user1' as dwd_insert_user,date_format(current_timestamp(),'yyyy-MM-dd HH:mm:ss') as dwd_insert_time,
|'user1' as dwd_modify_user,date_format(current_timestamp(),'yyyy-MM-dd HH:mm:ss') as dwd_modify_time
|from ods.order_info
|where etl_date=$date
|""".stripMargin)
.withColumn("operate_time",coalesce(col("operate_time"),col("create_time")))
.withColumn("etl_date",date_format(col("create_time"),"yyyyMMdd"))
.write.mode("overwrite").partitionBy("etl_date").saveAsTable("dwd.fact_order_info")

sql(
s"""
|select *,
|'user1' as dwd_insert_user,date_format(current_timestamp(),'yyyy-MM-dd HH:mm:ss') as dwd_insert_time,
|'user1' as dwd_modify_user,date_format(current_timestamp(),'yyyy-MM-dd HH:mm:ss') as dwd_modify_time
|from ods.order_detail
|where etl_date=$date
|""".stripMargin)
.withColumn("etl_date", date_format(col("create_time"), "yyyyMMdd"))
.write.mode("overwrite").partitionBy("etl_date").saveAsTable("dwd.fact_order_detail")


def odsUserClear(sparkSession: SparkSession,odsName:String,dwdName:String): Unit = {
sparkSession.sql(
s"""
|select *,
|'user1' as dwd_insert_user,date_format(current_timestamp(),'yyyy-MM-dd HH:mm:ss') as dwd_insert_time,
|'user1' as dwd_modify_user,date_format(current_timestamp(),'yyyy-MM-dd HH:mm:ss') as dwd_modify_time
|from $odsName
|where etl_date = $date
|""".stripMargin)
.drop("etl_date")
.withColumn("etl_date",lit(date))
.createOrReplaceTempView("ods_data")
sparkSession.sql(
s"""
|select * from $dwdName
|where etl_date = $date
|""".stripMargin)
.createOrReplaceTempView("dwd_data")
sparkSession.sql(
"""
|select * from ods_data
|union
|select * from dwd_data
|""".stripMargin)
.withColumn("operate_time",coalesce(col("operate_time"),col("create_time")))
.createOrReplaceTempView("union_data")
sparkSession.sql(
"""
|select *,row_number() over(partition by id order by operate_time desc) as time_rank
|from union_data
|""".stripMargin)
.where("time_rank=1")
.drop("time_rank")
.withColumn("dwd_modify_time",date_format(current_timestamp(),"yyyy-MM-dd HH:mm:ss"))
.write.mode("overwrite").partitionBy("etl_date").saveAsTable("temptable")
sparkSession.table("temptable")
.write.mode("overwrite").partitionBy("etl_date")
.saveAsTable(dwdName)
sparkSession.sql("drop table temptable")
}

def odsSkuClear(sparkSession: SparkSession, odsName: String, dwdName: String): Unit = {
sparkSession.sql(
s"""
|select *,
|'user1' as dwd_insert_user,date_format(current_timestamp(),'yyyy-MM-dd HH:mm:ss') as dwd_insert_time,
|'user1' as dwd_modify_user,date_format(current_timestamp(),'yyyy-MM-dd HH:mm:ss') as dwd_modify_time
|from $odsName
|where etl_date = $date
|""".stripMargin)
.drop("etl_date")
.withColumn("etl_date", lit(date))
.createOrReplaceTempView("ods_data")
sparkSession.sql(
s"""
|select * from $dwdName
|where etl_date = $date
|""".stripMargin)
.createOrReplaceTempView("dwd_data")
sparkSession.sql(
"""
|select * from ods_data
|union
|select * from dwd_data
|""".stripMargin)
.createOrReplaceTempView("union_data")
sparkSession.sql(
"""
|select *,row_number() over(partition by id order by create_time desc) as time_rank
|from union_data
|""".stripMargin)
.where("time_rank=1")
.drop("time_rank")
.withColumn("dwd_modify_time", date_format(current_timestamp(), "yyyy-MM-dd HH:mm:ss"))
.write.mode("overwrite").partitionBy("etl_date").saveAsTable("temptable")
sparkSession.table("temptable")
.write.mode("overwrite").partitionBy("etl_date")
.saveAsTable(dwdName)
sparkSession.sql("drop table temptable")
}
}
}

指标计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package gz07.real.subject3

import org.apache.spark.sql.{DataFrame, SparkSession}

object dataMath {
def main(args: Array[String]): Unit = {
val spark: SparkSession = SparkSession.builder().master("local[*]").appName("t3")
.enableHiveSupport()
.config("hive.exec.dynamic.partition", "true")
.config("hive.exec.dynamic.partition.mode", "nonstrict")
.config("spark.sql.parquet.writeLegacyFormat", "true")
.getOrCreate()
import spark.sql
val t1: DataFrame = sql(
"""
|with t1 as (
|select
|province_id,
|final_total_amount,
|substring(etl_date,1,4) as year,
|substring(etl_date,5,2) as month
|from dwd.fact_order_info foi), t2 as (
|select
|province_id,
|sum(final_total_amount) as total_amount,
|count(1) as total_count,
|year,
|month
|from t1
|group by province_id,year,month)
|select
|province_id,
|dp.name as province_name,
|dr.id as region_id,
|dr.region_name as region_name,
|total_amount,
|total_count,
|row_number() over(partition by year,month,dr.id order by total_amount desc) as sequence,
|year,month
|from t2
|left join dwd.dim_province dp on dp.id = t2.province_id
|left join dwd.dim_region dr on dr.id = dp.region_id
|""".stripMargin)
t1.show()
t1.write.mode("overwrite")
.saveAsTable("dws.province_consumption_day_aggr")
sql(
"""
|with t1 as (
|select
|region_id,
|sum(total_amount)/sum(total_count) as regionavgconsumption
|from dws.province_consumption_day_aggr
|group by region_id), t2 as (
|select
|province_id as provinceid,
|province_name as provincename,
|total_amount/total_count as provinceavgconsumption,
|t1.region_id as regionid,
|region_name as regionname,
|t1.regionavgconsumption as regionavgconsumption
|from dws.province_consumption_day_aggr pcda
|left join t1 on pcda.region_id = t1.region_id)
|select t2.*,
|case
| when provinceavgconsumption>regionavgconsumption then '高'
| when provinceavgconsumption=regionavgconsumption then '相同'
| when provinceavgconsumption<regionavgconsumption then '低' end
|from t2
|""".stripMargin).createOrReplaceTempView("t2TableTemp")
mysqlTempTable(spark,"provinceavgcmpregion")
sql(
"""
|insert overwrite table mysql_provinceavgcmpregion
|select * from t2TableTemp
|""".stripMargin)

sql(
"""
|select
|region_id,
|region_name,
|province_id,
|province_name,
|total_amount,
|row_number() over(partition by region_id order by total_amount desc) as threeprovince
|from dws.province_consumption_day_aggr
|""".stripMargin).where("threeprovince<=3").drop("threeprovince").createOrReplaceTempView("t3Temp")
sql(
"""
|select
|region_id,
|region_name,
|concat_ws(',',collect_set(cast(province_id as string))) as provinceids,
|concat_ws(',',collect_set(province_name)) as provincenames,
|concat_ws(',',collect_set(cast(total_amount as string))) as provinceamount
|from
|t3Temp
|group by region_id,region_name
|""".stripMargin).createOrReplaceTempView("t333")
mysqlTempTable(spark,"regiontopthree")
sql(
"""
|insert overwrite table mysql_regiontopthree
|select * from t333
|""".stripMargin)

def mysqlTempTable(sparkSession: SparkSession, mysqlName: String): Unit = {
val url = "jdbc:mysql://172.16.42.128/shtd_result?useSSL = false"
val driver = "org.mariadb.jdbc.Driver"
val user = "root"
val password = "admin"
sparkSession.read.format("jdbc")
.option("url", url)
.option("driver", driver)
.option("user", user)
.option("password", password)
.option("dbtable", mysqlName).load().createTempView(s"mysql_$mysqlName")
}
}
}

BUG点

指标计算最后一题的时候,是之前没怎么做过的类型,是列转行,想起来了collect_set和concat_ws两个方法能实现任务目标,但是报错了,看报错信息,明白了似乎是province_id的类型不对需要是array 或者是string,就去用cast (xxx as string)转换成string再放进去,问题解决。

image-20230307184042519

扩展学习

想得到这样的表

image-20230307191546184

多行变一行 有公共列

在这里插入图片描述

1).先使用collect_set函数使多行成为一行数组

1
2
3
hive> select name,collect_set(subject)  res1 ,
> collect_set(cast(score as string)) res2
> from stu group by name;

在这里插入图片描述

2). 加上concat_ws函数可以取出数组中的每一个元素的值在用分隔符连接

1
2
3
hive> select name,concat_ws('@',collect_set(subject))  res1 ,
> concat_ws('-',collect_set(cast(score as string))) res2
> from stu group by name;

在这里插入图片描述

1
2
3
4
5
hive>select substr('qwertyuio',0,4)
--qwer、
对字符串的
第一个参数是从哪个位置开始
第二个参数是取几位数

日总结

今天做题的状态极佳,应该是前几天都在研究七月任务7,因此前面的抽取和清洗只花了大概1小时20分钟,剩下时间在做指标计算,指标计算已经熟悉表结构了而且连接的表不是很多就一两个,所以我认为难度一般,最后一题只是刚开始被吓到了,其实只要使用两个函数再结合分组就能很顺利的解决,整体做下来,将近快3个小时了,还是比四月份难很多的。过程中也是出现了几次小错误,也没浪费多少时间在修改上。

7月任务4 指标计算

学习日期: 3.9

所学内容概述

其他几题没什么问题,在第二题的时候犯了难。

t1

简单的表连接和分组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
//TODO t1
sql(
"""
|with t1 as (
|select
|final_total_amount,
|province_id,
|substring(etl_date,4,0) as year,
|substring(etl_date,5,2) as month
|from
|dwd.fact_order_info),
|t2 as (
|select
|province_id,
|sum(t1.final_total_amount) as total_amount,
|count(1) as total_count,
|year,month
|from
|t1
|group by province_id,year,month)
|select
|t2.province_id as province_id,
|dp.name as province_name,
|dr.id as region_id,
|dr.region_name as region_name,
|t2.total_amount,
|t2.total_count,
|row_number() over(partition by year,month,region_id order by total_amount desc) as sequence,
|year,month
|from
|t2
|left join dwd.dim_province dp on dp.id = t2.province_id
|left join dwd.dim_region dr on dr.id = dp.region_id
|""".stripMargin).write.mode("overwrite")
.saveAsTable("dws.province_consumption_day_aggr")

t2

任务如下

image-20230313082722921

最开始的想法是将销售量和销售额前十的表都先查找出来,然后结合,结合以后排序即可。

  • 结果如下,没有达到自己的要求,不是直接合并,而是出现了笛卡尔积,销售量表和销售额表的数据是直接相乘了。

image-20230313082922761

  • 又想到一种方法,将两个表都多出一行根据 各自需要降序排序的数据,列出排名,根据排名进行join,这样就能达到两个表的连接,然后使用drop删除左边的排序信息。实现代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//TODO t2
sql(
"""
|with t1 as (
|select
|sku_id,sku_name,order_price,cast(sum(sku_num) as int) as skuSum,
|cast(sum(sku_num)*order_price as int) as skuOrderSum
|from
|dwd.fact_order_detail
|group by sku_id,sku_name,order_price),
|t2 as (
|select
|t1.sku_id as topquantityid,
|t1.sku_name as topquantityname,
|t1.skuSum as topquantity,
|row_number() over(order by t1.skuSum desc,t1.sku_id) as topTenQuantity
|from
|t1
|limit 10),
|t3 as (
|select
|t1.sku_id as toppriceid,
|t1.sku_name as toppricename,
|t1.skuOrderSum as topprice,
|row_number() over(order by t1.skuOrderSum desc) as sequence
|from
|t1
|limit 10)
|select topquantityid,
|topquantityname,
|topquantity,
|t3.*
|from
|t2
|left join t3 on t2.topTenQuantity=t3.sequence
|order by t3.sequence
|""".stripMargin).createOrReplaceTempView("t2")
mysqlTempTable(spark,"topten")
sql(
"""
|insert overwrite table mysql_topten
|select * from t2
|""".stripMargin)

加入MySQL表 以及完成任务

image-20230313091804187

###t3

这个是要求求中位数的之前没有使用过,但是题目提供了函数方法。

percentile(col,doule) 该方法第一个参数是列,列的要求是整数bigint或者array(doule),因为是中位数,第二个参数就是0.5,如果要3/4的值的话就是0.75,以此类推

问题就不会很难了,求两个表一个省份中位数,一个地区中位数,join即可

  • 代码实现如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//TODO t3
sql(
"""
|with t1 as (
|select
|foi.province_id as provinceid,
|dp.name as provincename,
|dr.id as regionid,
|percentile(cast(final_total_amount as bigint),0.5) as provincemedian
|from
|dwd.fact_order_info foi
|left join dwd.dim_province dp on dp.id = foi.province_id
|left join dwd.dim_region dr on dr.id = dp.region_id
|group by foi.province_id,dp.name,dr.id),
|t2 as (
|select
|dr.id as regionid,
|dr.region_name as regionname,
|percentile(cast(final_total_amount as bigint),0.5) as regionmedian
|from
|dwd.fact_order_info foi
|left join dwd.dim_province dp on dp.id = foi.province_id
|left join dwd.dim_region dr on dr.id = dp.region_id
|group by dr.id,region_name
|)
|select t1.provinceid,t1.provincename,t2.regionid,t2.regionname,
|t1.provincemedian,t2.regionmedian
|from
|t1
|left join t2 on t1.regionid = t2.regionid
|""".stripMargin).createOrReplaceTempView("t3")
mysqlTempTable(spark, "nationmedian")
sql(
"""
|insert overwrite table mysql_nationmedian
|select * from t3
|""".stripMargin)

mysql执行命令以及结果

image-20230313100851640

BUG点

想将两个表给合并起来,就想到了之前使用过的select t1.x,t2.x from t2,t3报了错误,看报错信息是hive不支持笛卡尔积的使用,在hive中开启笛卡尔积就能解决报错。开启方法如下在截图最后两行。如果放在spark中提交的话,还是会报错。报错信息是crossJoin也是笛卡尔,需要用如下的代码解决

1
spark.conf.set("spark.sql.crossJoin.enabled","true")

image-20230313081819796

日总结

今天完成了任务4的指标计算部分,上午花时间和小白老师以及江苏省第一的朋友探讨了一下,对题目模糊不清的要求的看法,清洗部分对dwd表是overwrite还是append等。改天会具体列出来,还有最后指标计算对order表要不要取最新分区的看法还没讨论出结果。然后就是任务四,一路上除了第二题出了点问题,还是顺利的,而且第二题也顺利解决了问题,今天学习状态也不错,做题顺利。

7月任务书讨论

学习日期: 3.10

所学内容概述

问题疑惑点如下:

### 问题一

image-20230313102707184

因为我们是不知道dwd表中的数据以及分区是什么样子的,只能推测。根据业务理解,可以认为ods的数据是比赛日期的昨天的,而dwd的数据是昨天之前的,也就是前天或者更早的,题目要求ods取昨天的分区,dwd取最新的分区,假设这个最新的分区就是前天的,那和ods通过id去重以后的数据再放入dwd的时候,是append还是overwrite,还是说append然后将重复的id删除了。

经过讨论认为直接append的就好了,虽然会有重复的id但是分区是不一样的。如果是真实的业务上的话,其实是应该对表的id去重的,因为userid是主键是唯一值。

问题二(未解决)

在指标计算中都会有这么一句话,以前没有注意,看到以后发现是要取分区的,但是我又去看了小白的代码,发现他并没有对order表取最新分区。

image-20230313104805961

小白给我的解释是order表是事实表不是维表所以不取分区,问了福建比赛的朋友,他也说不用取,但是江苏第一说他比赛的时候order取了最新的分区,一时间不知道听哪个的,我自己认为表的确是有维表和事实表的区别,所以题目要求维表,order的确是不用取最新分区的。但是江苏第一说他比赛的时候取的,结果是全对的。…等后面问四合的老师吧。

扩展学习

将7月任务书综合分析部分都使用chatgpt,将问题回答完整并做了总结。总结完以后发现也就6个问题,每天背一下记一下,比赛前应该都能记住了。

日总结

对任务书疑惑点进行讨论,结合大家看法,找到最合理的解决办法,以及题目理解。比较突出的其实就我列出来的两个问题,其他的大家的理解都基本一致。总结差不多都写上面了。然后就是7月任务书综合分析部分都使用chatgpt,将问题回答完整并做了总结。总结完以后发现也就6个问题,每天背一下记一下,比赛前应该都能记住了。

7月任务书5 指标计算

学习日期: 3.13

所学内容概述

第一题

其他很多任务书都有这题,就不细说了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
sql(
"""
|with t1 as (
|select
|final_total_amount,
|province_id,
|substring(etl_date,4,0) as year,
|substring(etl_date,5,2) as month
|from
|dwd.fact_order_info),
|t2 as (
|select
|province_id,
|sum(t1.final_total_amount) as total_amount,
|count(1) as total_count,
|year,month
|from
|t1
|group by province_id,year,month)
|select
|t2.province_id as province_id,
|dp.name as province_name,
|dr.id as region_id,
|dr.region_name as region_name,
|t2.total_amount,
|t2.total_count,
|row_number() over(partition by year,month,region_id order by total_amount desc) as sequence,
|year,month
|from
|t2
|left join dwd.dim_province dp on dp.id = t2.province_id
|left join dwd.dim_region dr on dr.id = dp.region_id
|""".stripMargin).write.mode("overwrite")
.saveAsTable("dws.province_consumption_day_aggr")

第二题

计算连续两天下单的用户与已下单用户的占比,将结果存入MySQL数据库shtd_result的userrepurchasedrate表中

分析题目要求,求出下单用户,和连续两天下单的用户,然后占比,也就是一行即可,那就可以使用dsl的getLong获取数量,再最后导入表的时候使用常量值就行了。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
val l1: Long = sql(
"""
|select count(*) from dwd.fact_order_info
|""".stripMargin).first().getLong(0) //已下单人数
println(l1+"*******************************************************************")
val l2: Long = sql(
"""
|with t1 as(
|select
|user_id as userid,
|cast(date_format(create_time,'yyyyMMdd') as int) as day,
|final_total_amount
|from
|dwd.fact_order_info a),
|t2 as (
|select
|userid,day,sum(final_total_amount) as sumorder
|from t1
|group by userid,day
|),t3 as (
|select
|a.userid,
|concat(a.day,'_',b.day),
|a.sumorder
|from
|t2 a
|left join t2 b on a.userid=b.userid
|where
|a.day+1=b.day
|and
|a.sumorder<b.sumorder
|)
|select count(*)
|from
|t3
|""".stripMargin).first().getLong(0) //连续两天的人数
print(l2+"*******************************************************************")
sql(
s"""
|select $l1 as purchaseduser,
|$l2 as repurchaseduser,
|concat(round($l2/$l1,3)*100,'%') as repurchaserate
|""".stripMargin).show() //直接查 保留三位小数然后乘100加%就是百分比

第三题

第三题比较难,行转列,需要用scala中柯里化函数,因为scala学的不是很深入,所以这题如果自己做的话,应该是用union或者join来实现了。问了朋友明白代码用法,实现了功能,foldLeft其实还不是很理解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
val data = sql(
"""
|select
|b.name,count(*) as con
|from
|dwd.fact_order_info a
|join dwd.dim_province b on a.province_id=b.id
|group by b.name
|order by con desc
|""".stripMargin)
.rdd.map(x => (x.getString(0),x.getLong(1)))
.collect().toList
print(data)
import spark.implicits._
val dum: DataFrame = Seq("1").toDF("dum")
data.foldLeft(dum)((dum,d) => dum.withColumn(d._1,lit(d._2)))
.drop("dum")
.show(false)

BUG点

修了个hexo博客的bug,问题点在我文章h1标签,点击工作台会报错,而且无法跳转。查看博客源代码发现在main.js和util.js中有使用,一直深拔源码,发现是hexo-renderer-markdown-it这个插件的问题,因为seo标题就是h1标签,所以源码中跳转特意从h2开始的,源码改不了就只能改配置覆盖,在_config.yml中加入,问题顺利解决。

1
2
3
markdown:
anchors:
level: 1

image-20230314143507008

日总结

今天的任务是任务书5的指标计算,任务5的指标计算和之前的很不同,之前的都是分组完了然后调用函数,任务5后面的两题就是比较新颖的,一题是单行的,另外一题列转行就比较难了,需要用scala函数柯里化。解决了之前博客点击跳转的BUG。新版博客链接https://u7u7.top

任务4修改

学习日期: 3.14

所学内容概述

刚开始的题目理解都是使用detail商品表,但是忘记了订单额,应该是算最终订单,也就是info表中的final_total_amount,所以之前的代码是有疑问的,而且因为两个表order_id是不相同的,讨论过后选择使用join,用两个表都有的id,修改以后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//TODO t2
sql(
"""
|with t1 as (
|select
|sku_id,sku_name,
|cast(sum(sku_num) as int) as skuSum,
|sum(final_total_amount) as topprice
|from
|dwd.fact_order_detail fod
|join dwd.fact_order_info foi on foi.id=fod.order_id
|group by sku_id,sku_name),
|t2 as (
|select
|t1.sku_id as topquantityid,
|t1.sku_name as topquantityname,
|t1.skuSum as topquantity,
|row_number() over(order by t1.skuSum desc,t1.sku_id) as topTenQuantity
|from
|t1
|limit 10),
|t3 as (
|select
|t1.sku_id as toppriceid,
|t1.sku_name as toppricename,
|t1.topprice as topprice,
|row_number() over(order by t1.skuOrderSum desc) as sequence
|from
|t1
|limit 10)
|select topquantityid,
|topquantityname,
|topquantity,
|t3.*
|from
|t2
|left join t3 on t2.topTenQuantity=t3.sequence
|order by t3.sequence
|""".stripMargin).createOrReplaceTempView("t2")
mysqlTempTable(spark,"topten")
sql(
"""
|insert overwrite table mysql_topten
|select * from t2
|""".stripMargin)

日总结

明天开始每天都是要去做几道算法题了,蓝桥杯算法比赛快开始了,最近一些算法使用已经有点生疏了,今天讨论的时候又解决了一个任务不清晰的要求,今天主要是复习了一下java算法和解决大数据的问题以及背了一下综合分析。

任务5以及算法

学习日期: 3.15

所学内容概述

编号2120

题目不难,找到规律就行了。

image-20230315093825402

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//在此输入您的代码...
String Ax = scan.nextLine();
int num = Integer.parseInt(Ax.substring(1));
int[] a = new int[6]; //A9超出 所以设置6个
int[] b = new int[5];
int aNumOne = 1189; //定义初始值
int bNumOne = 841;
for(int i = 0;i<6;i++){
a[i] = aNumOne;
aNumOne = aNumOne / 2; //取整添加
}
for(int j = 0;j<5;j++){
b[j] = bNumOne;
bNumOne = bNumOne / 2;
}
if(num%2==0){
System.out.println(a[num/2]);//偶数按索引取数组
System.out.println(b[num/2]);
}else{
System.out.println(b[(num-1)/2]);//奇数数前后取头
System.out.println(a[(num+1)/2]);
}
scan.close();
}

BUG点

任务书5第三题,是使用rdd算子实现的,在idea中能打印,能show,都是蛮正常的,但是放到服务器spark提交,就报很多错误,而且找不到关键信息,回到IDEA的时候,发现最上面是有报错信息的,代码却能正常执行。搞了一下午没解决,推测是因为环境使用的是mariadb的问题,因为IDEA中出现很多这个报错,Spark无法显示的原因,应该是mariadb的报错没有过滤,导致一报错就自动杀死自己的进程。

image-20230315183251364

扩展学习

  • 研究了一下自己博客插件的源代码,并安装了看板娘在博客中,自己自定义了位置以及大小,写yml配置中
  • 其他的live2d有些比较吃性能,自己的没有买云服务器所以只能找一个资源占用不大的模型:yml代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
live2d:
enable: true #开关插件版看板娘
scriptFrom: local # 默认
pluginRootPath: live2dw/ # 插件在站点上的根目录(相对路径)
pluginJsPath: lib/ # 脚本文件相对与插件根目录路径
pluginModelPath: assets/ # 模型文件相对与插件根目录路径
model:
use: live2d-widget-model-koharu # npm-module package name
display:
position: left #控制看板娘位置
width: 150 #控制看板娘大小
height: 300 #控制看板娘大小
mobile:
show: true # 手机中是否展示

效果如下:

image-20230315185643870

日总结

今天下午是大数据学习,可惜一直在解决BUG,而且没解决完。等后面服务器来了再测试一下吧,我怀疑是小白服务器的问题,真要改起来肯定会花费大量的时间和精力,所以打算先放一放。上午改了博客学习了一下Vue,vue没看几集时间不是很充裕,就没有记笔记。做了一道简单的算法题,做第二题的时候,难度上去了,一中午没做完,下午要大数据了,就先放明天弄了。今天学习状态一般。

Vue TodoList案例完善

学习日期: 3.17

所学内容概述

全局事件总线(GlobalEventBus)

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 安装全局事件总线:

    1
    2
    3
    4
    5
    6
    7
    new Vue({
    ......
    beforeCreate() {
    Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm
    },
    ......
    })
  3. 使用事件总线:

    1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身。

      1
      2
      3
      4
      5
      6
      7
      methods(){
      demo(data){......}
      }
      ......
      mounted() {
      this.$bus.$on('xxxx',this.demo)
      }
    2. 提供数据:this.$bus.$emit('xxxx',数据)

  4. 最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。

消息订阅与发布(pubsub)

  1. 一种组件间通信的方式,适用于任意组件间通信

  2. 使用步骤:

    1. 安装pubsub:npm i pubsub-js

    2. 引入: import pubsub from 'pubsub-js'

    3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。

      1
      2
      3
      4
      5
      6
      7
      methods(){
      demo(data){......}
      }
      ......
      mounted() {
      this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
      }
    4. 提供数据:pubsub.publish('xxx',数据)

    5. 最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)取消订阅。

nextTick

  1. 语法:this.$nextTick(回调函数)
  2. 作用:在下一次 DOM 更新结束后执行其指定的回调。
  3. 什么时候用:当改变数据后,要基于更新后的新DOM进行某些操作时,要在nextTick所指定的回调函数中执行。

扩展学习

力扣算法练习 两个一般难度 暴力做的出来 算法优解理解了很久 两题做了一下午

日总结

今天的学习内容不是很难,还是对案例的优化。重点在全局事件总线,它和消息订阅所实现的功能差不多的,但是还是全局事件比较方便而且不依靠第三方插件。然后是对文本框点击编辑按钮后,对文本框的聚焦,会发现是实现功能上先后逻辑顺序的问题导致无法实现的,我想到的是用一个setTimeout函数延时执行,也能实现,但是功能实用性不强就是了,nextTick就能很好的解决这个问题,在下一次DOM更新结束后执行,学习状态还不错。

Vuex

学习日期: 3.20

所学内容概述

1.概念

​ 在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。

2.使用情况

​ 多个组件需要共享数据时

3.搭建vuex环境

  1. 创建文件:src/store/index.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //引入Vue核心库
    import Vue from 'vue'
    //引入Vuex
    import Vuex from 'vuex'
    //应用Vuex插件
    Vue.use(Vuex)

    //准备actions对象——响应组件中用户的动作
    const actions = {}
    //准备mutations对象——修改state中的数据
    const mutations = {}
    //准备state对象——保存具体的数据
    const state = {}

    //创建并暴露store
    export default new Vuex.Store({
    actions,
    mutations,
    state
    })
  2. main.js中创建vm时传入store配置项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    ......
    //引入store
    import store from './store'
    ......

    //创建vm
    new Vue({
    el:'#app',
    render: h => h(App),
    store
    })

4.基本使用

  1. 初始化数据、配置actions、配置mutations,操作文件store.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    //引入Vue核心库
    import Vue from 'vue'
    //引入Vuex
    import Vuex from 'vuex'
    //引用Vuex
    Vue.use(Vuex)

    const actions = {
    //响应组件中加的动作
    jia(context,value){
    // console.log('actions中的jia被调用了',miniStore,value)
    context.commit('JIA',value)
    },
    }

    const mutations = {
    //执行加
    JIA(state,value){
    // console.log('mutations中的JIA被调用了',state,value)
    state.sum += value
    }
    }

    //初始化数据
    const state = {
    sum:0
    }

    //创建并暴露store
    export default new Vuex.Store({
    actions,
    mutations,
    state,
    })
  2. 组件中读取vuex中的数据:$store.state.sum

  3. 组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)

    备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

5.getters的使用

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。

  2. store.js中追加getters配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ......

    const getters = {
    bigSum(state){
    return state.sum * 10
    }
    }

    //创建并暴露store
    export default new Vuex.Store({
    ......
    getters
    })
  3. 组件中读取数据:$store.getters.bigSum

6.四个map方法的使用

  1. mapState方法:用于帮助我们映射state中的数据为计算属性

    1
    2
    3
    4
    5
    6
    7
    computed: {
    //借助mapState生成计算属性:sum、school、subject(对象写法)
    ...mapState({sum:'sum',school:'school',subject:'subject'}),

    //借助mapState生成计算属性:sum、school、subject(数组写法)
    ...mapState(['sum','school','subject']),
    },
  2. mapGetters方法:用于帮助我们映射getters中的数据为计算属性

    1
    2
    3
    4
    5
    6
    7
    computed: {
    //借助mapGetters生成计算属性:bigSum(对象写法)
    ...mapGetters({bigSum:'bigSum'}),

    //借助mapGetters生成计算属性:bigSum(数组写法)
    ...mapGetters(['bigSum'])
    },
  3. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数

    1
    2
    3
    4
    5
    6
    7
    methods:{
    //靠mapActions生成:incrementOdd、incrementWait(对象形式)
    ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

    //靠mapActions生成:incrementOdd、incrementWait(数组形式)
    ...mapActions(['jiaOdd','jiaWait'])
    }
  4. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数

    1
    2
    3
    4
    5
    6
    7
    methods:{
    //靠mapActions生成:increment、decrement(对象形式)
    ...mapMutations({increment:'JIA',decrement:'JIAN'}),

    //靠mapMutations生成:JIA、JIAN(对象形式)
    ...mapMutations(['JIA','JIAN']),
    }

备注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

扩展学习

模块化+命名空间

  1. 目的:让代码更好维护,让多种数据分类更加明确。

  2. 修改store.js

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    const countAbout = {
    namespaced:true,//开启命名空间
    state:{x:1},
    mutations: { ... },
    actions: { ... },
    getters: {
    bigSum(state){
    return state.sum * 10
    }
    }
    }

    const personAbout = {
    namespaced:true,//开启命名空间
    state:{ ... },
    mutations: { ... },
    actions: { ... }
    }

    const store = new Vuex.Store({
    modules: {
    countAbout,
    personAbout
    }
    })
  3. 开启命名空间后,组件中读取state数据:

    1
    2
    3
    4
    //方式一:自己直接读取
    this.$store.state.personAbout.list
    //方式二:借助mapState读取:
    ...mapState('countAbout',['sum','school','subject']),
  4. 开启命名空间后,组件中读取getters数据:

    1
    2
    3
    4
    //方式一:自己直接读取
    this.$store.getters['personAbout/firstPersonName']
    //方式二:借助mapGetters读取:
    ...mapGetters('countAbout',['bigSum'])
  5. 开启命名空间后,组件中调用dispatch

    1
    2
    3
    4
    //方式一:自己直接dispatch
    this.$store.dispatch('personAbout/addPersonWang',person)
    //方式二:借助mapActions:
    ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
  6. 开启命名空间后,组件中调用commit

    1
    2
    3
    4
    //方式一:自己直接commit
    this.$store.commit('personAbout/ADD_PERSON',person)
    //方式二:借助mapMutations:
    ...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),

日总结

今天的学习内容是vuex,感觉它的功能就是像是一个Java中的static,设置全局变量或者全局方法,比之前的组件间通讯都好用,流程梳理明白掌握起来也不难,有复杂计算逻辑的需要放Mutations中,普通的就放actions中就行,所有可以使用计算属性的,都可以用mapstate,模块化是算扩展了,也不算很难,以后为了规范性以及代码可读性,估计还是使用vuex模块化的比较多。

mysql数据导入

学习日期: 3.22

所学内容概述

将小白样题数据导入

source /opt/ds_db01.sql,发现有三个表报错了,解决了挺久的bug,因为sql文件比较大,所以导入或者修改的时候会很卡,而且迟迟不能成功。打开以后,慢慢找创建表语句,再慢慢修改。

BUG点

导入数据的时候出现的bug,导入小白样题的时候,发现只有20表,缺失了三个表,打开sql的源文件,单独创表,发现出现了报错。搜索以后发现是timestamp类型的不能超过两个default,原因是当你给一个timestamp设置为on updatecurrent_timestamp的时候,其他的timestamp字段需要显式设定default值。如果你有两个timestamp字段,但是只把第一个设定为current_timestamp而第二个没有设定默认值,MySQL能成功建表,但是反过来就不行,需要把之前的改成datetime,后面null的都删除,BUG就解决了。

image-20230322175615853

日总结

今天上午先是做了几道蓝桥杯的题目,有道填空题,是直接拿草稿纸推算的。下午的时候看了一下小白的样题,发现之前的sql文件是有点小bug的,无法导入,有两个表是有bug的,一下午解决了bug,然后分析了一下任务书,晚上将平时训练的计划规划了一下。

测试新镜像连接

学习日期: 3.23

所学内容概述

下载镜像导入VM,启动hadoop集群以及hive元数据,一切顺利。

BUG点

今天使用IDEA测试连接的时候报错了,之前出现过这种报错也没找到原因,我认为就是mariadb的问题。重新放入配置文件,修改hosts中的ip映射都不行,就算了,有时候就是会突然坏一下,这几天都没关机什么都没干,直接在spark中跑也是一样的,而且比idea要快很多。

image-20230323095003966

扩展学习

编号468

回文日期

题目很清晰明了,但是需要讨论的特殊条件比较多。就是日期规范性,每个月每个天数不一样,还有日期开头不为0,学份12月以内,不可为0,等等。在我最初的版本都是有一条一条if规范出来的。后来优化算法的时候,发现有些测试点,蓝桥杯是没有设置一些特殊检查点的所以像30天和28 29这种特殊月。所以后面闰月和2月都没用添加判断语句,但是也顺利通过了,耗时有点久。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import java.util.*;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//在此输入您的代码...
int N = scan.nextInt();
int i = N+1;
int res = 99999999;
while(i<=99999999){
String num = i+"";
int yearage = i/1000000;
int fanzhuanyearage = yearage/10+yearage%10*10;//反转day
int yearlaster = i%1000000/10000;
int fanzhuanyearlaster = yearlaster/10+yearlaster%10*10; //反转month
int month = i%10000/100;
int day = i%100;
int lastday = i%10; //最后一天不能为0 因为反转以后首不为0
if(month <= 12 && day <= 31 && lastday != 0 && fanzhuanyearage<=31 && fanzhuanyearlaster<=12 && fanzhuanyearlaster != 0){
if(huiwen(num)){
//设置回文 为第一个
if(res > Integer.parseInt(num)){
res = Integer.parseInt(num);
System.out.println(num);
}
//如果为AB回文直接停止
if(ABhuiwen(num)){
System.out.println(num);
break;
}
}
}
i++;
}
scan.close();
}
//判断闰年
public static boolean ruiyear(String num){
int year = Integer.parseInt(num);
return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}
//判断回文数
public static boolean huiwen(String num){
char[] c = num.toCharArray();
return c[0] == c[7] && c[1] == c[6] && c[2] == c[5] && c[3] == c[4];

}
//判断AB回文数
public static boolean ABhuiwen(String num){
char[] c = num.toCharArray();
return c[0] == c[2] && c[2] == c[5] && c[5] == c[7] && c[1] == c[3] && c[3] == c[4] && c[4] == c[6];
}
}

日总结

今天的主要任务是设置大数据的学习计划,平时练习的计划,然后导入了数据库,然后导入了新的镜像,发现里面是自带数据的,值得注意的是,software的安装位置都改变了,在IDEA中需要修改配置文件。hive hdfs 和core,yarn是不需要的, 晚上做了算法题,做题还是很顺利的,但是有个任务点一直无法通过,优化了算法,节省了不必要的空间。

Spark架构以及集群搭建

学习日期: 3.24

所学内容概述

Spark理论

Spark资源调度策略,之前spark内存爆炸过。看内容是有调优策略的,主要原因是CPU资源占用满了,电脑的i5 3代太垃垮了,之前也使用过spark的调度程序,还是爆了,最后是通过重启hadoop服务成功的。

BUG点

报错信息为截图,但是记住了。java.sql.SQLException: null, message from server: “Host ‘XXX‘ is not allowed to connect异常。是mysql拒绝了我的该网段的链接,是因为mysql中root用户只允许本地连接和 回环地址,之前IDEA无法直接运行的BUG也算是解决了。

解决步骤:

use mysql;
select user,host from user;
update user set host = ‘%’ where user = ‘root’;
flush privileges; --刷新权限

image-20230324103006317

日总结

今天的任务是看小白的spark教程,基础部分,自己基本都学过,就当过了一遍,在ch02开发和部署的实战部分,我就直接把样题数据从mysql导入hive中去了,当实操,用savetable,可以直接自动创建表,往hive中加入了部分假数据,基础部分感觉只有资源调度那边是有点用的,其他都是发展史。

Spark核心编程RDD

学习日期: 3.25

所学内容概述

Spark集合方法

1
2
3
4
5
6
// 构造这两个RDD 充当假数据
val rdd1 = sc.parallelize(List(1,2,3,3))
rdd1.collect

val rdd2 = sc.parallelize(List(3,4,5))
rdd2.collect

union(otherDataset)

合并两个rdd,并集。

1
2
val rdd3 = rdd1.union(rdd2)
rdd3.collect

intersection(otherDataset)

交集。只保留在两个rdd中都有的元素。

1
2
val rdd4 = rdd1.intersection(rdd2)
rdd4.collect

subtract转换

差集。只保留在第一个rdd中有而在第二个rdd中没有的元素。

1
2
val rdd5 = rdd1.subtract(rdd2)
rdd5.collect

cartesian(otherDataset)

两个rdd的笛卡尔集。

当在类型为T和U的RDD上调用时,返回一个(T, U)对(所有元素对)的RDD。

1
2
val rdd6 = rdd1.cartesian(rdd2)
rdd6.collect

BUG点

学习顺利没BUG

扩展学习

学习了action部分对表转换成rdd以后的操作函数还是有很多值得学习

action操作函数

  • reduce(func)

    使用函数func(接受两个参数并返回一个参数)聚合数据集的元素。这个函数应该是交换律和结合律,这样才能并行地正确地计算它。

  • collect()

    将RDD操作的所有结果返回给驱动程序。这通常对产生足够小的数据集的操作很有用。

  • count()

    这会返回数据集中的元素数量或RDD操作的结果输出。

  • first()

    返回数据集的第一个元素或RDD操作产生的结果输出。它的工作原理类似于take(1)函数。

  • take(n)

    返回RDD的前n个元素。它首先扫描一个分区,然后使用该分区的结果来估计满足该限制所需的其他分区的数量。这个方法应该只在预期得到的数组很小的情况下使用,因为所有的数据都加载到驱动程序的内存中。

  • top(n)

    按照指定的隐式排序[T]从这个RDD中取出最大的k个元素,并维护排序。这与takeOrdered相反。这个方法应该只在预期得到的数组很小的情况下使用,因为所有的数据都加载到驱动程序的内存中。

  • takeOrdered(n)

    返回RDD的前n个(最小的)元素,并维护排序。这和top是相反的。这个方法应该只在预期得到的数组很小的情况下使用,因为所有的数据都加载到驱动程序的内存中。

  • saveAsTextFile(path)

    将数据集的元素作为文本文件(或文本文件集)写入本地文件系统、HDFS或任何其他hadoop支持的文件系统的给定目录中。Spark将对每个元素调用toString,将其转换为文件中的一行文本。

  • countByKey()

    仅在类型(K, V)的RDDs上可用。返回(K, Int)对的hashmap和每个键的计数。

  • foreach(func)

    在数据集的每个元素上运行函数func。

日总结

复习使用了spark中的RDD编程,集合部分感觉对大数据处理表数据的时候还是有帮助的,然后自己看了以前学期scala时候,中spark的RDD和scala中的方法,结合小白书苑还是有很多对于RDD方法的提升和理解的,使用sql编程,和RDD一起使用并独立完成了RDD的案例。

SparkSQL编程

学习日期: 3.27-3.28

所学内容概述

因为27号sql部分基础所学的内容比较简单没什么可以值得记得,28号学习的sql高级有些需要记笔记的部分,因此将两天的所学内容放到一起。

Spark SQL内置函数

SQL函数大致分为四类:

  • 标量函数:每一行返回单个的值。
  • 聚合函数:每一组行返回单个的值。
  • 窗口函数:每一组行返回多个值。
  • 用户自定义函数(UDF):包括自定义的标量函数或聚合函数。

标量和聚合函数在spark的内置方法包中,使用需要导入包

1
import org.apache.spark.sql.functions._ //(_通配,将functions所有方法都导入)

常用的内置函数自己基本都使用过,就先不一一列出来了,找个时间全部梳理一下。

Spark SQL聚合函数

这部分基本是和sql中的使用是一样的,参数也一样,就是分组聚合的函数,DSL风格通过groupBy(col)进行分组。

聚合函数 描述
count(col) 返回每组中成员数量
countDistinct(col) 返回每组中成员唯一数量
approx_count_distinct(col) 返回每组中成员唯一近似数量
min(col) 返回每组中给定列的最小值
max(col) 返回每组中给定列的最大值
sum(col) 返回每组中给定列的值的和
sumDistinct(col) 返回每组中给定列的唯一值的和
avg(col) 返回每组中给定列的值的平均
skewness(col) 返回每组中给定列的值的分布的偏斜度
kurtosis(col) 返回每组中给定列的值的分布的峰度
variance(col) 返回每组中给定列的值的无偏方差
stddev(col) 返回每组中给定列的值的标准差
collect_list(col) 返回每组中给定列的值的集合。返回的集合可能包含重复的值
collect_set(col) 返回每组中给定列的唯一值的集合

数据缺失值处理

比如df表如下:

image-20230328195221944

1
2
3
4
ds1.na.drop().show() //删除缺失值的行 有null的都删除
ds1.na.drop(minNonNulls=2).show() //删除缺失值有两个及以上的行
ds1.na.fill("不详").show() //将所有null值填充为不详
ds1.na.fill(Map("dynasty"->"--", "dob"->"不详")).show() //dynasty列填充为-- dob为不详

新数据如下,验证去重函数
img

1
2
3
ds1.dropDuplicates().show() //完全重复的行删除 注意第2和4不一样,一个清朝一个清代
ds1.dropDuplicates("name").show() //删除指定的列重复的行(基于一个列的子集删除重复)
ds1.dropDuplicates(Array("dynasty","dob")).show() //删除dynasty和dob都重复的行

BUG点

安装使用Vue3中的vite初始化vue项目结束以后,需要npm i装插件的时候报错了,分析报错信息是npm版本太低了,有些插件装不了,因为vite是比较新的构建项目的一种方式,比较轻量化大小比较小,速度也快了不止一倍,在项目越大中和常规初始化的差距越明显。

image-20230328200035204

扩展学习

蓝桥杯 123

这题主要用到二分以及数据累加,难度一般,明天打算优化优化算法再研究研究。

image-20230328202738987

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import java.util.Scanner;

public class Main {
/*
a[i] = a[i-1] + i 前i个区间的个数之和
s[i] = s[i-1] + i i的前缀和
*/
static int maxLen = 1414215;
static long[] a = new long[maxLen];
static long[] s = new long[maxLen];
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
//在此输入您的代码...\
a[0] = 0;
for (int i = 1; i < maxLen; i++) {
a[i] = a[i-1] + i;
}
s[0] = 0;
for (int i = 1; i < maxLen; i++) {
s[i] = s[i-1] + a[i];
}
int n = scan.nextInt();
for (int i = 0; i < n; i++) {
long l = scan.nextLong();
long r = scan.nextLong();
System.out.println(preSum(r) - preSum(l-1));

}
scan.close();
}

private static long preSum(long num) {
//找到num处在哪个区间
int l = 0,r = maxLen;
int mid;
while (l<r){
mid = (l+r+1)>>1;
if (a[mid] > num){
r = mid - 1;
}else l = mid ;
}
return s[l] + a[(int) (num - a[l])];
}
}

日总结

27日

今天学习时间只有上午部分,还好这个时间学习的内容不是很难,SparkSQL自己已经是非常熟悉了,对DF的创建以及操作,基础部分没什么难度,案例也没有必要去做了。今天的学习状态也还可以,明天学高级部分看能不能学习到有用的东西

28日

大数据训练部分:今天上午学习的大数据,SparkSQL高级部分所能学到的东西还是听过的,比如自定义sql函数,以及如何处理字段为null的数据,删除或者替换,还有去重相同的数据,都是有方法的。基本这一章的内容就是对sql中高级的函数的讲解以及如何使用,大半部分是对dsl风格的讲解,因为之前学过一段时间了,前几天也着重复习了,所以这部分对自己没什么难度。

Vue路由(重点)

学习日期:3.29

所学内容概述

  1. 理解: 一个路由(route)就是一组映射关系(key - value),多个路由需要路由器(router)进行管理。
  2. 前端路由:key是路径,value是组件。

1.基本使用

  1. 安装vue-router,命令:npm i vue-router

  2. 应用插件:Vue.use(VueRouter)

  3. 编写router配置项:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //引入VueRouter
    import VueRouter from 'vue-router'
    //引入Luyou 组件
    import About from '../components/About'
    import Home from '../components/Home'

    //创建router实例对象,去管理一组一组的路由规则
    const router = new VueRouter({
    routes:[
    {
    path:'/about',
    component:About
    },
    {
    path:'/home',
    component:Home
    }
    ]
    })

    //暴露router
    export default router
  4. 实现切换(active-class可配置高亮样式)

    1
    <router-link active-class="active" to="/about">About</router-link>
  5. 指定展示位置

    1
    <router-view></router-view>

2.几个注意点

  1. 路由组件通常存放在pages文件夹,一般组件通常存放在components文件夹。
  2. 通过切换,“隐藏”了的路由组件,默认是被销毁掉的,需要的时候再去挂载。
  3. 每个组件都有自己的$route属性,里面存储着自己的路由信息。
  4. 整个应用只有一个router,可以通过组件的$router属性获取到。

3.多级路由(多级路由)

  1. 配置路由规则,使用children配置项:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    routes:[
    {
    path:'/about',
    component:About,
    },
    {
    path:'/home',
    component:Home,
    children:[ //通过children配置子级路由
    {
    path:'news', //此处一定不要写:/news
    component:News
    },
    {
    path:'message',//此处一定不要写:/message
    component:Message
    }
    ]
    }
    ]
  2. 跳转(要写完整路径):

    1
    <router-link to="/home/news">News</router-link>

4.路由的query参数

  1. 传递参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- 跳转并携带query参数,to的字符串写法 -->
    <router-link :to="/home/message/detail?id=666&title=你好">跳转</router-link>

    <!-- 跳转并携带query参数,to的对象写法 -->
    <router-link
    :to="{
    path:'/home/message/detail',
    query:{
    id:666,
    title:'你好'
    }
    }"
    >跳转</router-link>
  2. 接收参数:

    1
    2
    $route.query.id
    $route.query.title

5.命名路由

  1. 作用:可以简化路由的跳转。

  2. 如何使用

    1. 给路由命名:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      {
      path:'/demo',
      component:Demo,
      children:[
      {
      path:'test',
      component:Test,
      children:[
      {
      name:'hello' //给路由命名
      path:'welcome',
      component:Hello,
      }
      ]
      }
      ]
      }
    2. 简化跳转:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      <!--简化前,需要写完整的路径 -->
      <router-link to="/demo/test/welcome">跳转</router-link>

      <!--简化后,直接通过名字跳转 -->
      <router-link :to="{name:'hello'}">跳转</router-link>

      <!--简化写法配合传递参数 -->
      <router-link
      :to="{
      name:'hello',
      query:{
      id:666,
      title:'你好'
      }
      }"
      >跳转</router-link>

6.路由的params参数

  1. 配置路由,声明接收params参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    {
    path:'/home',
    component:Home,
    children:[
    {
    path:'news',
    component:News
    },
    {
    component:Message,
    children:[
    {
    name:'xiangqing',
    path:'detail/:id/:title', //使用占位符声明接收params参数
    component:Detail
    }
    ]
    }
    ]
    }
  2. 传递参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <!-- 跳转并携带params参数,to的字符串写法 -->
    <router-link :to="/home/message/detail/666/你好">跳转</router-link>

    <!-- 跳转并携带params参数,to的对象写法 -->
    <router-link
    :to="{
    name:'xiangqing',
    params:{
    id:666,
    title:'你好'
    }
    }"
    >跳转</router-link>

    特别注意:路由携带params参数时,若使用to的对象写法,则不能使用path配置项,必须使用name配置!

  3. 接收参数:

    1
    2
    $route.params.id
    $route.params.title

7.路由的props配置

​ 作用:让路由组件更方便的收到参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
name:'xiangqing',
path:'detail/:id',
component:Detail,

//第一种写法:props值为对象,该对象中所有的key-value的组合最终都会通过props传给Detail组件
// props:{a:900}

//第二种写法:props值为布尔值,布尔值为true,则把路由收到的所有params参数通过props传给Detail组件
// props:true

//第三种写法:props值为函数,该函数返回的对象中每一组key-value都会通过props传给Detail组件
props(route){
return {
id:route.query.id,
title:route.query.title
}
}
}
  1. 作用:控制路由跳转时操作浏览器历史记录的模式
  2. 浏览器的历史记录有两种写入方式:分别为pushreplacepush是追加历史记录,replace是替换当前记录。路由跳转时候默认为push
  3. 如何开启replace模式:<router-link replace .......>News</router-link>

9.编程式路由导航

  1. 作用:不借助<router-link> 实现路由跳转,让路由跳转更加灵活

  2. 具体编码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    //$router的两个API
    this.$router.push({
    name:'xiangqing',
    params:{
    id:xxx,
    title:xxx
    }
    })

    this.$router.replace({
    name:'xiangqing',
    params:{
    id:xxx,
    title:xxx
    }
    })
    this.$router.forward() //前进
    this.$router.back() //后退
    this.$router.go() //可前进也可后退

10.缓存路由组件

  1. 作用:让不展示的路由组件保持挂载,不被销毁。

  2. 具体编码:

    1
    2
    3
    <keep-alive include="News"> 
    <router-view></router-view>
    </keep-alive>

11.两个新的生命周期钩子

  1. 作用:路由组件所独有的两个钩子,用于捕获路由组件的激活状态。
  2. 具体名字:
    1. activated路由组件被激活时触发。
    2. deactivated路由组件失活时触发。

12.路由守卫

  1. 作用:对路由进行权限控制

  2. 分类:全局守卫、独享守卫、组件内守卫

  3. 全局守卫:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    //全局前置守卫:初始化时执行、每次路由切换前执行
    router.beforeEach((to,from,next)=>{
    console.log('beforeEach',to,from)
    if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
    if(localStorage.getItem('school') === 'atguigu'){ //权限控制的具体规则
    next() //放行
    }else{
    alert('暂无权限查看')
    // next({name:'guanyu'})
    }
    }else{
    next() //放行
    }
    })

    //全局后置守卫:初始化时执行、每次路由切换后执行
    router.afterEach((to,from)=>{
    console.log('afterEach',to,from)
    if(to.meta.title){
    document.title = to.meta.title //修改网页的title
    }else{
    document.title = 'vue_test'
    }
    })
  4. 独享守卫:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    beforeEnter(to,from,next){
    console.log('beforeEnter',to,from)
    if(to.meta.isAuth){ //判断当前路由是否需要进行权限控制
    if(localStorage.getItem('school') === 'atguigu'){
    next()
    }else{
    alert('暂无权限查看')
    // next({name:'guanyu'})
    }
    }else{
    next()
    }
    }
  5. 组件内守卫:

    1
    2
    3
    4
    5
    6
    //进入守卫:通过路由规则,进入该组件时被调用
    beforeRouteEnter (to, from, next) {
    },
    //离开守卫:通过路由规则,离开该组件时被调用
    beforeRouteLeave (to, from, next) {
    }

13.路由器的两种工作模式

  1. 对于一个url来说,什么是hash值?—— #及其后面的内容就是hash值。
  2. hash值不会包含在 HTTP 请求中,即:hash值不会带给服务器。
  3. hash模式:
    1. 地址中永远带着#号,不美观 。
    2. 若以后将地址通过第三方手机app分享,若app校验严格,则地址会被标记为不合法。
    3. 兼容性较好。
  4. history模式:
    1. 地址干净,美观 。
    2. 兼容性和hash模式相比略差。
    3. 应用部署上线时需要后端人员支持,解决刷新页面服务端404的问题。

总结

这两天把Vue的重中之重的路由学完了,基本都是掌握了,内容还是挺多的,但是我觉得不是很难,之前给金师附小渲染的时候,主要接触的就是路由,所以掌握就没什么难度,我的理解就是通过路由实现跳转不同的组件,不用刷新页面,然后就是缓存以及守卫,也是比较简单,就几个方法几个参数,

Vue3(重点)

学习日期:3.30-3.31

所学内容概述

创建Vue3.0

vue-cli创建

和vue2一样了那就

1
2
3
4
5
6
7
8
9
## 查看@vue/cli版本,确保@vue/cli版本在4.5.0以上
vue --version
## 安装或者升级你的@vue/cli
npm install -g @vue/cli
## 创建
vue create vue_test
## 启动
cd vue_test
npm run serve

vite创建(推荐)

这种创建的方式运行和打包都会高效很多,端口号和普通的不一样默认是3000

1
2
3
4
5
6
7
8
## 创建工程
npm init vite-app <project-name>
## 进入工程目录
cd <project-name>
## 安装依赖
npm install
## 运行
npm run dev

Composition API(重点)

这是Vue3的重中之重,因为内容比较多,放网站上去了。https://u7u7.top/posts/adf5c49.html下面vue3部分

总结

花了两三天的时间把Vue3基础给学习了,Vue3给我最直观的改变就是使用了composition API把setup作为舞台,将原来的vue2那种数据以及方法在不同生命周期地方,使用不同的方法,都放在了setup中,最后只要return就行了,和java和python中所定义的方法一样,里面有属性以及一些方法。而且代码看起来舒服很多,可读性对我而言强了,上手还是蛮快的,也没什么难点,生命周期部分也非常的知名见意,我觉得vue3的setup这样的写法还是很适合我的。而且原本Vue2中的语法在vue3中都是可以使用的,虽然官方不建议这么做,但是也能写能用,如果和Vue3有冲突的地方,会以Vue3为主,因为自己是使用vite创建的vue3工程,npm run dev跑起来真的比serve快了很多,打包也快了不少。我觉得Vue3的核心应该是响应式proxy,属性值的读写、属性的添加、属性的删除都可以通过proxy完成,所以我认为该点为核心部分,而且基本API都离不开proxy。