我正在尝试从SQL Server上的三个不同表生成报告,该报告显示Account_id
和Account_entries
表中的account表中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
表上(由其注册)
请帮助
答案 0 :(得分:0)
我认为获取所需方法的快捷方式是使用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;
答案 1 :(得分:0)
我在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
子句中,而不要移到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
与之类似,但与用户表有关。
最后,它们全部合并到最后的SELECT
语句中,过滤到仅“代理”帐户类型,并且LEFT JOIN
s加入到CTE,每个帐户仅产生一条记录,因此不会有笛卡尔积。
ISNULL
在这里也非常有帮助。例如,如果没有一个帐户的帐户条目,但是有12个用户,那么您可能最终尝试将它们加在一起,例如NULL + 12,这将产生NULL。 ISNULL会将那个NULL转换为0,所以您得到0 + 12。