【问题标题】:How to fix subquery running too long?如何修复子查询运行时间过长?
【发布时间】:2020-01-14 12:55:09
【问题描述】:

我从FDIC's website 下载了 CSV 文件。

我使用银行名称“NAMEFULL”作为参数,然后汇总所有其他银行的存款“DEPSUMBR”银行所在的银行“MSABR”

我通过构建子查询来处理此代码,但查询花费的时间太长 - 我会让 Access 运行一个小时,但查询每次都会冻结。

SELECT max(NAMEFULL), sum(DEPSUMBR) AS Deposit FROM ALL_2018 WHERE MSABR IN (SELECT DISTINCT MSABR FROM ALL_2018 WHERE NAMEFULL = [Enter Bank Name] AND MSABR <> '0') ;

输出应显示共享 MSA 作为参数银行的其他银行的名称以及每个 MSA 中的存款总和。

下面是csv文件的截图


尝试显示市场份额排名前 5 的地区的 MSA:

RANK (*) Over (partition by tots.msabr, tots.namefull, order by percentmsashare desc) as Rank
(
SELECT  tots.msabr, tots.namefull, 100 * (tots.summsabra / tots.summsa) as percentmsashare  FROM
(
 SELECT msabra.msabr, msabra.namefull, msabra.summsabra, msa.summsa FROM
 (
  SELECT msabr, namefull, sum(depsumbr) as summsabra
  FROM all_2018 
  GROUP BY msabr, namefull
 ) msabra
 INNER JOIN
 (
  SELECT msabr, sum(depsumbr) as summsa
  FROM all_2018 
  GROUP BY msabr
 ) msa
 ON msabra.msabr = msa.msabr
) tots
INNER JOIN
(
  SELECT distinct msabr
  FROM ALL_2018 
  WHERE namefull = 'Regions bank' and msabr > '0'
) bnks 
ON tots.msabr = bnks.msabr
)

【问题讨论】:

  • 我正在使用手机,无法下载此 csv 数据以查看其外观。您所说的 NAMEFULL 是指 NAMEBR 吗?您所说的“每个 MSA 中的存款总额”是指“MSA 中每个银行的存款总额”。您可以在问题中添加 csv 数据的 sn-p 吗?
  • 嗨 Caius,非常感谢您的观看!我认为 NAMEFULL 比 NAMEBR 更全面,因为 NAMEBR 可以将 JP Morgan 与 JP Morgan Chase 混为一谈。是的,“每个 MSA 中的存款总额”是指“MSA 中每个银行的存款总额”。我清理了一些数据,但我在原始问题上添加了一个快照以供参考。
  • 这是一次记忆之旅;取出一台可以访问的旧机器并导入 2018 年数据(约 53mb)并尝试了以下一些事情/结果
  • @hbk239 ALL_2018 是包含从 CSV 文件导入的数据的 Access 表,还是指向 CSV 文件的链接。如果还没有,请将其设置为带有索引的 MSABRNAMEFULL 字段的表。
  • 这点值得注意;对于我的查询从头开始,我将 csv 导入到正确的访问中,而不是链接

标签: sql ms-access subquery


【解决方案1】:

抱歉,这只是一个混乱、无法解释的代码答案。如果我们使用内部联接而不是 IN 来过滤“仅在与所需银行相关的 msabr 列表中具有 msabr 的银行”,即使没有索引,它在旧 ibm t60p 上的 access 2010 也能在几秒钟内运行。 /p>

SELECT * FROM
(
  SELECT msabr, namefull, sum(depsumbr) as sumdep 
  FROM all_2018 
  GROUP BY msabr, namefull
) sums
INNER JOIN
(
  SELECT distinct msabr
  FROM ALL_2018 
  WHERE namefull = 'Regions bank' and msabr > 0
) bnks 
ON sums.msabr = bnks.msabr

我不知道您是想要输出中每个 msabr/分支配对的总和还是每个分支的总和。如果它只是在外部再次分支任一组(将select * 替换为select nameful, sum(sumdep) ... group by namefull)或尝试这种形式:

SELECT d.namefull, sum(d.depsumbr) as sumdep
FROM all_2018 d
INNER JOIN
(
  SELECT distinct msabr
  FROM ALL_2018 
  WHERE namefull = 'Regions bank' and msabr > 0
) bnks 
ON d.msabr = bnks.msabr
GROUP BY d.namefull

如果您想要通过 msa 求和,请更改第一种形式的 sums 子查询中的分组,使其不提及分支。修改 msa 的第二种形式而不是分支将其从外部选择/分组中交换出来

我不知道第二个查询的执行情况,因为我是在手机上写的;未经测试


这是一个可以回答您在 cmets 中的两个问题的查询:

SELECT tots.msabr, tots.namefull, 100 * (tots.summsabra / tots.summsa) as percentmsashare  FROM
(
 SELECT msabra.msabr, msabra.namefull, msabra.summsabra, msa.summsa FROM
 (
  SELECT msabr, namefull, sum(depsumbr) as summsabra
  FROM all_2018 
  GROUP BY msabr, namefull
 ) msabra

 INNER JOIN
 (
  SELECT msabr, sum(depsumbr) as summsa
  FROM all_2018 
  GROUP BY msabr
 ) msa
 ON msabra.msabr = msa.msabr
) tots
INNER JOIN
(
  SELECT distinct msabr
  FROM ALL_2018 
  WHERE namefull = 'Regions bank' and msabr > 0
) bnks 
ON tots.msabr = bnks.msabr

ORDER BY tots.msabr ASC, tots.summsabra / tots.summsa DESC

它显示了 MSA 中的所有银行,按 MSA 中存款总额的市场份额降序排列。为此,它将数据分组在两个不同的级别:per-bank-per-msa 和 per-msa,然后百分比由 (perbankmsa / permsa) 给出

看起来像:

我可以想出一些方法来去除前 5 名之后的任何内容,但是这个查询最好在更强大的数据库中完成。现在,我将使用前端忽略此列表中每个 msa 的前 5 行之后的任何行 - 处理问题 2

问题 1,Regions 银行的市场份额会更简单:删除 INNER JOIN(...)bnks ON ... 并在 ORDER BY 上方添加一个 WHERE 子句,即 WHERE tots.namefull = 'Regions bank' - 整个过程访问因此是:计算总和所有 msa,计算所有 bank-msa 的总和,连接在一起,仅限于那些提到“区域银行”的行。因为每个 msa 的每家银行的市场份额是在过滤到仅“区域银行”之前计算的,所以我们仅在该 msa 中获得了“区域银行”的真实百分比。如果我们在计算总和之前过滤到“区域银行”,那么“区域银行”将拥有每个 MSA 的 100%,因为我们在分组/求和之前过滤掉了所有竞争行

SELECT tots.msabr, tots.namefull, 100 * (tots.summsabra / tots.summsa) as percentmsashare  FROM
(
 SELECT msabra.msabr, msabra.namefull, msabra.summsabra, msa.summsa FROM
 (
  SELECT msabr, namefull, sum(depsumbr) as summsabra
  FROM all_2018 
  GROUP BY msabr, namefull
 ) msabra

 INNER JOIN
 (
  SELECT msabr, sum(depsumbr) as summsa
  FROM all_2018 
  GROUP BY msabr
 ) msa
 ON msabra.msabr = msa.msabr
) tots
WHERE tots.namefull = 'Regions bank'

技术上我们不需要外部选择; where 子句和求和可以被推入 tots 和 tots 被取消。它只是以这种方式结束了对需要连接 3 个表的早期查询的修改(并且访问一次只能连接两个 - 再次,选择是多余的,我可以将连接括起来 - select * from (a join b) join c 与 @ 987654332@ - 我倾向于做后者,因为我的大部分工作都在不接受前者的数据库中)


好的,所以我在 cmets 中谈到了笛卡尔积,以给出排名。这很讨厌,但它是这样工作的。假设我们有以下田径成绩:

Name, Event, Seconds
Usain, 100m, 9.0
Jonno, 100m, 10.1
Timmy, 100m, 11.3
Roger, 400m, 41.3
Salva, 400m, 42.1
Erdoh, 400m, 44.0

如果我们运行这样的查询:

SELECT * FROM
  scores s1
  INNER JOIN scores s2 ON s1.event = s2.event and s1.time <= s2.time

那么&lt;=会导致行数相乘:

s1Name, s1Event, s1Seconds, s2Name, s2Event, s2Seconds
Usain, 100m, 9.0, Usain, 100m, 9.0
Jonno, 100m, 10.1, Usain, 100m, 9.0
Jonno, 100m, 10.1, Jonno, 100m, 10.1
Timmy, 100m, 11.3, Usain, 100m, 9.0
Timmy, 100m, 11.3, Jonno, 100m, 10.1
Timmy, 100m, 11.3, Timmy, 100m, 11.3

只有 1 个 usain 行;只有一个人等于或快于usain,那就是usain。有两个 jonno 行; usain 更快,所以他的时间比&lt; 少。乔诺等于乔诺。 Usain 和 Jonno 都比 Timmy 快,所以我们最终得到一个 Timmy 行与 usain 匹配,timmy 与 jonno 匹配,timmy 与自己匹配

因此,您会注意到 Usain 有 1 排,他在比赛中排名第一。 Jonn 有 2 排,排在第 2 位。 Timmy 有 3 排,排在第三位。如果我们因此只对 s1 数据进行分组并计算它,我们会得到一个排名:

SELECT s1.Name, s1.Event, s1.Time, count(*) as ranking 
FROM
  scores s1
  INNER JOIN scores s2 ON s1.event = s2.event and s1.time <= s2.time
GROUP BY
  s1.Name, s1.Event, s1.Time

s1Name, s1Event, s1Seconds, ranking
Usain, 100m,  9.0, 1
Jonno, 100m, 10.1, 2
Timmy, 100m, 11.3, 3

现在,您正在计算市场份额等总体百分比的主要查询已经很大而且很丑陋;我不会把它全部粘贴两次让它变得更大更丑,这样它就可以连接到自身。也许可以创建一个视图 (CREATE VIEW AS [big ugly select statement]),然后编写另一个查询将视图连接到自身

请记住:您可能需要在过滤到“仅区域银行”之前执行此排名技巧,因为您想要对所有银行进行排名,然后仅选择“区域银行”为名称且&lt;=5 为排名

【讨论】:

  • Caius - 非常感谢您在这里的专业知识。我确实有几个与您刚刚创建的查询相关的后续问题:1)我将如何显示您示例中的“区域银行”在每个 MSA 中的市场份额百分比? 2) 我将如何显示银行在哪些地区的 MSA 排名前 5?
  • ooof,,, Access 可能不是这项工作的最佳工具;您对将其加载到本地运行的免费版本的 SQL Server 有什么兴趣?必须是访问权限吗?
  • 你是 GOAT Caius - 我认为 Access 是我们办公室中唯一使用的数据科学工具集...今晚我将消化第二个答案并回复任何后续问题。再次感谢!
  • Hey Caius - 我在下面添加了一个关于排名问题的后续问题。如果您有能力,我将非常感谢您的帮助!
  • 我试图围绕“排名”和“分区依据”来解决第一个问题,即显示哪些地区银行的市场份额排名前 5 位的 msas
【解决方案2】:

EXISTS 通常比IN 快,尤其是使用正确的索引。因此,将查询写为:

SELECT max(NAMEFULL), sum(DEPSUMBR) AS Deposit 
FROM ALL_2018 as a
WHERE EXISTS (SELECT 1
              FROM ALL_2018 as a2
              WHERE a2.MSABR = a.MSABR AND
                    a2.NAMEFULL = [Enter Bank Name]
             ) AND
      a.MSABR <> '0';

正确的索引是ALL_2018(MSABR, NUMEFULL)

【讨论】:

  • 谢谢戈登!不幸的是,我遇到了一个语法错误——我尝试在括号内移动最后一行,但这似乎没有帮助。上面的代码还有别的写法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-14
  • 2015-11-25
  • 1970-01-01
  • 1970-01-01
  • 2021-10-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多