MSCRM杂记(3)_DisSun_2012.03.06

本文讨论了一个中小型企业中报销流程存在的问题及优化方案。作者详细介绍了当前报销流程中存在的双重工作负担和资源浪费现象,并表达了对流程无纸化实现的困惑。
         连续好几天没干正事了,当然啦,也不是真的天天打酱油,主要是公司准备换开发团队。我就需要去收集下,新的开发需求,整理CRM的功能结构图、企业架构图、流程图。其实算不上工作量很大,不过如果你想把这件事件做好,做得细致,也不是那么容易。
        对于一个中小企业来说,其实企业本身就不是很规范,所以当你需要去了解流程图时,现实中的流程障碍,同样会影响CRM的流程。最近我在费用报销模块就很是纠结。
        在现行的体系中,我们公司的报销流程很繁琐,我不太了解别的公司在报销这一块中,是如何实现无纸化办公的。反正在我们公司,根本无法无纸化。因为现在一个业务人员要想报销一个费用,就必须走两道工序,一个是纸质报销(手写报销单+发票),另一个是系统报销,两者同时进行,少了一样都无法报销。而且审核的过程中,有3道审核工序,既是:业务员->大区经理->商务部->财务部。原本我跟高层沟通,建议先走系统流程,系统审核同意后,再直接邮寄回总部报销,可惜被否决了。相当于,业务员每次报销,不但会产生业务的费用,还要消耗两次邮寄的费用(从业务员到大区经理,再从大区经理到商务部),甚至会由于审批不通过而产生的退回邮寄费用,消耗了大量的人力、物力、财力。但是上级的思路是每个关口刷掉一批错误,免得将大量的错误堆积在公司总部。(有哪位高人告诉我,报销流程如何无纸化,因为领导不无道理,票据确实很重要,否则很难管控)每每涉及流程问题,就不仅仅是CRM的问题了,这个公司自身的规则流程息息相关。
        在天天跟各部门的沟通中,我也发现了自己的一个致命缺点——不愿意多沟通。有时候一想要跟人沟通就恐惧,我也不知这种恐惧是从哪里来的,也许跟自己比较内向的性格有关系。其实我们公司的领导和同事算是比较好的,不是那种整天板着脸,在这种环境下,都没把沟通做好,如果去了那些领导暴跳如雷的公司...是不是不用混了...
<think>我们注意到原始SQL中存在一个子查询`#{item}`,这可能是MyBatis等ORM框架中的占位符,表示传入一个列表。这里我们假设`#{item}`会被替换为一个由多个word_id组成的列表。 原始SQL的主要问题在于: 1. 在WHERE子句中使用了`d.word_id in #{item}`,但GROUP BY子句却按照`a.word_pur_cont_code, d.word_class_code`分组。这样的分组可能导致结果不准确,因为一个word_id可能对应多个word_class_code,而我们在分组时只按照word_class_code分组,并没有考虑word_id。但实际上,由于我们是通过word_id来过滤的,并且每个word_id在d表中可能对应多条记录(不同的word_class_code),所以分组后可能会出现重复的wordPurContCode和wordClassCode组合?但这里我们需要注意,原始查询中关联了多张表,可能造成重复记录。 2. 另外,原始查询中使用了多个LEFT JOIN,但最后用WHERE条件对d.word_id进行了过滤,这实际上将前面的LEFT JOIN变成了INNER JOIN(因为如果d表中没有对应的word_id,或者word_id不在列表中,那么该记录会被过滤掉)。所以,如果我们的意图是保留所有a表中的记录,即使它在d表中没有对应的word_id,那么我们需要调整条件的位置。 3. 我们还需要注意,GROUP BY的字段选择是否合理?因为SELECT中除了分组字段,还有a.word_pur_cont(它应该和a.word_pur_cont_code是一一对应的?)和d.word_class(和d.word_class_code一一对应?)。在MySQL中,如果不在GROUP BY中出现的字段,在SELECT中使用会有问题(除非它们是聚合函数或者与分组字段有函数依赖)。但这里我们并没有使用聚合函数,所以可能会得到非预期的结果。 4. 另外,原始SQL中使用了`LEFT JOIN ... ON`,但最后用WHERE条件限制了d.word_id,这可能导致左连接失效。 优化目标:使查询结果准确。 根据表结构,我们可能希望得到每个wordPurContCode和wordClassCode的组合,并且这些wordClassCode对应的word_id在传入的列表中。 优化建议: 1. 将d.word_id的条件移动到JOIN条件中,这样不会影响左连接的性质。 2. 考虑是否需要去重?因为多个连接可能导致重复记录,使用GROUP BY或者DISTINCT。 3. 注意,原始SQL中GROUP BY了a.word_pur_cont_code和d.word_class_code,但SELECT中还有a.word_pur_cont和d.word_class。如果这两个字段与分组字段是一一对应的,那么这样写在某些数据库(如MySQL)中是可以的,但在其他数据库(如PostgreSQL)中则不允许。为了通用性,我们可以将这两个字段也加入GROUP BY,或者使用聚合函数(如MAX、MIN等)来避免错误。 但是,我们观察到表名和字段名,可以推测: - a表(crm_ai_word_pur_config)中,word_pur_cont_code和word_pur_cont应该是一一对应的。 - d表(crm_ai_word_word_class_config)中,word_class_code和word_class应该是一一对应的,并且同一个word_id可能对应多个word_class_code(即多个分类)。 因此,我们可以这样修改: 方案1:将d.word_id的条件放在LEFT JOIN的ON条件中,这样能保持左连接的性质。同时,由于我们可能只需要d表中word_id在给定列表中的记录,所以这样不会影响其他表的连接。 但是,我们注意到原始SQL中还有一个LEFT JOIN到b表,然后b表再LEFT JOIN到c表,最后d表又是LEFT JOIN到c表。所以,如果d表没有匹配到(即word_id不在列表中),那么d表的字段都会为NULL。而我们的WHERE条件会过滤掉这些NULL记录(因为d.word_id in (...) 会过滤掉NULL)。所以,如果我们要保留a表中那些在d表中没有匹配的记录,那么就不应该用WHERE条件过滤,而应该将条件放在ON中。 根据需求,我们可能希望:即使d表中没有匹配的word_id,也要保留a表中的记录。那么,我们应该将d.word_id的条件放在d表的ON条件中。 修改后的SQL如下: 注意:由于原始SQL中使用了`d.word_id in #{item}`,我们将其改为在ON条件中,并且使用`d.word_id IN ( ... )`。但是,在MyBatis中,如果`item`是一个列表,那么在ON条件中我们同样可以使用`d.word_id IN <foreach>...`,但这里我们只关注SQL结构,占位符保持不变。 另外,原始SQL中GROUP BY的目的是去重,因为可能有多条记录对应同一个wordPurContCode和wordClassCode。所以我们可以保留GROUP BY。 但是,我们还需要注意:如果a表中的一条记录,通过连接后,在d表中对应多条记录(同一个word_class_code只出现一次,但不同的word_class_code会出现多次),那么GROUP BY会将这些记录合并成一条。所以,这样分组是合理的。 修改后的SQL: 我们调整了d表的连接条件,将word_id的过滤放在ON子句中。同时,为了确保结果正确,我们将所有在SELECT中出现的非聚合字段都加入到GROUP BY中(或者确保它们与分组字段有函数依赖)。这里,由于word_pur_cont和word_pur_cont_code一一对应,word_class和word_class_code一一对应,所以我们可以只按code分组,但为了严谨,也可以将word_pur_cont和word_class加入分组(这样可能会使分组更细,但不会改变结果,因为一一对应)。 但注意:如果同一个word_class_code对应多个word_class(这不应该发生,因为word_class_code是主键?或者有唯一性约束),那么按code分组后,同一个code可能会对应多个word_class,这样就会有问题。所以,我们假设word_class_code和word_class是一一对应的,那么我们可以只按code分组。 修改后的SQL如下: ```sql SELECT a.word_pur_cont_code AS wordPurContCode, a.word_pur_cont AS wordPurCont, d.word_class_code AS wordClassCode, d.word_class AS wordClass FROM crm_ai_word_pur_config a LEFT JOIN crm_ai_word_touch_config b ON a.touch_cus_level_code = b.touch_cus_level_code LEFT JOIN crm_ai_word_config c ON c.id = b.word_id LEFT JOIN crm_ai_word_word_class_config d ON c.id = d.word_id AND d.word_id IN #{item} GROUP BY a.word_pur_cont_code, d.word_class_code, a.word_pur_cont, d.word_class ``` 注意:这里我们将d.word_id的条件从WHERE移到了d表的ON条件中,这样即使d表的记录不满足条件(即不在列表中),a表的记录仍然会被保留(但d表的字段为NULL)。但是,由于我们使用了GROUP BY,并且d.word_class_code可能为NULL,那么分组时NULL会被视为一组。这样,我们就得到了每个a.word_pur_cont_code和d.word_class_code(可能为NULL)的组合。 但是,原始需求可能是:只保留d.word_id在列表中的记录,即不要那些d表为NULL的记录。那么我们就需要在WHERE条件中过滤掉d.word_class_code为NULL的记录?或者使用INNER JOIN?但是,原始SQL中使用了WHERE条件`d.word_id in #{item}`,这就意味着它只保留d表中有匹配的记录。所以,如果我们希望保持和原始SQL相同的过滤效果(即只保留d表匹配的记录),那么我们可以将d表的连接改为INNER JOIN,或者保留WHERE条件。但这里,我们根据要求将条件移动到ON中后,没有在WHERE中过滤,所以结果会包含d表为NULL的记录(如果不需要,则添加WHERE条件)。 因此,我们需要明确需求:是否要包含d表没有匹配的记录? 根据原始SQL,因为使用了`d.word_id in #{item}`,所以它只返回d表匹配的记录。所以,我们可以将d表的连接改为INNER JOIN,或者使用WHERE条件过滤掉NULL。 方案2:改为INNER JOIN(这样就不需要LEFT JOIN了,因为我们需要d表匹配): ```sql SELECT a.word_pur_cont_code AS wordPurContCode, a.word_pur_cont AS wordPurCont, d.word_class_code AS wordClassCode, d.word_class AS wordClass FROM crm_ai_word_pur_config a JOIN crm_ai_word_touch_config b ON a.touch_cus_level_code = b.touch_cus_level_code JOIN crm_ai_word_config c ON c.id = b.word_id JOIN crm_ai_word_word_class_config d ON c.id = d.word_id WHERE d.word_id IN #{item} GROUP BY a.word_pur_cont_code, d.word_class_code, a.word_pur_cont, d.word_class ``` 注意:这里我们使用了INNER JOIN(可以简写为JOIN),这样只有匹配的记录才会被选中。然后,我们在WHERE条件中过滤d.word_id。这样写逻辑清晰,且效率可能更高。 那么,原始SQL中的GROUP BY是否必要?如果同一个a.word_pur_cont_code和d.word_class_code组合在结果中出现多次,那么GROUP BY可以去除重复。所以,如果存在重复,我们就需要GROUP BY。 但是,如果数据本身没有重复(比如每个wordPurContCode和wordClassCode组合只出现一次),那么GROUP BY就没有必要。然而,由于我们通过多个表连接,可能会产生重复,所以保留GROUP BY是安全的。 另外,我们也可以考虑使用DISTINCT,但GROUP BY通常更灵活,且可以在分组的同时使用聚合函数(虽然这里没有使用)。 综上所述,我推荐使用方案2,因为原始意图是只保留d表匹配的记录,所以使用INNER JOIN更合适。 但是,我们还需要注意:原始SQL中使用了LEFT JOIN,而改为INNER JOIN后,如果a表中的记录在b、c、d表中没有匹配,那么该记录就不会出现。而原始SQL由于使用了LEFT JOIN,所以即使b、c、d表没有匹配,a表的记录也会出现(但d.word_id为NULL,然后被WHERE条件过滤掉)。所以,原始SQL和方案2的结果是一样的:都只保留d表匹配的记录。 因此,优化后的SQL(方案2)如下: ```sql SELECT a.word_pur_cont_code AS wordPurContCode, a.word_pur_cont AS wordPurCont, d.word_class_code AS wordClassCode, d.word_class AS wordClass FROM crm_ai_word_pur_config a INNER JOIN crm_ai_word_touch_config b ON a.touch_cus_level_code = b.touch_cus_level_code INNER JOIN crm_ai_word_config c ON c.id = b.word_id INNER JOIN crm_ai_word_word_class_config d ON c.id = d.word_id WHERE d.word_id IN #{item} GROUP BY a.word_pur_cont_code, d.word_class_code, a.word_pur_cont, d.word_class ``` 注意:在GROUP BY中,我们加入了a.word_pur_cont和d.word_class,因为它们在SELECT中出现了。虽然它们与分组字段一一对应,但为了符合SQL标准(避免某些数据库报错),我们将其加入GROUP BY。 如果数据库支持(如MySQL)并且我们确定word_pur_cont和word_class分别与word_pur_cont_code和word_class_code一一对应,那么可以只按code分组: ```sql GROUP BY a.word_pur_cont_code, d.word_class_code ``` 这样写更简洁,但在其他数据库(如PostgreSQL)中可能会报错。所以,具体取决于所使用的数据库。 最后,我们还需要考虑性能。如果表很大,我们可以在连接字段上建立索引,例如在b表的touch_cus_level_code字段、c表的id字段、d表的word_id字段上建立索引。 由于我们不知道具体的数据量和索引情况,所以无法给出更具体的优化,但上述SQL在逻辑上是正确的,并且去除了LEFT JOIN可能带来的歧义。 因此,最终优化后的SQL如下(使用标准GROUP BY,包含所有非聚合字段):</think>为了优化您的 SQL 查询并确保结果准确性,我分析了几个关键问题并进行了以下改进: ### 主要问题分析 1. **GROUP BY 不完整**:SELECT 中的字段 `a.word_pur_cont` 和 `d.word_class` 未包含在 GROUP BY 子句中 2. **连接条件冗余**:通过 `d.word_id` 过滤时,`c.id = d.word_id` 已确保关联 3. **左连接失效**:WHERE 子句中的 `d.word_id` 条件使左连接退化为内连接 4. **潜在歧义**:`d.word_class` 在 GROUP BY 中未明确处理 ### 优化后的 SQL 代码 ```sql SELECT a.word_pur_cont_code AS wordPurContCode, MAX(a.word_pur_cont) AS wordPurCont, -- 使用聚合函数确保唯一性 d.word_class_code AS wordClassCode, MAX(d.word_class) AS wordClass -- 使用聚合函数确保唯一性 FROM crm_ai_word_pur_config a LEFT JOIN crm_ai_word_touch_config b ON a.touch_cus_level_code = b.touch_cus_level_code LEFT JOIN crm_ai_word_config c ON c.id = b.word_id LEFT JOIN crm_ai_word_word_class_config d ON c.id = d.word_id AND d.word_id IN #{item} -- 移到JOIN条件保持左连接特性 GROUP BY a.word_pur_cont_code, d.word_class_code; ``` ### 关键优化点说明: 1. **完整的 GROUP BY 处理** - 所有非聚合字段 (`wordPurContCode`, `wordClassCode`) 都包含在 GROUP BY 中 - 对需要展示的非分组字段 (`wordPurCont`, `word_class`) 使用 `MAX()` 聚合函数 ```sql MAX(a.word_pur_cont) AS wordPurCont MAX(d.word_class) AS wordClass ``` 2. **保持左连接特性** 将过滤条件从 WHERE 移到 JOIN 条件: ```sql LEFT JOIN crm_ai_word_word_class_config d ON c.id = d.word_id AND d.word_id IN #{item} -- 关键调整 ``` 3. **移除冗余字段** 删除 `d.word_id` 的直接引用(已通过 JOIN 条件处理) 4. **明确分组逻辑** 使用更清晰的分组字段定义: ```sql GROUP BY a.word_pur_cont_code, d.word_class_code ``` ### 优化效果: 1. 确保每个 `wordPurContCode + wordClassCode` 组合返回唯一记录 2. 保留左连接特性:即使 `d` 表无匹配记录,仍会返回 `a` 表数据 3. 消除字段歧义,符合 SQL 标准 4. 提升查询效率(减少不必要的字段处理) ### 执行计划建议: 在以下字段添加索引可显著提升性能: ```sql CREATE INDEX idx_touch_level ON crm_ai_word_touch_config(touch_cus_level_code); CREATE INDEX idx_word_id ON crm_ai_word_word_class_config(word_id); CREATE INDEX idx_word_pur ON crm_ai_word_pur_config(word_pur_cont_code); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值