需求引入
你有一個銷售單表A 和一個銷售單詳情表B 和一個收付款記錄表C
A---->B 一對多 A---->C一對多
如果一個銷售單有兩個詳情,三條收款記錄
對一個銷售單 我們想查詢出一個結果集 有銷售單的id、詳情總額、銷售單的收款總額
如果你select A join B on(B和A的外鍵) 查詢出2條記錄結果集
如果你select A join C on(C和A的外鍵) 查詢出3條記錄結果集
那你 如果你select A join B on(B和A的外鍵) join C on(C和A的外鍵) 查詢出2*3=6條記錄結果集 然后在sum
如果select語句中有聚合函數(sum count )很可能導致查詢的結果不符合需求的要求。因為詳情總額只需要對兩條記錄進行sum 收款進行需要三條 而sum之前我們查出來了六條
解決辦法
1. 用兩條sql查詢 然后通過java對結果集進行合並。
缺點:只能對一些簡單的需求,如果多個銷售單,還要做分頁 而且有 付款記錄和詳情的搜索條件 JAVA很難做
2. 用兩條sql查詢 然后就是用sql的垂直合並了
select * from (sql1) join (sql2) on(sql1.銷售單的主鍵=sql2.銷售單的主鍵) 就是把兩條sql當成兩個子查詢
優點:解決了用java操作List進行合並,做不了分頁搜索的問題。因為我們用的一個SQL 無論需求怎么搜索分頁,都可以搞定
工作中做某張報表的時候發現的。sql語句含有多個一對多關系的時候 注意影響聚合函數的結果。所以采用了先拆分sql,然后根據需求垂直合並。
設計表結構的時,如何避免出現過長的SQL查詢
冗余一些字段,比如你需要對收付款記錄的某一個字段(比如金額)做sum 把這個字段冗余到一張額外的表里面。
方法一:每次進行收付款的時候加減這個字段。這樣做報表的時候就不必再關聯收付款記錄表做關聯求sum了(這樣的話 有哪個接口忘記更新收付款就完蛋了 很難排查)
方法二 :每次進行收付款的時候 從新進行一遍收付款的sum放進銷售單里面,比較消耗性能。引入消息隊列 異步的操作這種接口。
方法三 :收付款的時候多開一個線程,去更新sum字段
當我們把計算邏輯(比如錢的增量)寫在SQL里面的時候 可以不用樂觀鎖,因為SQL會讀到其他已經提交的事物,但是如果這個錢是先計算好,然后直接update的 需要用樂觀鎖