Become a Mochi that writes SQL!
3
Fumi 2022/05
review
加油請求(refueling_request)上會紀錄這次請求的油量(quantity)和對應到的加油站 ID(gas_station_id)。
今天想撈取油量大於 55 的加油請求之加油請求 ID、加油請求油量、加油站名稱(gas_stations.name),並以加油請求 ID 由小至大排序
今天想撈取油量大於 55 的加油請求之加油請求 ID、加油請求油量、加油站名稱(gas_stations.name),並以加油請求 ID 由小至大排序
SELECT:加油請求 ID、加油請求油量、加油站名稱
FROM:加油請求
JOIN:加油站
ORDER:加油請求 ID 由小至大
今天想撈取油量大於 55 的加油請求之加油請求 ID、加油請求油量、加油站名稱(gas_stations.name),並以加油請求 ID 由小至大排序
SELECT
refueling_requests.id
refueling_requests.quantity
gas_stations.name
FROM refueling_requests
JOIN gas_stations
ON refueling_requests.gas_station_id = gas_stations.id
WHERE refueling_requests.quantity > 55
ORDER BY refueling_requests.id
有兩間出色的加油站(gas_stations),名稱(name)分別是 Happy Mochi Station 與 Unhappy Mochi Station QQ。想獲取這兩間加油站的總發油量,結果按照總油量由大至小排序,希望撈取的結果如下圖所示:
gas_station_name | total_quantity |
---|---|
Happy Mochi Station | xxx(某個數字) |
Unhappy Mochi Station QQ | xxx(某個數字) |
總加油量可以透過加油站的加油請求(refueling_requests)上的油量(quantity)加總所得
想獲取 Happy Mochi Station 與 Unhappy Mochi Station QQ 這兩間加油站的總發油量,結果按照總油量由大至小排序
SELECT:加油站名稱、加油站之加油請求油量加總
FROM:加油請求
JOIN:加油站
WHERE:名稱是 Happy Mochi Station 或 Unhappy Mochi Station
GROUP BY:加油站名稱
ORDER:加油站之加油請求油量加總 由大至小
想獲取 Happy Mochi Station 與 Unhappy Mochi Station QQ 這兩間加油站的總發油量,結果按照總油量由大至小排序
SELECT
gas_stations.name AS gas_station_name,
SUM(refueling_requests.quantity) AS total_quantity
FROM gas_stations
JOIN refueling_requests
ON refueling_requests.gas_station_id = gas_stations.id
WHERE gas_stations.name
IN ('Happy Mochi Station', 'Unhappy Mochi Station QQ')
GROUP BY gas_stations.name
ORDER BY total_quantity DESC
Autoplus Schema
手機號碼
通路 ID
顯示名稱
是否為會員
mobile
distributor_id
display_name
registered
訂單類型
交易類型
訂單金額
收入金額
已退款金額
type
transaction_type
amount
income_amount
refunded_amount
加油
停車
路邊停車
連鎖店
PayTaipei
月租
RefuelingOrder
ParkingOrder
OnStreetParkingOrder
InvoiceOrder
PayTaipeiOrder
LeaseOrder
訂單類型 type
加油
停車
路邊停車
PayTaipei
月租
refueling
parking
on_street_parking
pay_taipei
lease
交易類型 transaction_type
汽車美容
保險
洗車
充電
租車
維修保養
加油代收
停車場代收
悠遊付退款
其他代收
vehicle_maintenance
insurance
wash_car
charging
rental_car
vehicle_repair
refueling_payment
parking_payment
easy_wallet_refund
miscellaneous
交易類型 transaction_type
找一筆退款金額為 20,
交易類型為加油站代收的連鎖店訂單,
撈其 ID、訂單類型、交易類型、收入金額、退款金額
找一筆退款金額為 20,
交易類型為加油站代收的連鎖店訂單,
撈其 ID、訂單類型、交易類型、收入金額、退款金額
SELECT: ID、訂單類型、交易類型、收入金額、退款金額
FROM:orders
WHERE:退款金額為 20 且 交易類型為加油站代收
LIMIT:1
找一筆退款金額為 20,
交易類型為加油站代收的連鎖店訂單,
撈其 ID、訂單類型、交易類型、收入金額、退款金額
SELECT id, type, transaction_type, income_amount,
refunded_amount
FROM orders
WHERE refunded_amount = 20
AND transaction_type = 'refueling_payment'
LIMIT 1
車牌
使用者 ID
通路 ID
錢包 ID
付款狀態
退款狀態
plate_number
user_id
distributor_id
wallet_id
payment_state
refund_state
未付款
已付款
付款失敗
pending
paid
unpaid
付款狀態 payment_state
沒退款
退款(包含部分退款)
no_refund
refunded
退款狀態 refund_state
找一筆通路 ID 為 ec31ddb3-652b-47cd-b7bd-892a62eca2f3 的部分退款訂單,
撈其 ID、訂單類型、交易類型、收入金額、退款金額
找一筆通路 ID 為 ec31ddb3-652b-47cd-b7bd-892a62eca2f3 的部分退款訂單,
撈其 ID、訂單類型、交易類型、收入金額、退款金額
SELECT: ID、訂單類型、交易類型、收入金額、退款金額
FROM:orders
WHERE:通路 ID 為 ec31ddb3-652b-47cd-b7bd-892a62eca2f3 且 部分退款(???)
LIMIT:1
找一筆通路 ID 為 ec31ddb3-652b-47cd-b7bd-892a62eca2f3 的部分退款訂單,
撈其 ID、訂單類型、交易類型、收入金額、退款金額
SELECT: ID、訂單類型、交易類型、收入金額、退款金額
FROM:orders
WHERE:通路 ID 為 ec31ddb3-652b-47cd-b7bd-892a62eca2f3 且 部分退款(???)
LIMIT:1
訂單金額 != 退款金額的退款訂單
找一筆通路 ID 為 ec31ddb3-652b-47cd-b7bd-892a62eca2f3 的部分退款訂單,
撈其 ID、訂單類型、交易類型、收入金額、退款金額
SELECT id, type, transaction_type, income_amount,
refunded_amount
FROM orders
WHERE refunded_amount != amount
AND refund_state = 'refunded'
AND distributor_id = 'ec31ddb3-652b-47cd-b7bd-892a62eca2f3'
LIMIT 1
交易來源 ID
交易來源類型
次要交易來源 ID
次要交易類型 ID
付款時間
main_item_source_id
main_item_source_type
sub_item_source_id
sub_item_source_type
paid_at
停車紀錄
加油請求
路停交易
連鎖店請求
PayTaipei 交易
月租合約
ParkingRecord
RefuelingRequest
OnStreetParkingBill
Invoice
PayTaipeiTransaction
LeaseContract
交易來源類型 main_item_source_type
停車票
訪客車辨紀錄
ParkingTicket
ParkingPrevalidation
次要來源類型 sub_item_source_type
類型
狀態
訂單 ID
錢包 ID
原刷卡紀錄 ID
type
state
order_id
wallet_id
refund_from_payment_id
付款紀錄
LINE 綁卡紀錄
綁卡紀錄
退款紀錄
(NULL)
LinePayApprovedPayPayment
ValidationPayment
RefundPayment
type
未經過付款流程
初始化付款流程
完成預授權
授權+請款完成
付款失敗
取消授權
pending
initialized
authorized
paid
unpaid
canceled
state
一般付款流程
pending
預授權
pending
authorized
paid
paid
請款
授權
授權請款
canceled
取消授權
一次性付款
pending
初始化
pending
initialized
paid
確認付款成功
付款失敗
unpaid
???
ID | type | refund_from_payment_id |
---|---|---|
3de03676-5a10-4f6b-95dd-2077daabfc7e | ||
9b1234cc-a42e-484b-ae17-8a32fc51c2ae | RefundPayment | 3de03676-5a10-4f6b-95dd-2077daabfc7e |
金額
收入金額
付款時間
amount
income
paid_at
付款成功才會有收入金額
找 UTC 時間 2022-05-01 08:00:00 到 2022-05-02 08:00:00 之間付款成功的一般付款紀錄(不是綁定紀錄也不是退款紀錄),
撈這些紀錄的收入金額總和
找 UTC 時間 2022-05-01 08:00:00 到 2022-05-02 08:00:00 之間付款成功的一般付款紀錄(不是綁定紀錄也不是退款紀錄),
撈這些紀錄的收入金額總和
SELECT: 收入金額總和
FROM:payments
WHERE:時間為 5/1 且 是一般付款紀錄 且 付款成功
找 UTC 時間 2022-05-01 08:00:00 到 2022-05-02 08:00:00 之間付款成功的一般付款紀錄(不是綁定紀錄也不是退款紀錄),
撈這些紀錄的收入金額總和
SELECT SUM(amount)
FROM payments
WHERE payments.state = 'paid'
AND payments.type IS NULL
AND paid_at >= '2022-05-01 08:00:00'
AND paid_at <= '2022-05-02 08:00:00'
找 UTC 時間 2022-05-01 08:00:00 到 2022-05-02 08:00:00 之間付款成功的一般付款紀錄(不是綁定紀錄也不是退款紀錄),
撈這些紀錄的收入金額總和
SELECT SUM(amount)
FROM payments
WHERE payments.state = 'paid'
AND payments.type IS NULL
AND paid_at
BETWEEN '2022-05-01 08:00:00'
AND '2022-05-02 08:00:00'
場站 ID
場站類型
車牌
請款金額
撥款金額
store_id
store_type
plate_number
request_amount appropriation_amount
停車場
加油站
Parkinglot
GasStation
store_type
停車狀態
撥款狀態
進場時間
出場時間
離場時間
撥款請款成功時間
parking_state
payment_state
checked_in_at
checked_out_at
left_at
paid_at
停車中
正常離場
被業者取消
parking
left
canceled
parking_state
還沒算帳
認列撥款
拒絕撥款
pending
paid
unpaid
payment_state
以下 SQL 可以找到停車場名稱為「掃地僧內湖停車場」的停車場的一筆停車紀錄
SELECT parking_records.id,
parking_records.parking_state,
parking_records.payment_state
FROM parking_records
JOIN parkinglots
ON parkinglots.id = parking_records.store_id
AND parking_records.store_type = 'Parkinglot'
WHERE parkinglots.name = '掃地僧內湖停車場'
LIMIT 1
請計算 2022-05-01 08:00:00 之後進場的「掃地僧內湖停車場」的停車紀錄,有幾筆目前是已撥款的?
請計算 2022-05-01 08:00:00 之後進場的「掃地僧內湖停車場」的停車紀錄,有幾筆目前是已撥款的?
SELECT:資料筆數
FROM:停車紀錄
JOIN:停車場
WHERE:停車場名稱為掃地僧內湖停車場 且 進場時間在 2022-05-01 之後 且 撥款狀態是已撥款
請計算 2022-05-01 08:00:00 之後進場的「掃地僧內湖停車場」的停車紀錄,有幾筆目前是已撥款的?
SELECT count(1)
FROM parking_records
JOIN parkinglots
ON parkinglots.id = parking_records.store_id
AND parking_records.store_type = 'Parkinglot'
WHERE parkinglots.name = '掃地僧內湖停車場'
AND parking_records.checked_in_at >= '2022-05-01 08:00:00'
AND parking_records.payment_state = 'paid'
加油站 ID
車牌
油量
油種
請款金額
撥款金額
gas_station_id
plate_number
quantity
sku
request_amount appropriation_amount
92
95
98
柴油
gas_92
gas_95
gas_98
diesel
sku
狀態
撥款狀態
是否為自助加油
加油時間
撥款請款成功時間
state
payment_state
is_self_service
refueled_at
paid_at
init
waiting
accepted
rejected
canceled
expired
新建
等待使用者回應
接受
拒絕
取消
過期
狀態 state
self_service_waiting
self_service_pre_authorized
self_service_unlocked
self_service_picked
self_service_completed
self_service_captured
self_service_interrupted
等待授權
授權成功
油槍解鎖
已取油槍
加油完成
請款完成
發生錯誤
狀態 state
還沒算帳
認列撥款
拒絕撥款
pending
paid
unpaid
payment_state
有哪張表想多多了解呢?
Introduction of Query Plan
postgres 是怎麼執行我的 SQL 呢?
圖片來源 https://www.interdb.jp/pg/pgsql03.html
The structure of a query plan is a tree of plan nodes
圖片來源 https://www.interdb.jp/pg/pgsql03.html
EXPLAIN
SELECT *
FROM parkinglots
Seq Scan on parkinglots (cost=0.00..48225.97 rows=9897 width=1238)
EXPLAIN (FORMAT JSON)
SELECT *
FROM parkinglots
[
{
"Plan": {
"Node Type": "Seq Scan",
"Parallel Aware": false,
"Relation Name": "parkinglots",
"Alias": "parkinglots",
"Startup Cost": 0.00,
"Total Cost": 48225.97,
"Plan Rows": 9897,
"Plan Width": 1238
}
}
]
Node Type:node 類型
Relation Name:關聯名稱
Startup Cost:這個步驟開始前的花費成本
Total Cost:這個步驟結束後的花費成本
Plan Rows:node 估計會輸出多少列資料
Plan Width:估計每列資料的平均大小(單位是 byte)
Seq Scan on parkinglots (cost=0.00..48225.97 rows=9897 width=1238)
EXPLAIN
SELECT *
FROM parkinglots
WHERE created_at > '2022-05-01 08:00:00'
AND cooperation_state = 'in_cooperated'
ORDER BY serial_number
Sort (cost=48275.47..48275.47 rows=1 width=1238)
Sort Key: serial_number
-> Seq Scan on parkinglots (cost=0.00..48275.46 rows=1 width=1238)
Filter: ((created_at > '2022-05-01 08:00:00+00'::timestamp with time zone) AND ((cooperation_state)::text = 'in_cooperated'::text))
EXPLAIN
SELECT *
FROM parking_records
WHERE checked_in_at > '2022-05-01 08:00:00'
AND plate_number = 'AAA-1111'
Index Scan using index_parking_records_on_plate_number on parking_records (cost=0.56..1656.96 rows=3 width=630)
Index Cond: ((plate_number)::text = 'AAA-1111'::text)
Filter: (checked_in_at > '2022-05-01 08:00:00'::timestamp without time zone)
EXPLAIN
SELECT *
FROM parkinglots
Seq Scan on parkinglots (cost=0.00..48224.75 rows=9775 width=1243) (actual time=0.010..47.579 rows=9828 loops=1)
Planning Time: 1.377 ms
Execution Time: 48.217 ms
和 EXPLAIN 差異在於會實際執行該 Query
actual time:該節點實際執行時間
Planning Time:planning 所花的時間
Execution Time:執行所花的時間
Seq Scan on parkinglots (cost=0.00..48224.75 rows=9775 width=1243) (actual time=0.010..47.579 rows=9828 loops=1)
Planning Time: 1.377 ms
Execution Time: 48.217 ms
EXPLAIN ANALYSE
SELECT *
FROM parkinglots
WHERE created_at > '2022-05-01 08:00:00'
AND cooperation_state = 'in_cooperated'
ORDER BY serial_number
Sort (cost=48290.72..48290.72 rows=1 width=1243) (actual time=43.210..43.212 rows=0 loops=1)
Sort Key: serial_number
Sort Method: quicksort Memory: 25kB
-> Seq Scan on parkinglots (cost=0.00..48290.71 rows=1 width=1243) (actual time=43.187..43.188 rows=0 loops=1)
Filter: ((created_at > '2022-05-01 08:00:00+00'::timestamp with time zone) AND ((cooperation_state)::text = 'in_cooperated'::text))
Rows Removed by Filter: 9829
Planning Time: 3.399 ms
Execution Time: 43.412 ms
EXPLAIN ANALYSE
SELECT *
FROM parking_records
WHERE checked_in_at > '2022-05-01 08:00:00'
AND plate_number = 'AAA-1111'
Index Scan using index_parking_records_on_plate_number on parking_records (cost=0.56..1656.96 rows=3 width=630) (actual time=102.074..102.077 rows=1 loops=1)
Index Cond: ((plate_number)::text = 'AAA-1111'::text)
Filter: (checked_in_at > '2022-05-01 08:00:00'::timestamp without time zone)
Rows Removed by Filter: 73
Planning Time: 2.091 ms
Execution Time: 102.428 ms
大家用 3 小時成為了會寫 SQL 的麻吉
給自己一個鼓勵 👏