【问题标题】:Optimizing a SQL Query when joining two tables. Naive algorithm gives me millions of rows连接两个表时优化 SQL 查询。天真的算法给了我数百万行
【发布时间】:2018-09-14 03:11:53
【问题描述】:

我很抱歉,我不知道该问题的标题如何措辞。如果有人可以为我改进它以更好地满足我的要求,将不胜感激。

我有一个困扰我很长时间的问题。我将 Tableau 与 SQLServer 2014 结合使用。

我有一个表格,基本上显示了我们公司内的所有员工。他们的雇用日期和终止日期(如果仍在雇用,则为空)。我正在寻找过去的人数。下面是这个表的一个例子:

employeeID    HireDate    TermDate    FavouriteFish    FavouriteColor
    1           1/1/15     1/1/18         Cod               Blue
    2           4/12/16     NULL          Bass              Red
    .
    .
    .
    n

正如你所看到的,这个列表可以继续下去。事实上,这个有问题的表我目前有超过 10000 行的所有过去和现在的员工。

我的目标是构建一个视图,以查看过去 5 年中一年中的每一天我们拥有的受雇员工总数。这是踢球者...我需要保留其余信息,例如:

FavouriteFish    FavouriteColor... and so on

我能想到的唯一方法是为过去 5 年中的每一天创建一个单独的日历表,因为它非常慢,所以效果不佳;像这样:

Date       CrossJoinKey
1/1/2013        1
1/2/2013        1
1/3/2013        1
    .
    .
    .
4/4/2018        1

从这里我在我原来的员工表中添加一个名为:CrossJoinKey; 的列。像这样..

employeeID    HireDate    TermDate    FavouriteFish    FavouriteColor    CrossJoinKey
    1           1/1/15     1/1/18         Cod               Blue            1
    2           4/12/16     NULL          Bass              Red             1
    .
    .
    .
    n

从这里我创建一个LEFT JOIN Calendar ON Employee.CrossKeyJoin=Calendar.CrossKeyJoin

希望在这里您可以立即看到问题.. 它与 A LOT OF ROWS 建立了关系!!事实上,它给了我大约 1800 万行。它为我提供了我所追求的信息,但是查询需要很长时间,当我将其导入 Tableau 以创建数据提取时,它也需要很长时间才能完成。但是,一旦 Tableau 最终创建了数据提取,它比较快。在过去的 5 年中,我可以利用内心的勇气来隔离并每天创建员工人数……通过查看 date 字段是否介于 termDateHireDate 之间。但这整个过程需要相当频繁,我觉得目前的方法不实用。

我觉得这是完成我所追求的事情的一种天真的方式,而且我觉得这个问题必须在过去解决。这里有没有人可以解释一下如何优化这个?

注意事项...我考虑过创建一个查询,通过查看员工表并“计算”每个仍在雇用的员工来填充日历表,但这种方法失去了解决方案,我无法保留员工的任何其他数据。

如下所示,这样的东西可以工作并且速度更快,但不是我想要的:

Date       HeadCount
1/1/2013        1200
1/2/2013        1201
1/3/2013        1200
    .
    .
    .
4/4/2018        5000

非常感谢您花时间在这方面。

更新: 这是google sheets data sample的链接

【问题讨论】:

  • 首先:你不想交叉加入。你想加入calendar date between HireDate and ISNULL(TermDate,'2099-01-01')。 (我刚刚重读了您的问题并看到您尝试了这个)但是...即使这将您的报告减少为... 100,000 行...谁能理解 100,000 行?报告的目的是什么?
  • 还有一点不清楚的是您是否需要 Emp ID vals ......员工人数的数字人数衡量标准。 (您甚至可以将多个维度“垃圾”在一起以减少行数)
  • 为此您需要一个计数表。而且你不应该得到 100,000 行。您将获得每个日期的一行。你能提供一些样本数据吗?像2个多一点的员工。十几两个。然后,我可以向您展示如何使用计数表来非常轻松地构建它。
  • 您可以将其以可消耗的格式发布,而不是发布指向电子表格的链接吗? spaghettidba.com/2015/04/24/…
  • 这就是我感到困惑的地方。 “虽然这是踢球者......我需要保留其余信息,例如”。那是什么意思?您是在说您想要汇总数据和详细信息吗?那没有意义。您怎么能期望汇总和汇总数据?

标签: sql sql-server tableau-api


【解决方案1】:

我已经编辑了您的一些数据,您可以在@example 表中看到。

我想说明:您拼写错误 =D favorite 或 Color,请更正它,FavoriteColor 或 FavouriteColour。

declare @example as table (
    exampleid int identity(1,1) not null primary key clustered
,   StartDate date not null
,   TermDate  date null
);

insert into @example (StartDate, TermDate)
select '1/1/2016',  '1/1/2018' union all
select '4/3/2017',  '1/10/2018' union all
select '9/3/2016',  '2/4/2018' union all
select '5/9/2017',  '11/21/2017' union all
select '9/18/2016', '11/15/2017' union all
select '12/12/2015', '2/8/2018' union all
select '6/18/2016', '12/20/2017' union all
select '7/26/2015', '11/4/2017' union all
select '1/7/2015',  NULL union all
select '10/2/2013', '10/21/2013' union all
select '10/14/2013',    '12/12/2017' union all
select '10/11/2013',    '11/3/2017' union all
select '6/30/2015', '1/12/2018' union all
select '2/17/2016', NULL union all
select '8/12/2015', '11/26/2017' union all
select '12/2/2015', '11/15/2017' union all
select '3/30/2016', '11/30/2017' union all
select '6/18/2016', '11/9/2017' union all
select '4/3/2017',  '2/12/2018' union all
select '3/26/2017', '1/15/2018' union all
select '1/27/2017', NULL union all
select '7/29/2016', '1/10/2018';

  --| This is an adaption of Aaron Bertrand's work (time dim table)
  --| this will control the start date

  declare @date datetime = '2013-10-01';

   ;with cte as (
             select 1 ID
               , @date date_
           union all

          select ID + 1
               , dateadd(day, 1, date_)
            from cte
                  )
            , cte2 as (
          select top 1000 ID
               , cast(date_ as date) date_
               , 0 Running
               , iif(datepart(weekday, date_) in(1,7), 0,1) isWeekday
               , datepart(weekday, date_) DayOfWeek
               , datename(weekday, date_)  DayOfWeekName
               , month(date_) Month
               , datename(month, date_) MonthName
               , datepart(quarter, date_) Quarter
            from cte 
          --option (maxrecursion 1000)
                    )
        , cte3 as (
        select a.id 
             , Date_
             , b.StartDate
             , iif(b.StartDate is not null, 1, 0) Add_
             , iif(c.TermDate is not null, -1, 0) Remove_
          from cte2 a
          left join @example b
            on a.date_ = b.StartDate
          left join @example c
            on a.date_ = c.TermDate
         -- option (maxrecursion 1000)
                    )

        select date_
             --, Add_
             --, Remove_
             , sum((add_ + remove_)) over (order by date_ rows unbounded preceding) CurrentCount
             from cte3
            option (maxrecursion 1000)

结果集:

date_       CurrentCount
2013-10-01  0
2013-10-02  1
2013-10-03  1
2013-10-04  1
2013-10-05  1

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2023-01-05
  • 1970-01-01
  • 1970-01-01
  • 2018-12-01
  • 1970-01-01
  • 2010-10-18
  • 2016-10-16
  • 1970-01-01
相关资源
最近更新 更多