sql server:获取“日历”表中的值之和而不进行联接

qhhrdooz  于 2021-07-29  发布在  Java
关注(0)|答案(4)|浏览(261)

有没有可能得到 valuecalendar_tablemain_table 没有像下面这样加入?

select 
    date, sum(value)
from 
    main_table
inner join 
    calendar_table on start_date <= date and end_date >= date
group by 
    date

我尽量避免这样的加入,因为 main_table 是一个非常大的表,其中的行具有非常大的开始日期和结束日期,这绝对会扼杀我的性能。我已经为这两个表编制了索引。
预期结果示例:

+-----------+-------+
|   date    | total |
+-----------+-------+
| 7-24-2010 |    11 |
+-----------+-------+

示例表
日历表:

+-----------+-------+
|   date    | value |
+-----------+-------+
| 7-24-2010 |     5 |
| 7-25-2010 |     6 |
| ...       |   ... |
| 7-23-2020 |     2 |
| 7-24-2020 |    10 |
+-----------+-------+

主表:

+------------+-----------+
| start_date | end_date  |
+------------+-----------+
| 7-24-2010  | 7-25-2010 |
| 8-1-2011   | 8-5-2011  |
+------------+-----------+
k5ifujac

k5ifujac1#

您需要日历表中的总和。因此,我建议采用“增量”方法。首先取消激活数据,并将值作为增量和减量放入结果中:

select c.date, c.value as inc
from main_table m join
     calendar_table t
     on m.start_date = c.date
union all
select dateadd(day, 1, c.date), - c.value as inc
from main_table m join
     calendar_table t
     on m.end_date = c.date;

最后一步是聚合并进行累积求和:

select date, sum(inc) as value_on_date,
       sum(sum(inc)) over (order by date) as net_value
from ((select c.date, c.value as inc
       from main_table m join
            calendar_table t
            on m.start_date = c.date
      ) union all
      (select dateadd(day, 1, c.date), - c.value as inc
       from main_table m join
            calendar_table t
            on m.end_date = c.date
      )
     ) c
group by date
order by date;

这是为主表中的每一行处理两行数据。假设您的时间跨度通常长于两天(对于每个主行),则处理的结果数据应该小得多。更小的数据意味着更快的查询。

juud5qan

juud5qan2#

下面是一个交叉应用的例子。

select main_table.date
     , CalendarTable.ValueSum
from main_table
CROSS APPLY(
    SELECT SUM(value) as ValueSum
    FROM calendar_table
    WHERE start_date <= main_table.date and main_table.end_date >= date
) as CalendarTable
group by date
esbemjvw

esbemjvw3#

你可以试试这个。。。但请注意,从技术上讲,它仍然“连接”到主表。如果您查看一个执行计划,您将看到正在进行某种连接操作。

select
   date,
   (select sum(value) from calendar_table t where m.start_date <= t.date and m.end_date >= t.date)
from
   main_table m

该查询的问题是“main_table”没有作为结果的一部分进行分组。你可以在选择范围之外这样做,但我不知道你想达到什么目的。如果您分组只是为了得到总和,那么在组中维护“main_table”可能是超级棒的。

uujelgoq

uujelgoq4#

如前所述,为了从查询中的多个表中获取数据,必须执行某种类型的联接。
您没有提供索引对性能很重要的详细信息。我建议使用以下索引来优化查询性能。
为了 calendar_table ,请确保上有唯一的聚集索引(或主键) date . 或者,在 datevalue 包括列。
市场综合指数
main_table start_date 以及 end_date 列也可能是有益的。
即使使用了最佳索引,对于没有附加筛选条件的500m行表,查询仍然需要一些时间(例如,几分钟)。如果需要毫秒级的结果,请创建一个索引视图来具体化联接和聚合结果。请注意,索引视图将为两个表上的插入/删除以及对数据库的更新增加开销 value 列以保持索引与基础数据一致。
下面是一个索引视图ddl示例。

CREATE VIEW dbo.vw_example
WITH SCHEMABINDING
AS
SELECT
    date, sum(value) AS value, COUNT_BIG(*) AS countbig
from 
    dbo.main_table
inner join 
    dbo.calendar_table on start_date <= date and end_date >= date
group by 
    date;
GO
CREATE UNIQUE CLUSTERED INDEX cdx ON dbo.vw_example(date);
GO

根据您的sql server版本,优化器可能能够自动使用索引视图,以便您的原始查询可以使用视图索引而不做任何更改。否则,直接查询视图并指定 NOEXPAND 提示:

SELECT date, value AS total
FROM dbo.vw_example WITH (NOEXPAND);

编辑:
建议使用查询改进@gordonlinoff,在
main_table end_date 列将有助于优化查询。

相关问题