Convert MySQL view to Postgres
我继承了将生产MySQL数据库转换为Postgres的需求。使用简单的SQL语句进行表/函数创建(使用Navicat生成半自动转换)已基本解决了这一问题,但是现在我遇到了转换有点复杂的视图的问题。
研究表明,这可能是由于两个DB处理子查询(WHERE语句)的方式不同,也许只是语法上的差异。这里的业务逻辑是未知的,因为代码库是从另一个开发人员继承的。
运行以下命令(使用Laravel迁移/ PHP脚本):
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 | SELECT parent.is_owner AS is_owner, parent.brand AS first_name, parent.id AS id, (SELECT count(c.id) FROM campaigns c WHERE(( (c.user_id = parent.id) OR (c.user_id = child.id) ) AND (c.campaign_status_id = 4) )) AS current_campaigns, (SELECT count(c.id) FROM campaigns c WHERE (( (c.user_id = parent.id) OR (c.user_id = child.id) ) AND (c.campaign_status_id = 5) )) AS past_campaigns, (SELECT count(c.id) FROM campaigns c WHERE (( (c.user_id = parent.id) OR (c.user_id = child.id)) AND (c.campaign_status_id = 2) )) AS pending_campaigns, (SELECT count(c.id) FROM campaigns c WHERE (( (c.user_id = parent.id) OR (c.user_id = child.id) ) AND (c.invoice_status = '1') )) AS past_invoices FROM ((users parent LEFT JOIN campaigns mc ON ((parent.id = mc.user_id))) LEFT JOIN users child ON ((child.parent_owner = parent.id) )) WHERE ( (parent.is_owner = 1) OR (child.is_retailer = 1) ) GROUP BY parent.id ORDER BY parent.brand |
...触发错误
1 2 |
有人可以建议如何设置其格式,以便Postgres运行子查询吗?
顺便说一句,在Laravel迁移脚本中使用的PHP代码是:
...
1 2 3 4 5 6 7 8 9 10 11 12 | DB::unprepared("CREATE VIEW client AS select parent.is_owner AS is_owner,parent.brand AS first_name,parent.id AS id ,(select count(c.id) from campaigns c where (((c.user_id = parent.id) or (c.user_id = child.id)) and (c.campaign_status_id = 4))) AS current_campaigns ,(select count(c.id) from campaigns c where (((c.user_id = parent.id) or (c.user_id = child.id)) and (c.campaign_status_id = 5))) AS past_campaigns ,(select count(c.id) from campaigns c where (((c.user_id = parent.id) or (c.user_id = child.id)) and (c.campaign_status_id = 2))) AS pending_campaigns ,(select count(c.id) from campaigns c where (((c.user_id = parent.id) or (c.user_id = child.id)) and (c.invoice_status = '1'))) AS past_invoices from ((users parent left join campaigns mc on((parent.id = mc.user_id))) left join users child on((child.parent_owner = parent.id))) where ((parent.is_owner = 1) or (child.is_retailer = 1)) group by parent.id order by parent.brand;"); |
已更新,已修复:
精彩。所有人的意见都很好。
@patrick和@ErwinBrandstetter的解决方案均有效。我会喜欢Patrick \\'s here,因为我的角色是转换系统"原样"。将来可能会有重构的空间,但是在此阶段,我觉得与别人的风管带解决方案混为一谈(或改进)是有风险的(即,代码库在某些地方似乎过于复杂,没有文档记录的迹象) ,而我不愿意在没有更多有关业务逻辑的背景信息的情况下四处寻找或尝试进行核心改进)。我怀疑该模型的某些部分可能仍需要进行大修,因此[sic] -fix在这里受到青睐。
我怀疑某些单击跳动可能会产生原始查询...试图使原始开发人员受益匪浅,并假设存在一定的业务压力,要求快速(如摩丝)周转。复杂的SQL不是我的强项,但我很高兴我的直觉是正确的,查询从一开始就是不必要的复杂。也许视图是计划外的,并非一开始就设计好了。明智与否,我可能已经尝试过使用基于ORM的方法来解决问题。
我上分钟正在这个项目上,正在运行清理工作以重新启动(原始开发者是"放手"),因此我正在使用一个主要是未公开文档的,功能未知的代码库。照常运行伞兵。幸运的是,这个观点问题似乎是难题的最后一部分。谢谢:-)
哦,我的,哦,我的。毫无疑问,开发人员的右手无名指,因为该声明有不少于74个括号。这是仅用8个括号和14行而不是54行的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | SELECT parent.is_owner AS is_owner, parent.brand AS first_name, parent.id AS id, sum(CASE WHEN c.campaign_status_id = 4 THEN 1 ElSE 0 END) AS current_campaigns, sum(CASE WHEN c.campaign_status_id = 5 THEN 1 ElSE 0 END) AS past_campaigns, sum(CASE WHEN c.campaign_status_id = 2 THEN 1 ElSE 0 END) AS pending_campaigns, sum(CASE WHEN c.invoice_status = '1' THEN 1 ElSE 0 END) AS past_invoices, FROM users parent LEFT JOIN users child ON child.parent_owner = parent.id LEFT JOIN campaigns c ON c.user_id = parent.id OR c.user_id = child.id WHERE parent.is_owner = 1 OR child.is_retailer = 1 GROUP BY parent.is_owner, parent.brand, parent.id ORDER BY parent.brand; |
没有子选项意味着此代码将大大加快启动速度。就像沃尔夫在其评论中提到的那样,选择列表中未包括在聚合函数中的每一列都必须出现在
通过使用
正如沃尔夫(Wolph)在此答案下方的评论中指出的那样,子句
也可以更简洁地写为
这可能比
该问题中缺少解释,但可能的用例是:
计算每个用户"拥有"的广告系列数量。用户可以有子用户,其活动应添加到父用户。
除了@Patrick在他的演示中含糊不清的语法外,该查询也是模棱两可的(并且可能完全是错误的):
如果我们可以假设:
-
引用完整性:子用户仅引用受
FOREIGN KEY 约束强制执行的现有父用户。 -
父级和子级可靠地标记为
is_owner /is_retailer ,这些列仅包含值0 和1 。参见下文。
此查询将完成这项工作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | SELECT CASE WHEN u.is_retailer = 1 THEN u.parent_owner WHEN u.is_owner = 1 THEN u.id END AS user_id , max(u.is_owner) AS is_owner , max(u.brand) FILTER (WHERE u.is_owner = 1) AS first_name , count(*) FILTER (WHERE c.campaign_status_id = 4) AS current_campaigns , count(*) FILTER (WHERE c.campaign_status_id = 5) AS past_campaigns , count(*) FILTER (WHERE c.campaign_status_id = 2) AS pending_campaigns , count(*) FILTER (WHERE c.invoice_status = '1') AS past_invoices FROM users u LEFT JOIN campaigns c ON u.id = c.user_id AND (c.campaign_status_id IN (4, 5, 2) OR c.invoice_status = '1') -- exclude irrelevant early WHERE 1 IN (u.is_owner, u.is_retailer) -- parent & child, may be redundant GROUP BY 1 ORDER BY 2; |
应该很快。确保有适合大表的索引。
如果没有其他选项,则此条件是多余的:
我使用您的数据模型"按原样",但是您可能应该只具有
-
is_child :对于儿童,true ,对于父母,false 。 -
is_owner :所有者为true ,零售商为false 。
使用Postgres 9.4中引入的新的汇总
- 如何简化此游戏统计信息查询?