BigQuery 避免多个子查询
Posted
技术标签:
【中文标题】BigQuery 避免多个子查询【英文标题】:BigQuery avoiding multiple subqueries 【发布时间】:2018-06-16 02:14:04 【问题描述】:我们正在开发一个应用程序,它将请求存储在一个表中,并将响应存储在另一个表中(当然)。每个请求可以有多个响应,并且我们将请求 ID 存储在两个表中。
最初,我认为我们可以使用请求中的左连接 -> 响应来计算每个匹配条件的总数:
SELECT source, COUNT(*) as requests, COUNT(responses.request_id) as responses
FROM DATASET.requests
LEFT JOIN DATASET.responses ON requests.id = responses.request_id
WHERE source = "source1"
GROUP BY source
有 70 个请求符合 WHERE 条件,30 个响应符合这个条件。预期输出为:“source1, 70, 30”。 从那以后,我了解了更多关于 JOIN 行为的信息,而我们得到了“source1, 259, 207”。两边有重复的ID。
我能够得到我想要的结果的唯一方法是创建一个巨大的查询,以及多个在给定条件过滤的 ID 集中匹配的完整子查询。然后使用过滤后的 ID 集来真正提取我们的字段、统计信息等。
SELECT * FROM
(SELECT COUNT(*) as responses FROM DATASET.responses
WHERE id IN (SELECT id FROM DATASET.requests WHERE source =
"source1"))
,
(SELECT source, COUNT(*) as requests
FROM PUBDATA.requests
WHERE id IN (SELECT id FROM DATASET.requests WHERE source = "source1")
GROUP BY source)
这看起来很糟糕。我曾尝试使用 CTE 来收集我们想要的 ID 列表,并使用 WHERE id/request_id IN (cte.id) 但这显然是不可能的,除非我们在 cte 上加入,这再次产生错误和成倍的结果。
由于我们想在查询中添加额外的统计信息,这将需要更多的 WHERE 子句,我担心这个怪物会继续增长并且难以实现。
如果有更好的方法,请告诉我。谢谢!
编辑 - 请求的示例架构 请求
id (String), source (String), partner_ids (Integer array), user_agent (String), timestamp (Timestamp), ...
回复
request_id (String, from requests.id), partner_id (Integer), is_billed (boolean), price_charged (float, null if is_billed = false), response_categories (String array, not from requests), ...
挑战在于我们必须主要查询 Requests 表以获取与我们的条件匹配的 ID 值列表,然后查询每个表的统计信息(例如 counts、count where is_billed 等)以获取一份合并报告。我们可能还需要从每个表的条件中提取 ID 池(例如 where requests.source = 'source1' 和response.response_categories IN 'action')
【问题讨论】:
样本数据和期望的结果会有所帮助。 【参考方案1】:也许我误会了什么,你为什么不只计算每个并加入 id 呢?
WITH
sources
AS
( SELECT COUNT (*) source_cnt, id
FROM dataset.request
GROUP BY id),
responses
AS
( SELECT COUNT (*) AS response_cnt, id
FROM dataset.responses
GROUP BY id)
SELECT source_cnt, response_cnt, sources.id
FROM sources INNER JOIN responses ON sources.id = responses.id;
如果您想保留所有记录,可以将其修改为完全外部联接:
WITH
sources
AS
( SELECT COUNT (*) source_cnt, id
FROM dataset.request
GROUP BY id),
responses
AS
( SELECT COUNT (*) AS response_cnt, id
FROM dataset.responses
GROUP BY id)
SELECT COALESCE (sources.id, responses.id) AS id, source_cnt, response_cnt
FROM sources FULL OUTER JOIN responses ON sources.id = responses.id
【讨论】:
【参考方案2】:我认为你可以用union all
和group by
做你想做的事:
select source, sum(requests) as requests, sum(responses) as responses
from ((select source, count(*) as requests, 0 as response
from dataset.requests
group by source
) union all
(select source, 0 as requests, count(*) as responses
from dataset.responses
group by source
)
) rr
group by source;
这会对所有来源进行计算。
编辑:
对于修改后的版本,只需多加一个join
:
select source, sum(requests) as requests, sum(responses) as responses
from ((select source, count(*) as requests, 0 as response
from dataset.requests rq
group by rq.source
) union all
(select rq.source, 0 as requests, count(*) as responses
from dataset.responses r join
(select distinct rq.id
from dataset.requests rq
) rq
on r.id = rq.id
group by rq.source
)
) rr
group by source;
如果每个请求最多有一个响应,您可以将其缩短为:
select rq.source, count(*) as requests, count(r.id) as responses
from dataset.requests rq left join
dataset.responses r
on r.id = rq.id
group by rq.source
【讨论】:
谢谢,但我也不认为这会奏效。我已添加说明,即源字段仅存在于 requests 表中 - ID 列是它们之间唯一常见的交集。 更新给了我 (source1, 70, 207)。所以它仍然在乘以响应计数,应该是 30。 @EvanTestvoid 。 . .你真的应该用样本数据来澄清这个问题。您不是在寻找 响应数。您正在寻找有响应的请求数。这些是完全不同的计算。 这是不正确的。我们想要一个独立的请求和响应计数,它们具有预先过滤的 ID 集中的 ID 值,然后在任一表中按 ID 及其相关列值排序。再重复一遍,请求中有 70 个条目,响应中有 30 个条目。这是每个表的大小。【参考方案3】:老实说,我对您最终希望看到的内容感到有些困惑,而且我也不完全理解如果一个请求可以有多个响应,那么您如何有 70 个请求而只有 30 个响应。您的意思是某些请求可以有 0 个响应吗?还是您在计算不同的响应?
如果您希望计算请求总数以及与这些特定请求相关的响应总数,我相信对您的代码进行的这种轻微修改应该可以工作:
SELECT source, COUNT(DISTINCT id) as requests, COUNT(responses.request_id) as responses
FROM `dataset.requests` as requests
LEFT JOIN `dataset.responses` as responses ON requests.id = responses.request_id
WHERE source = "source1"
GROUP BY source
【讨论】:
以上是关于BigQuery 避免多个子查询的主要内容,如果未能解决你的问题,请参考以下文章