[英]SQL query to find count of multiple occurrences of column values from a table?
[英]SQL Server query - count occurrences of values in a column with specific criteria on multiple tables
我試圖從生成的SQL Server,這表明計數或出現的數量在三個不同的表報告Account_id
在從賬表Account_entries
和Users
從三個表不同的標准表。
表#1: 帳戶
ID ACCOUNT_TYPE
-------------------------
354857 Customer
354858 Agent
354859 Fee
354860 Customer
354861 Customer
354862 Agent
354863 Cashier
表#2: ACCOUNT_ENTRIES
ID ACCOUNT_ID narrative_TYPE CREATED_AT
-------------------------------------------------
35 Customer Fee 2018-01-02
36 Agent Fee 2018-11-02
37 Fee BalanceUpdate 2018-11-03
39 Customer BalanceUpdate 2018-11-03
表#3: 用戶
ID PHONE_NUMBER REGISTERED_BY (ACCOUNT_ID) CREATED_AT
------------------------------------------------------------
35 XXXXXXX 354858 2018-01-02
36 XXXXXXX 354877 2018-11-02
37 XXXXXXX 354858 2018-11-03
39 XXXXXXX 354858 2018-11-03
我已經嘗試過此SQL查詢,但無法獲得所需的輸出:
select
ac.id, count(ae.id) as counter1, count(u.registered_by) as counter2
from
db2inst1.accounts ac
left outer join
db2inst1.account_entries ae on ac.id = ae.account_id
left outer join
db2inst1.users u on ac.id = u.registered_by
where
ae.narrative_type = 'BalanceUpdate'
and ae.created_at > '2018-11-30'
and ae.created_at < '2019-01-01'
and u.created_at > '2018-11-30'
and u.created_at < '2019-01-01'
and ac.account_type = 'Agent'
group by
ac.id
我實際上想看到的是下面
ACCOUNT_ID COUNTER1 COUNTER2 COUNTER1+COUNTER2
----------------------------------------------------
354857 20 2 22
354858 24 23 47
354859 26 11 37
354860 27 23 60
其中計數器1計算account_id
中account_entries
的出現次數,計數器2在users
表上(注冊者)
請幫助
我認為獲取所需方法的快捷方式是使用count(distinct)
。 您還需要將過濾條件移到on
子句中,因此不必不必要地將行過濾掉:
select ac.id, count(distinct ae.id) as counter1,
count(distinct u.registered_by) as counter2
from db2inst1.accounts ac left outer join
db2inst1.account_entries ae
on ac.id = ae.account_id and
ae.narrative_type = 'BalanceUpdate' and
ae.created_at > '2018-11-30' and
ae.created_at < '2019-01-01' left outer join
db2inst1.users u
on ac.id = u.registered_by and
u.created_at > '2018-11-30' and
u.created_at < '2019-01-01'
where ac.account_type = 'Agent'
group by ac.id;
我在SELECT查詢中看到了幾個潛在的問題(盡管嘗試非常可靠,但是很好的開始!)
LEFT JOIN
,然后在WHERE
子句中,對LEFT JOIN
的表中的列進行過濾幾乎可以將其轉換為INNER JOIN
。 假設account_id
“ 2”在account_entries
表中沒有記錄,請考慮左連接的以下結果:
SELECT * FROM accounts A LEFT JOIN account_entries B ON A.id = B.account_id
|-- accounts table --| |----------- account_entries table ---------|
id account_type id account_id narrative_type created_at
---------------------------------------------------------------------
1 Agent 101 1 Fee 2018-12-01
1 Agent 102 1 BalanceUpdate 2018-12-02
2 Customer NULL NULL NULL NULL
3 Agent 103 3 Fee 2018-12-01
在這種情況下,如果將查詢添加到WHERE narrative_type = 'BalanceUpdate'
,則將對每個記錄進行評估,並且由於NULL不等於'BalanceUpdate',它將過濾出account_id
“ 2”。 這模仿了INNER JOIN
的行為
為了解決這個問題,您可以將過濾器移到ON A.id = B.account_id AND B.narrative_type = 'BalanceUpdate'
的ON
子句中,而不是WHERE
子句中(例如, ON A.id = B.account_id AND B.narrative_type = 'BalanceUpdate'
)
在某些情況下,將其保留在WHERE
子句中,但是使用ISNULL
可以有所幫助,但是我認為在這種特定用例中這沒有任何意義。
例如,如果您具有以下account_entries:
id account_id narrative_type created_at
--------------------------------------------
101 1 Fee 2018-12-01
102 1 BalanceUpdate 2018-12-02
103 3 Fee 2018-12-01
這些用戶:
id phone_number registered_by created_at
---------------------------------------------
1001 XXXXX 1 2018-12-01
1002 XXXXX 1 2018-12-01
1003 XXXXX 2 2018-12-01
將它們連接在一起時,除了帳戶ID之外,它們之間沒有任何其他關系,必須將每個帳戶條目與每個與該帳戶ID相匹配的用戶進行匹配。 然后,您將得到以下結果:
account_id account_entry_id user_id
--------------------------------------------
1 101 1001
1 101 1002
1 102 1001
1 102 1002
2 NULL 1003
3 103 NULL
為了解決這個問題,您可以使用COUNT(DISTINCT ...)
,然后將其忽略。 這可能很好,但是也許在更大的數據集上可能會導致性能問題。
我希望在加入數據之前先進行匯總。 這可以作為簡單的子查詢完成,也可以使用通用表表達式(“ CTE”)非常干凈地完成
這是我處理查詢的方法:
WITH cte_account_entries AS
(
SELECT
account_id,
COUNT(*) account_entries
FROM account_entries
WHERE narrative_type = 'BalanceUpdate'
AND CAST(created_at AS DATE) BETWEEN '2018-12-01' AND '2018-12-31'
GROUP BY
account_id
),
cte_users AS
(
SELECT
registered_by,
COUNT(*) users
FROM users
WHERE CAST(created_at AS DATE) BETWEEN '2018-12-01' AND '2018-12-31'
GROUP BY
registered_by
)
SELECT
A.id account_id,
A.account_type,
ISNULL(B.account_entries, 0) counter1,
ISNULL(C.users, 0) counter2,
ISNULL(B.account_entries, 0) + ISNULL(C.users, 0) [counter1+counter2]
FROM accounts A
LEFT JOIN cte_account_entries B
ON A.id = B.account_id
LEFT JOIN cte_users C
ON A.id = C.registered_by
WHERE A.account_type = 'Agent'
cte_account_entries
是第一個公用表表達式,該表達式按帳戶計算帳戶條目的數量,從而實現問題中指出的過濾器。 注意如果列中同時包含日期和時間,我會進行CAST(... AS DATE)
。
cte_users
相似,但是與users表有關。
最后,所有這些都合並到最終的SELECT
語句中,篩選出僅“ Agent”帳戶類型,並且LEFT JOIN
到CTE,每個帳戶僅產生一個記錄,因此不會有笛卡爾積。
ISNULL
在這里也非常有幫助。 例如,如果沒有一個帳戶的帳戶條目,但是有12個用戶,那么您可能最終嘗試將它們加在一起,例如NULL + 12,這將產生NULL。 ISNULL會將那個NULL轉換為0,所以您得到0 + 12。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.