【问题标题】:Conditional aggregation efficiency条件聚合效率
【发布时间】:2019-11-17 18:05:31
【问题描述】:

让我们有两张桌子。

A(id int primary key, groupby int, fkb int, search int, padding varchar(1000))
B(id int primary key, groupby int, search int)

它们是使用以下脚本创建的。第一个表很大(1M 行),第二个较小(10k 行)。

CREATE  TABLE A(
  id int not null primary key, 
  groupby int null, 
  fkb int null, 
  search int null,
  padding varchar(1000) null
)  AS
WITH x AS
(
  SELECT 0 n FROM dual
  union all
  SELECT 1 FROM dual
  union all
  SELECT 2 FROM dual
  union all
  SELECT 3 FROM dual
  union all
  SELECT 4 FROM dual
  union all
  SELECT 5 FROM dual
  union all
  SELECT 6 FROM dual
  union all
  SELECT 7 FROM dual
  union all
  SELECT 8 FROM dual
  union all
  SELECT 9 FROM dual
), t1 AS
(
  SELECT ones.n + 10 * tens.n + 100 * hundreds.n + 1000 * thousands.n + 10000 * tenthousands.n + 100000 * hundredthousands.n as id
  FROM x ones,     x tens,      x hundreds,       x thousands,       x tenthousands,       x hundredthousands
), t2 AS
(
    SELECT  id,
            mod(id, 100) groupby
    FROM t1
)
SELECT  cast(id as int) id,
        cast(groupby as int) groupby,
        cast(mod(orderby, 9173) as int) fkb,
        cast(mod(id, 911) as int) search
FROM t2;

CREATE  TABLE B(
  id int not null primary key, 
  groupby int null, 
  search int null
) AS
WITH x AS 
(
  SELECT 0 n FROM dual
  union all 
  SELECT 1 FROM dual
  union all 
  SELECT 2 FROM dual
  union all 
  SELECT 3 FROM dual
  union all 
  SELECT 4 FROM dual
  union all 
  SELECT 5 FROM dual
  union all 
  SELECT 6 FROM dual
  union all 
  SELECT 7 FROM dual
  union all 
  SELECT 8 FROM dual
  union all 
  SELECT 9 FROM dual  
), t1 AS
(
  SELECT ones.n + 10 * tens.n + 100 * hundreds.n + 1000 * thousands.n as id  
  FROM x ones,     x tens,      x hundreds,       x thousands       
)
SELECT  cast(id as int) id,
        cast(mod(id + floor(100000 / (id+1)) , 100) as int) groupby,
        cast(mod(id, 901) as int) search,
        rpad(concat('Value ', id), 1000, '*') as padding
FROM t1;

我想尽快在 H2 中处理以下条件聚合查询,但不添加任何其他索引。

SELECT  B.groupby,
       count(CASE WHEN A.search = 1 THEN 1 END) as search1,
       count(CASE WHEN A.search = 900 THEN 1 END) as search2
FROM B
LEFT JOIN A ON A.fkb = B.id
WHERE B.search < 10
GROUP BY B.groupby

是否可以重写查询最多运行 2 分钟的查询?我尝试了许多不同的重写,但是,每一个都持续运行几分钟而没有结束。我将 Java 虚拟机内存设置为 4GB (-Xmx4G)。

如果我在 MySQL 中尝试相同的测试并且查询的处理时间少于 10 秒。

【问题讨论】:

    标签: sql h2 conditional-aggregation


    【解决方案1】:

    你的初始化脚本有语法错误,我修改如下:

    CREATE  TABLE A(
      id int not null primary key, 
      groupby int null, 
      fkb int null, 
      search int null,
      padding varchar(1000) null
    )  AS
    SELECT  cast(x as int) id,
            cast(mod(x, 100) as int) groupby,
            cast(mod(mod(x, 100), 9173) as int) fkb,
            cast(mod(x, 911) as int) search,
            rpad(concat('Value ', x), 1000, '*') as padding
    FROM SYSTEM_RANGE(0, 999999);
    
    CREATE  TABLE B(
      id int not null primary key, 
      groupby int null, 
      search int null
    ) AS
    SELECT  cast(x as int) id,
            cast(mod(x + floor(100000 / (x+1)), 100) as int) groupby,
            cast(mod(x, 901) as int) search
    FROM SYSTEM_RANGE(0, 9999);
    

    为了简单起见,我还使用了 H2 特定的 SYSTEM_RANGE()

    与您查询的 EXPLAIN 命令显示以下执行计划

    SELECT
        "B"."GROUPBY",
        COUNT(CASE WHEN ("A"."SEARCH" = 1) THEN 1 END) AS "SEARCH1",
        COUNT(CASE WHEN ("A"."SEARCH" = 900) THEN 1 END) AS "SEARCH2"
    FROM "PUBLIC"."B"
        /* PUBLIC.B.tableScan */
        /* WHERE B.SEARCH < 10
        */
    LEFT OUTER JOIN "PUBLIC"."A"
        /* PUBLIC.A.tableScan */
        ON "A"."FKB" = "B"."ID"
    WHERE "B"."SEARCH" < 10
    GROUP BY "B"."GROUPBY"
    

    这是意料之中的,因为您没有任何索引。不幸的是,如果没有它们,您将无法显着提高性能。

    我认为你需要一个约束。

    ALTER TABLE A ADD CONSTRAINT A_FKB_FK FOREIGN KEY(FKB) REFERENCES B(ID);
    

    有了这样的约束执行计划就更好了:

    SELECT
        "B"."GROUPBY",
        COUNT(CASE WHEN ("A"."SEARCH" = 1) THEN 1 END) AS "SEARCH1",
        COUNT(CASE WHEN ("A"."SEARCH" = 900) THEN 1 END) AS "SEARCH2"
    FROM "PUBLIC"."B"
        /* PUBLIC.B.tableScan */
        /* WHERE B.SEARCH < 10
        */
    LEFT OUTER JOIN "PUBLIC"."A"
        /* PUBLIC.A_FKB_FK_INDEX_4: FKB = B.ID */
        ON "A"."FKB" = "B"."ID"
    WHERE "B"."SEARCH" < 10
    GROUP BY "B"."GROUPBY"
    

    在我的旧电脑上,您的查询需要大约 11 秒。

    您也可以在使用 H2 的查询中使用 COUNT(*) FILTER (WHERE A.search = 1),但这样的查询将与 MySQL 不兼容,MySQL 尚不支持标准 SQL:2003 FILTER 子句,并且 FILTER 子句并不能真正提高性能这个查询,它只会提供更好的可读性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-11
      • 1970-01-01
      • 1970-01-01
      • 2020-02-27
      • 2021-08-24
      • 2019-08-13
      • 2010-11-01
      • 1970-01-01
      相关资源
      最近更新 更多