【问题标题】:Slow query in SQL Server with left join左连接 SQL Server 中的慢查询
【发布时间】:2015-02-13 21:11:25
【问题描述】:

我正在寻找一种方法来优化 SELECT 查询,该查询在日期维度表和事实表之间进行左连接,该表必须显示 2014 年的度量总和。

这里是查询:

select SUM(coalesce(f.NBSCANS,0)) as somme
from DIM_DATE as d 
left join FCT_SCAN as f
on d.DATE = CAST(f.DATE_HEURE as DATE)
and CAST(d.HEURE as varchar(4)) = CAST(CAST(f.DATE_HEURE as time) as varchar(4))
where d.ANNEE = 2014

这个查询太慢了,因为我从未见过结果。 如果我在月份添加 WHERE 子句(例如:d.MOIS = 11),则需要 1 分钟(所以有点长)。

但是如果我也像这样在当天添加 WHERE 子句,结果会在 4 秒内显示:

select SUM(coalesce(f.NBSCANS,0)) as somme
from DIM_DATE as d 
left join FCT_SCAN as f
on d.DATE = CAST(f.DATE_HEURE as DATE)
and CAST(d.HEURE as varchar(4)) = CAST(CAST(f.DATE_HEURE as time) as varchar(4))
where d.ANNEE = 2014
and d.MOIS = 11
and d.JOUR = 5

有关信息,这里是 DIM_DATE 的 CREATE TABLE 脚本:

CREATE TABLE [dbo].[DIM_DATE](
    [DATE_HEURE] [datetime] NOT NULL,
    [ANNEE] [int] NULL,
    [MOIS] [int] NULL,
    [JOUR] [int] NULL,
    [DATE] [date] NULL,
    [JOUR_SEM_DATE] [varchar](10) NULL,
    [NUM_JOUR_SEM_DATE] [int] NULL,
    [HEURE] [time](0) NULL,
    [TRANCHE_1H] [time](0) NULL,
    [TRANCHE_DEMIH] [time](0) NULL,
    [TRANCHE_QUARTH] [time](0) NULL,
    [TRANCHE_10M] [time](0) NULL,
 CONSTRAINT [PK_DIM_DATE] PRIMARY KEY NONCLUSTERED 
(
    [DATE_HEURE] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

FCT_SCAN 中的 DATE_HEURE 字段与 DIM_DATE 中的相同。

在 DIM_DATE 每 10 分钟有一条记录:

DATE_HEURE
2015-06-17 12:00:00.000
2015-06-17 12:10:00.000
2015-06-17 12:20:00.000
2015-06-17 12:30:00.000
2015-06-17 12:40:00.000
2015-06-17 12:50:00.000
2015-06-17 13:00:00.000
2015-06-17 13:10:00.000
2015-06-17 13:20:00.000
2015-06-17 13:30:00.000

所以我的问题如下:知道我必须保留 LEFT JOIN,如何优化此查询? (对于 Cognos 包)

编辑:这是执行计划。

|--Compute Scalar(DEFINE:([Expr1006]=CASE WHEN [globalagg1013]=(0) THEN NULL ELSE [globalagg1015] END))
   |--Stream Aggregate(DEFINE:([globalagg1013]=SUM([partialagg1012]), [globalagg1015]=SUM([partialagg1014])))
        |--Parallelism(Gather Streams)
             |--Stream Aggregate(DEFINE:([partialagg1012]=COUNT_BIG([Expr1007]), [partialagg1014]=SUM([Expr1007])))
                  |--Compute Scalar(DEFINE:([Expr1007]=CASE WHEN [DECIS_DM_PARCOURS_PAX].[dbo].[FCT_SCAN].[NBSCANS] as [f].[NBSCANS] IS NOT NULL THEN CONVERT_IMPLICIT(int,[DECIS_DM_PARCOURS_PAX].[dbo].[FCT_SCAN].[NBSCANS] as [f].[NBSCANS],0) ELSE (0) END))
                       |--Nested Loops(Left Outer Join, OUTER REFERENCES:([d].[DATE], [Expr1009]))
                            |--Compute Scalar(DEFINE:([Expr1009]=CONVERT(varchar(4),[DECIS_DM_PARCOURS_PAX].[dbo].[DIM_DATE].[HEURE] as [d].[HEURE],121)))
                            |    |--Table Scan(OBJECT:([DECIS_DM_PARCOURS_PAX].[dbo].[DIM_DATE] AS [d]), WHERE:([DECIS_DM_PARCOURS_PAX].[dbo].[DIM_DATE].[ANNEE] as [d].[ANNEE]=(2014)))
                            |--Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1003]) OPTIMIZED)
                                 |--Compute Scalar(DEFINE:([Expr1021]=BmkToPage([Bmk1003])))
                                 |    |--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1019], [Expr1020], [Expr1018]))
                                 |         |--Compute Scalar(DEFINE:(([Expr1019],[Expr1020],[Expr1018])=GetRangeThroughConvert([DECIS_DM_PARCOURS_PAX].[dbo].[DIM_DATE].[DATE] as [d].[DATE],[DECIS_DM_PARCOURS_PAX].[dbo].[DIM_DATE].[DATE] as [d].[DATE],(62))))
                                 |         |    |--Constant Scan
                                 |         |--Index Seek(OBJECT:([DECIS_DM_PARCOURS_PAX].[dbo].[FCT_SCAN].[IDX_DATE_HEURE] AS [f]), SEEK:([f].[DATE_HEURE] > [Expr1019] AND [f].[DATE_HEURE] < [Expr1020]),  WHERE:([DECIS_DM_PARCOURS_PAX].[dbo].[DIM_DATE].[DATE] as [d].[DATE]=CONVERT(date,[DECIS_DM_PARCOURS_PAX].[dbo].[FCT_SCAN].[DATE_HEURE] as [f].[DATE_HEURE],0) AND [Expr1009]=CONVERT(varchar(4),CONVERT(time(7),[DECIS_DM_PARCOURS_PAX].[dbo].[FCT_SCAN].[DATE_HEURE] as [f].[DATE_HEURE],0),121)) ORDERED FORWARD)
                                 |--RID Lookup(OBJECT:([DECIS_DM_PARCOURS_PAX].[dbo].[FCT_SCAN] AS [f]), SEEK:([Bmk1003]=[Bmk1003]) LOOKUP ORDERED FORWARD)

【问题讨论】:

  • 我怀疑这里有一个循环加入。我有同样的问题。你能发布执行计划吗?如果它说循环连接,你会尝试在“左哈希连接”中更改“左连接”吗?

标签: sql sql-server date optimization left-join


【解决方案1】:

感谢执行计划,我找到了解决方案。它是 FCT_SCAN 的 DATE_HEURE 字段的索引,在查询中花费了很多,所以我删除了它。 现在执行时间大约是几秒钟。

谢谢大家的建议!

【讨论】:

  • 请尝试发布的答案。我相信其中一些比你的更快,更易读。
【解决方案2】:

试试这个。它应该会大大提高性能。您可以通过在表 FCT_SCAN 中创建计算列 PERSISTED 来进一步改进它。这将允许使用索引。

SELECT 
  coalesce(SUM(f.NBSCANS),0) as somme
FROM
  DIM_DATE as d 
LEFT JOIN
  FCT_SCAN as f
on 
  f.DATE_HEURE>= d.DATE_HEURE
  and f.DATE_HEURE < dateadd(minute, 10, d.DATE_HEURE)
WHERE
  d.ANNEE = 2014
  and d.MOIS = 11
  and d.JOUR = 5

【讨论】:

  • Msg 402, Level 16, State 1, Line 8 数据类型 datetime 和 time 在大于或等于运算符中不兼容。
  • 现在试试,我有一个错字 HEURE 而不是 DATE_HEURE
  • 我找到了解决方案,并写在了答案中。
【解决方案3】:

非等值连接总是不好的。

您应该更改逻辑:将 f.DATE_HEURE 截断为 0/10/20/30/40/50 分钟,然后加入。

select SUM(coalesce(f.NBSCANS,0)) as somme
from DIM_DATE as d 
left join 
  ( select
       DATEADD(minute, DATEDIFF(minute, 0, DATE_HEURE) / 10 * 10, 0) as x
      , ...
    from FCT_SCAN
  ) as f
on d.DATE = f.x
where d.ANNEE = 2014

在 FCT_SCAN.DATE_HEURE 上添加类似条件以限制在同一时期(例如 2014 年)也可能有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-01
    • 2010-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-25
    • 2016-09-05
    • 1970-01-01
    相关资源
    最近更新 更多