如何去除数学表达式中的无用括号

最近工作上遇到了这个问题,挺有意思,终于遇到了一些有点意思的东西,于是深入研究了下。

问题背景

问题大概背景是 系统中会展示数学公式,公式是用树来表达的,以运算符号为节点,比如 3=1+2,首先根节点是=,然后左子节点为3,右子节点是+,接下来+的左子节点是1,右子节点是2,就是以这种方式来表示一个数学公式。

然后展示的时候并不知道哪些地方要加括号,因为括号这个信息隐藏在树里了,展示的时候只能是每个操作符的两边都加上括号,这样展示出来就非常繁琐,那么就得想一个算法来将这些无用括号去除掉。

算法描述

算法大概是这样的,只要括号内的非括号部分的数学符号的最小优先级小于括号任意一边数学符号的优先级,那么括号就是必须的,否则就是无用的括号。

比如q * (a * b + c * d) + c,括号内部的+*,最小优先级是+,括号两边是*+,小于其中一个*的优先级,那么这个括号就是必须的。

同理,q * (a * b * c * d) + c,这个括号就是无用的。

原理就是括号里面的优先级如果都是高于括号两边,那么不加括号,也是会先计算括号里面的运算。

另外为啥是非括号部分,因为内部如果已经有括号了,那么子括号肯定是先运算了,可以把这个子括号当一个整体对待。

当然目前还有一个问题,就是遇到/-的时候会有一些问题,比如a/(b/c)或者a-(b-c),主要原因是/-有取反的作用,所以需要特殊处理:也就是如果/-右侧的括号需要保留。

代码实现

回到问题背景,按照这样的数据结构,其实算是简化了。结点都是运算符,那么如果把结点左侧作为括号包裹的内容,只需要比较结点左侧的非括号部分最小优先级和结点符号优先级就可以了。同理,右侧也是一样的。

而且针对/-的特殊处理也更简单了,只需要判断结点运算符是不是/-,如果是,那么右侧部分需要加括号,当然如果需要的话。

另外代码还实现了一个功能:小括号,中括号,大括号的嵌套。主要原理就是如果某个结点需要加括号,那么递归的去找一下左节点和右节点目前使用的最大括号,在此基础上再用更高一级的括号。

实现代码大概如下:

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
class BracketProcessor:
small_bracket = 'small'
middle_bracket = 'middle'
big_bracket = 'big'
priority_default = 3
operator_priority = {
'+': 1,
'-': 1,
'*': 2,
'/': 2,
}

def __init__(self, formula_items, expression):
self.formula_items = formula_items
self.expression = expression
self.formula_dict = self.get_formula_dict(formula_items)
self.start_node = self.get_start_node(self.formula_dict)
self.brackets = {
self.small_bracket: 0,
self.middle_bracket: 1,
self.big_bracket: 2
}

@classmethod
def get_formula_dict(cls, formula_items):
formula_dict = {}
for item in formula_items:
origin = item.data['origin']
if 'left' not in origin and 'node' in origin:
origin.update(origin['node'])
if 'name' not in origin:
origin['name'] = origin['operator']
formula_dict[origin['index']] = item
return formula_dict

@classmethod
def get_start_node(cls, formula_dict):
for key, value in formula_dict.items():
if value.data['origin']['name'] == '=':
left_node = formula_dict.get(value.data['origin']['left'])
right_node = formula_dict.get(value.data['origin']['right'])
return left_node if left_node.data['origin'].get('operator') else right_node
return None

def process(self):
if self.expression and '(' not in self.expression:
return
# 搜索 left 和 right 的最低优先级 X,Y,
# 如果 X 优先级小于 start_node 的优先级,那么 X 需要括号
# 否则,不需要括号
# 对于 Y 同理
# 只在一个分支上检查level
# 特例 / - 右边分支如果有同级的 需要带括号
# 括号内部 非括号部分的优先级
self.fill_node_bracket(self.start_node)

def ensure_bracket(self, child_bracket):
if not child_bracket:
return self.small_bracket
child_bracket_order = self.brackets[child_bracket]
for bracket, order in self.brackets.items():
if order > child_bracket_order:
return bracket
return None

def find_child_bracket(self, node):
if not node:
return
bracket = node.data['origin'].get('bracket')
if node.data['origin']['left'] != -1:
left_bracket = self.find_child_bracket(self.formula_dict.get(node.data['origin']['left']))
if left_bracket:
if not bracket:
bracket = left_bracket
else:
bracket = left_bracket if self.brackets[left_bracket] > self.brackets[bracket] else bracket
if node.data['origin']['right'] != -1:
right_bracket = self.find_child_bracket(self.formula_dict.get(node.data['origin']['right']))
if right_bracket:
if not bracket:
bracket = right_bracket
else:
bracket = right_bracket if self.brackets[right_bracket] > self.brackets[bracket] else bracket
return bracket

def fill_node_bracket(self, start_node, level=1):
start_node_priority = self.operator_priority.get(start_node.data['origin']['name'], self.priority_default)

left = self.formula_dict.get(start_node.data['origin']['left'])
if left:
self.fill_node_bracket(left, level=level + 1)
left_lowest_priority = self.get_lowest_priority(left)
if left_lowest_priority < start_node_priority:
child_bracket = self.find_child_bracket(left)
left.data['origin']['bracket'] = self.ensure_bracket(child_bracket)

right = self.formula_dict.get(start_node.data['origin']['right'])
if right:
self.fill_node_bracket(right, level=level + 1)
right_lowest_priority = self.get_lowest_priority(right)
if right_lowest_priority < start_node_priority or (start_node.data['origin']['name'] in ['/', '-'] and right_lowest_priority == start_node_priority):
child_bracket = self.find_child_bracket(right)
right.data['origin']['bracket'] = self.ensure_bracket(child_bracket)

def get_lowest_priority(self, start_node):
if not start_node or start_node.data['origin'].get('bracket'):
return self.priority_default
node_priority = self.operator_priority.get(start_node.data['origin']['name'], self.priority_default)
if start_node.data['origin']['left'] != -1:
left_priority = self.get_lowest_priority(self.formula_dict.get(start_node.data['origin']['left']))
node_priority = min(node_priority, left_priority)
if start_node.data['origin']['right'] != -1:
right_priority = self.get_lowest_priority(self.formula_dict.get(start_node.data['origin']['right']))
node_priority = min(node_priority, right_priority)
return node_priority

Get新技能——换锁芯

租了个新房子,这个房子之前住的人比较杂,所以需要换一个锁芯。

看了个视频觉得挺简单的,但是实际拆了之后出现了一些问题,因为不是很了解,所以小心翼翼的,现在想来也挺简单的。

首先是用螺丝刀将门锁内侧的两个螺丝拧开,把内侧把手拆掉,然后把门外侧把手拆掉。我这里内侧很好拆,但是外侧不好弄,刚开始以为有什么玄机,后来发现还是要大力出奇迹,来回扭一扭就拆掉了。其实外侧把手相当于一个螺母,内侧把手穿了一个螺丝,通过这种方式连接到一起了。

接下来是把门锁中间固定锁芯的螺丝拆掉,拆掉之后有的锁芯可以直接拔下来,我这边拔不掉,当时我就卡在这里了,后来在网上搜了一下说是插入钥匙拧一下,拧到合适的角度就能拔下来了。拔下来之后才发现,锁芯中间有个小塑料环,拧到一定角度才能和锁芯主体平齐,这样才能拔出来。

锁芯拔出来之后,量一下中间螺丝孔到锁芯两边的距离,然后网上下单,完了把旧锁芯再装回去。

新锁芯回来,按照上面的步骤把旧锁芯替换为新锁芯,装回去,万事大吉。

搬家了

中秋节那天开始打包搬家,我真是低估了家里的东西多少,高估了自己的搬运能力。

幸亏把好朋友官哥叫来帮忙了,否则我中秋节三天累死都不可能搬完。

当天打包完成,吃个火锅喝个小酒,吃饱喝足之后晚上干活!

image.png

从晚上8点开始,搬到晚上12点多。

image.png

第二天从早上9点搬到中午12点。

真的太累了。

搬完家,开始新生活,加油~

人生的松弛感

最近看到这样一条微博,很有意思。

image.png

我想大多数人因为证件过期登不了机,都会生气懊恼大崩溃吧,毕竟从小到大,我们都习惯了掌控生活。

任何计划被打乱,没有按照原本的想象进行,我们都会表现出深深的失望和恐慌,整个人活得特别像刺猬,每根刺都竖立着蓄势待发。

与之相反,面对小孩证件过期的这个突发事故,这个家庭没有揪着不放,没有放大这件事情对行程的影响。

他们很从容地应对当下,没有急切,没有压力,非常轻松地接受着这突如其来的变故,放任其发展,好像一切的发生本应如此,没有什么好气愤,好纠缠。

那一家人的那种人生的松弛感,让博主感动到想哭,我想,这是因为它是这个时代所稀缺的状态,怎能不让人羡慕呢?!

那到底什么是人生的松弛感呢?

人生的松弛感,就是面对世界的任何变化和不如意,你都完全接受的状态,爱咋样咋样!

这个世界的真相,就是所有事情都不是你能控制的。

每一次发生在你身上的事情,有时候在你的预期内,有时候出乎你的意料,但本质上,它的发生都有很多的偶然性和随机性,有很多的影响因素是大脑意识感知不到的。

如果你时刻紧张兮兮地面对生活,期待生活按照你所期望的那样在你眼前展开,那你注定要失望,而当你持续地因此而不开心,患得患失智只会让你的人生走下坡路。

相反,那种人生的松弛感,不是摆烂,不是无所谓,而是与世界的变化和解,跟自己的执念和解,不浪费心力与它纠缠,潇洒转身继续轻松地迎接人生的下一站。

所以,你要转变思路,我们来到这个世界,是来玩的,不设预期,不过多地计划,而是随着变化调整自己,用心体验生活,不急不躁地顺着生命之流而下。

拿工作来说,工作重要吗?当然重要,但是更重要的是我自己开心,所以你不用过度在意别人是怎么看你,不用忧心自己能不能升职加薪,不用过度思考自己怎样才能脱颖而出。

你真正要做的,是追求那种人生的松弛感,让自己用一个放松的心态去工作,不用跟别人比较,不用纠缠于当下的不顺,不用计较付出和回报,而是投入到当下真正该做的事情中去。

很神奇的是,当你的心态彻底转变之后,一切开始变得顺利起来,你在工作中更游刃有余,成就感暴增,瞎逼逼的同事也不常出现在你面前,连老板对你也开始赞赏有加,认可度提升。

这些都是我的亲身体验——

当你松弛了,你就没有太多得失心,也没有太多的焦虑内耗,你所有的能量都集中在你最需要实现的价值上。

人生的松弛感太难得了,它来自于你对自我的认可,对世界的不期待,以及对人生的负责。

底层的逻辑可能就是“爱谁谁,爱咋地咋地,我只要能自洽通透地过好自己的人生就好!”

为了获得人生的松弛感,我有如下建议:

  1. 临在当下,专注事情本身

你需要建立“成长型思维”和“体验者思维”。

“成长型思维”让你在面对过去的事情时认识到自己的不足,看到自身改进的契机,进而不断迭代精进自己。

“体验者思维”则让我们面对未来的生活时,不抱期待,不设预期,而是抱有更多的好奇和探索欲,让自己沉浸在那些该做的事情中。

抱持人生松弛感的人,总是能在过去的坏事中看到好的一面,也不会因为未来的预期而作茧自缚,相反,他们总是可以但行好事,莫问前程。

  1. 臣服生活,接纳一切

不拧巴,不较劲,生活不过就是见招拆招。

在《清醒地活》这本书中,作者指出,那些引发我们的执念的人事物,都是束缚在我们心灵的能量。这股被束缚的能量,因为我们的不接纳不臣服,而淤积于我们内心,成为阻挡我们成长的力量。

我们之所以有纠结有内耗,都是因为我们不去放手那些执着的人事物。

如果你放手了,臣服生活,接纳一切,这股被束缚的能量才能被释放。反过来,这股被释放的能量会重新流经你,成为支撑你往上走的意志力。

这时候,你就找到了人生的松弛感。

换句话说,当你不再执着,而是臣服生活,接纳一切,你会有更多的意志力去做好当下该做的事情。

很多人抱怨自己没有意志力去早起,去健身,不妨去审视你是否有太多对生活的执念,以至于你时刻恐慌焦虑,意志力缺失。

  1. 了解自己,接纳自己

回归到自身,为了获得人生的松弛感,你需要去了解自己,探索自己。

只有了解自己的人,才会真正地接纳自己,获得人生的松弛感。

你可以看看那些具有松弛感的明星,比如王菲,她应该很清楚自己要什么,很清楚什么对自己最重要,所以当她接纳自己真实的样子的时候,她就可以不惧外界的评判而真实地表达自己,所以我们总是可以看到她呈现给我们的那种淡定从容和那种“爱谁谁”的坦然。

我们对自己人生的定义,不来自于外界的评价体系,而是深深地来自于对自我的了解和接纳,那我们就不会过度地焦虑迷茫,就不会急于自证,不会操之过急,因为只有耐得住漫长的时间线,水到渠成才会是一件非常自然的事情。

人生的松弛感,是生活方式和人生态度由内而外地发散。

它应该成为我们人生的底色,让我们从容淡定地历经岁月的洗礼,并对于所有的体验都保持开放而随性的态度。

人生漫长,一路上会有高潮,也会有低谷。

慢慢地,你会发现,松弛感才是人生的王炸。

共勉~

雨天漫游

周日无事,跟媳妇开着车随便逛

image.png

image.png

我们老家的洋芋片夹馍,香太太。

馍中有乾坤,一馍一世界~

image.png

一个子查询优化

问题背景

同事写了一个接口,这个接口涉及到下面三个表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
User:
- id
- username
- created_utc
- ...

UserLoginInfo
- id
- user_id
- created_utc
- ...

Project
- id
- user_id
- name
- ...

需求是查询出:用户,最近用户登录时间,以及创建的项目数。这里的一条UserLoginInfo记录,代表一次登录,Project就是用户创建的项目记录,可能有多条。

同事实现是用子查询,Python里实现大概如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
user_projects_query = (
db_session.query(func.count(Project.id).label("cnt"))
.filter(
Project.user_id == User.id,
Project.deleted == 0,
)
.label("user_projects_cnt")
)
login_utc_query = (
db_session.query(UserLoginInfo.created_utc)
.filter(UserLoginInfo.user_id == User.id)
.order_by(UserLoginInfo.id.desc())
.limit(1)
.label("latest_login_time")
)
default_login_query = func.COALESCE(login_utc_query, User.created_utc).label("default_login_time")

order_mapping = {
"login_time": lambda desc: (default_login_query.desc(), User.id.desc()) if desc else (default_login_query, User.id),
"user_projects": lambda desc: (user_projects_query.desc(), User.id.desc()) if desc else (user_projects_query, User.id),
"created_utc": lambda desc: (User.created_utc.desc(), User.id.desc()) if desc else (User.created_utc, User.id)
}
query = db_session.query(User, user_projects_query, default_login_query).filter_by(deleted=0).order_by(*condition)

这里的condition是在order_mapping中根据desc的值进行选择的

上面用Python写的代码翻译成raw sql就是

1
2
3
4
5
6
7
SELECT 
"user".id as user_id, "user".username as user_name,
(SELECT count(project.id) AS cnt FROM project WHERE project.user_id = "user".id AND project.deleted = 0) AS user_projects_cnt,
coalesce((SELECT user_login_info.created_utc FROM user_login_info WHERE user_login_info.user_id = "user".id ORDER BY user_login_info.id DESC LIMIT 1), "user".id) AS default_login_time
FROM "user"
WHERE "user".deleted = 0 ORDER BY default_login_time DESC, "user".ext_id DESC
LIMIT 10 OFFSET 0

分析

这一实现一打眼就知道肯定会有问题,因为有很多关联子查询,在数据集小的情况下体现不出来,一旦数据集稍有增大,速度就会大幅下降。因为一般来说,关联子查询在查询优化器里是不好进行优化的,最后出来的算法大概率是Nested loop

下面是在测试环境用explain执行后的结果,果然是Nested Loop占了相当长的时间

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
Sort  (cost=750000347885.10..750000347885.29 rows=75 width=566)
Sort Key: ((SubPlan 2)) DESC, "user".id DESC
-> Bitmap Heap Scan on "user" (cost=8.75..750000347882.77 rows=75 width=566)
Recheck Cond: (ext_sys = 1)
Filter: (deleted = 0)
-> Bitmap Index Scan on idx_user_ext_sys (cost=0.00..8.73 rows=78 width=0)
Index Cond: (ext_sys = 1)
SubPlan 1
-> Aggregate (cost=19.27..19.28 rows=1 width=8)
-> Bitmap Heap Scan on document (cost=4.21..19.25 rows=5 width=4)
Recheck Cond: (user_id = "user".ext_id)
Filter: ((user_sys = 1) AND (deleted = 0))
-> Bitmap Index Scan on idx_user_id (cost=0.00..4.21 rows=8 width=0)
Index Cond: (user_id = "user".ext_id)
SubPlan 2
-> Limit (cost=10000004618.93..10000004618.94 rows=1 width=4)
-> Sort (cost=10000004618.93..10000005141.43 rows=209000 width=4)
Sort Key: user_login_info.created_utc DESC
-> Nested Loop (cost=10000000000.15..10000003573.93 rows=209000 width=4)
-> Seq Scan on user_login_info (cost=10000000000.00..10000000942.95 rows=1045 width=4)
Filter: (user_id = "user".id)
-> Materialize (cost=0.15..18.98 rows=200 width=0)
-> Index Only Scan using idx_document_user_sys on document document_1 (cost=0.15..17.98 rows=200 width=$
)
Index Cond: (user_sys = 1)

Planning time: 0.212 ms
Execution time: 229.674 ms

解决

那么现在首要问题就是如何避免子查询,可以看到需求里是需要最近用户登录时间用户的项目数,那么一个很自然的思路就是先把这两个数据查出来,然后再和 User Join到一起进行分页即可,这样就可以避免子查询嵌套到父查询里了,这里涉及到一个子查询的优化方法,尽量将关联子查询上推,上推到和父查询一个层级以避免 Nested Loop。

要实现先查出来某些数据,然后在后面的查询中使用,那么就是 CTE(公用表表达式) 了!公用表表达式,本质是允许用户通过一个子查询语句定义出一个临时表,然后在各个地方都可以使用这个临时表。

实现如下:

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
user_projects_query = db_session.query(
Project.user_id, func.count(Project.id).label("cnt")
).filter(
Project.user_id == User.id,
Project.deleted == 0,
).group_by(Project.user_id).cte('user_projects_query')
project_count = func.COALESCE(user_projects_query.c.cnt, 0)

login_utc_query = db_session.query(
UserLoginInfo.user_id, func.max(UserLoginInfo.created_utc).label('login_utc')
).group_by(UserLoginInfo.user_id).cte('login_utc_query')
login_utc = func.COALESCE(login_utc_query.c.login_utc, User.created_utc).label('login_utc')

order_mapping = {
"login_time": lambda desc: (login_utc.desc(), User.id.desc()) if desc else (login_utc, User.id),
"user_docs": lambda desc: (document_count.desc(), User.id.desc()) if desc else (document_count, User.id),
"created_utc": lambda desc: (User.created_utc.desc(), User.id.desc()) if desc else (User.created_utc, User.id)
}

query = db_session.query(
User, document_count, login_utc
).join(
user_projects_query, user_projects_query.c.user_id == User.id, isouter=True
).join(
login_utc_query, login_utc_query.c.user_id == User.id, isouter=True
).filter(
User.deleted == 0,
).order_by(*condition)

对应的raw sql如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
WITH user_projects_query AS 
(SELECT project.user_id AS user_id, count(project.id) AS cnt
FROM project
WHERE project.deleted = 0 GROUP BY document.user_id
),
login_utc_query AS
(SELECT user_login_info.user_id AS user_id, max(user_login_info.created_utc) AS login_utc
FROM user_login_info GROUP BY user_login_info.user_id
)
SELECT "user".id AS user_id, "user".username AS username, user_projects_query.cnt AS user_projects_query_cnt, coalesce(login_utc_query.login_utc, "user".created_utc) AS login_utc
FROM "user" LEFT OUTER JOIN user_projects_query ON user_projects_query.user_id = "user".id LEFT OUTER JOIN login_utc_query ON login_utc_query.user_id = "user".id
WHERE "user".deleted = 0
ORDER BY login_utc DESC, "user".ext_id DESC
LIMIT 20 OFFSET 0

在测试环境跑一下 EXPLAIN ANALYSE:

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
 Limit  (cost=2832.33..2832.38 rows=20 width=28) (actual time=11.156..11.162 rows=20 loops=1)
CTE user_projects_query
-> HashAggregate (cost=31.53..31.88 rows=35 width=12) (actual time=0.103..0.108 rows=26 loops=1)
Group Key: document.user_id
-> Bitmap Heap Scan on document (cost=9.69..30.69 rows=168 width=8) (actual time=0.018..0.074 rows=181 loops=1)
Recheck Cond: (user_sys = 1)
Filter: (deleted = 0)
Rows Removed by Filter: 29
Heap Blocks: exact=18
-> Bitmap Index Scan on idx_document_user_sys (cost=0.00..9.65 rows=200 width=0) (actual time=0.013..0.013 rows=211 loops=
1)
Index Cond: (user_sys = 1)
CTE login_utc_query
-> GroupAggregate (cost=0.29..2760.73 rows=46 width=8) (actual time=1.135..10.853 rows=70 loops=1)
Group Key: user_login_info.user_id
-> Index Scan using idx_user_login_info_user_id on user_login_info (cost=0.29..2515.61 rows=48932 width=8) (actual time=0.005..5
.915 rows=48804 loops=1)
-> Sort (cost=39.73..39.99 rows=107 width=28) (actual time=11.155..11.158 rows=20 loops=1)
Sort Key: (COALESCE(login_utc_query.login_utc, "user".created_utc)) DESC, "user".ext_id DESC
Sort Method: top-N heapsort Memory: 27kB
-> Hash Left Join (cost=2.78..36.88 rows=107 width=28) (actual time=11.028..11.130 rows=107 loops=1)
Hash Cond: ("user".id = login_utc_query.user_id)
-> Hash Left Join (cost=1.28..34.54 rows=107 width=28) (actual time=0.137..0.210 rows=107 loops=1)
Hash Cond: ("user".id = user_projects_query.user_id)
-> Index Scan using user_pkey on "user" (cost=0.14..32.67 rows=107 width=20) (actual time=0.014..0.066 rows=107 loops=
1)
Filter: (deleted = 0)
Rows Removed by Filter: 5
-> Hash (cost=0.70..0.70 rows=35 width=12) (actual time=0.119..0.119 rows=26 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 10kB
-> CTE Scan on user_projects_query (cost=0.00..0.70 rows=35 width=12) (actual time=0.104..0.115 rows=26 loops=1)
-> Hash (cost=0.92..0.92 rows=46 width=8) (actual time=10.887..10.887 rows=70 loops=1)
Buckets: 1024 Batches: 1 Memory Usage: 11kB
-> CTE Scan on login_utc_query (cost=0.00..0.92 rows=46 width=8) (actual time=1.136..10.875 rows=70 loops=1)
Planning time: 0.218 ms
Execution time: 11.211 ms

可以看到相比之前的子查询的229ms,使用cte的sql已经降到了11ms了

总结

不要在SELECT语句中滥用子查询

子查询只有必要的时候再用,使用时候应该注意考虑如何将子查询与SQL语句的主干进行融合,子查询不是独立的黑盒数据块,应该与主语句通盘考虑后再结合使用。

在使用子查询的地方,往往可以通过CTE表达式进行优化,核心思路就是将子查询提升到和父查询一样的层级,避免Nested Loop,而且CTE表达式可读性更好。

总之在一个SQL有比较多的子查询时候一定要小心,记得 EXPLAIN ANALYSE

最近的失误操作

最近操作失误了很多:

  • 创成长低卖,本来长期仓位,由于自己心理原因:怕跌,给卖掉了,卖了之后一直涨。
  • 上证50,深红利低卖高买,也是本来说好的长期持有仓位,却想着做个T,没想到白白把成本T高。
  • 还没有出信号的时候,提前买入上证50,但是当天却没有信号,买后就跌。但是当天有信号的两个标的,没钱买,这两个标的反而大涨。一来一回损失不少。

总结一下原因:

第一、划定为长期仓位的,自己没有信仰,在看盘的时候就想去操作一把,就导致反向操作的发生。以后长期仓位的平时不操作,只在有买入信号和卖出信号的时候操作。

第二、坚定执行策略,按信号买卖,把自己当成机器人即可,减少甚至做到不看盘,减少突发奇想的操作。

以后计划:

  1. 补齐上证50,红利,深红利,创成长,双创的长期持有仓位,做股权现金平衡,只在策略信号发生时候操作
  2. 坚定执行策略,按照信号买卖
2022年8月23日更新

截至今天,昨天错误买入的上证50,已经亏损1%,而如果买入新能车则盈利2.3%,一来一回损失3.3%收益率,啧啧。

2022年8月24日更新

今天在-0.29%的时候认亏服输卖掉了,这一波总共损失3.59%的收益率,好吧,也不算太惨重,及时止损。今天幸亏卖掉了,收盘的时候上证50跌1.01%。

2022年8月25日更新

MD今天早上也不知道哪里犯抽把红利在1%处给清仓了,后来涨了2.21%,我吐了,而且这个是长期仓位,为啥我要卖呢??

以后上面总结的两个原因和计划,切记切记。

要当爸爸了

媳妇怀孕现在快5周了,之前早孕试纸已经测出来了,今天去医院做了个B超,显示宫内孕,但是还属于比较早的时期,医生让2周后复查。

今年可算是事情比较多的一年了:

  • 交房装修,花钱费力(主要还是精力)
  • 房子租不成了,又得换
  • 怀孕

而这些都得一个人扛着,现在周末我就下厨房给媳妇做饭,还有考虑各种营养搭配,饮食也要健康。今天食谱:早上燕麦杂粮粥+饼+煎鸡蛋+生菜,下午面条+西兰花+胡萝卜+青菜+荷包蛋,晚上蒸了红薯和小南瓜。

目前还未决的事情有:

装修:卡到柜子这里了,等柜子来一装,门一装,插座灯装好,基本就差不多了。不过现在家具还没买,抽空还得去看看。

租房,确定了一家,130平一月4000块,但是家具简单,床是铁艺床,主要是考虑怀孕期间家里通风得好,然后家具少甲醛等有害物质也少。不过得等到9月10日才能搬,最近就先在这里继续狗着吧。

怀孕,最主要是保证媳妇的饮食健康,营养全面,至少得自己做饭吧,做一顿两顿好说,一直做饭洗碗,啧啧,是个挑战。最近疫情又开始肆虐,不知道什么时候是个头,疫情在平时倒也没啥,现在媳妇怀孕期间,就得特别小心了。

将来孩子出生了,就要做抉择了,如果公司能让我在家办公,就可以请一个保姆帮忙打理,要是不行,只能自己上了。经济问题只能期待A股了,希望FIRE基金能够稳定收益才行。哎,走着看吧。

克服中年危机

中产阶级的底座是「假设一切顺利」——这是小概率事件,从风控的角度看,底裤漏风。

现在能取得得高收入,应该多赚一年是一年,保持高储蓄,努力维持,而不是主动打破这种状态。

那么在40岁以前,主要做好三件事:

第一,大幅提高自己的“工资收入”;

第二,对投资有兴趣,开户越早越好,提升自己的投资能力,形成自己的盈利模式;

第三,要有一个好身体,不三高,不熬夜,吃得下,睡得着。

有此三项,保住中产阶级,克服中年危机应该也会有更强的底气了。

在这个社会上没有信任可言

2020年的时候我租下了这个房子,本来就是作为新房的过渡期居住,当时就想签三年合同,但是房东说最多只签两年,两年就两年吧,到时候再说。

转眼间到2022年了,房东当时是这么说的

image.png

当时我觉得:

  1. 签合同房东还得到房子来,我不喜欢被打扰
  2. 今年我的房子就装修好了,刚好到明年租房到期住进去,也就一年时间。

现在看来在这个社会上没有信任可言,一切嘴上说的都不可信,落实到文件上才是最真的事情

今天,房东给我下逐客令了,虽然比较委婉,但是就是那个意思。那现在这样对我的损害就是:

  1. 本来3200的房租,现在新租房要3500,一个月损失300,6个月损失1800
  2. 新找房要半个月中介费,损失1750
  3. 还要再搬家,劳心费神

所以我主张房东承担这个损失,但是他不行,他的意思就是他不可能承担这个损失。

我就说当初是你说的因为找中介麻烦,所以没有签合同,我们是基于基本的信任以及租房惯例(默认续期一年)来达成共识的。

但是我也知道,没有合同,我没法主张这个损失,在法理上站不住,因为:根据《合同法》第二百三十六条:“租赁期间届满,承租人继续使用租赁物,出租人没有提出异议的,原租赁合同继续有效,但租赁期限为不定期。“

不定期的意思就是房东随时可以不租,但是需要提前一个月通知,租客随时可以退租。

当然如果合同里有特殊规定的除外,比如到期后如果没有异议继续续一个合同期。

那现在我的这个情况就是之前合同找不到了,大概率是到期后重新签订合同。现在没有合同,所以就是不定期合同,房东现在的这个要求合法。

那我这里跟他沟通的重点就是 当初是你提出不签合同,我表示信任,也不想让你跑一趟,所以没签。用道德绑架房东,毕竟房东是银行工作人员,还是有一些羞耻感的。

最后达成的结果就是:房东补偿我1000元。

那我也只能见好就收了,然后尽快搬走,省的木乱了。

最后再强调一遍:在这个社会上没有信任可言,一切嘴上说的都不可信,落实到文件上才是最真的事情