【问题标题】:Get Lead value over multiple partitions获取多个分区的潜在客户价值
【发布时间】:2019-04-17 07:32:20
【问题描述】:

我有一个问题,我觉得可以使用滞后/领先 + 分区来解决,但我无法解决这个问题。

每两年(大约)邀请客户参与研究项目。 每个项目都会选择一些客户。 一些客户被选中进行多个研究项目。 那些会收到邀请。在某些情况下,不会发送邀请。如果客户没有对邀请做出反应,则会发送第二个邀请(提醒)。第三,第四也是可能的。

我需要了解客户是否收到过之前研究项目的邀请。 (以及可选的,是哪个邀请)。

数据集如下所示:

clientID | projectID | invitationID
  14     |    267    |     489
  14     |    267    |     325
  16     |    385    |     475
  17     |    546    |     NULL
  17     |    547    |     885
  17     |    548    |     901
  18     |    721    |     905
  18     |    834    |     906
  18     |    834    |     907
  19     |    856    |     908
  19     |    856    |     929
  19     |    857    |     931
  19     |    857    |     945
  19     |    858    |     NULL


Client 14 has had 2 invitations for the same research-project
Client 16 has had 1 invitation for 1 research-project
Client 17 has been selected for 3 research-projects but opted out for project 546, receiving 1 invitation each for the following projects. 
Client 18 has been selected for 2 research-projects. For the second project he got a 2 invitations.
Client 19 has been selected for three research-projects. For the first two a reminder was set. Client 19 was selected for project 858 but opted out thus no invitation.

现在我需要确定每个客户是否收到了之前研究项目的邀请。 (并且可选地,那是哪个邀请)。我只需要第一个邀请(如果有多个)。 所以我得到的数据集应该是这样的(括号之间的东西是可选的):

clientID | projectID | invitationID | InvitedForPreviousProject
  14     |    267    |     489      |      0
  14     |    267    |     325      |      0
  16     |    385    |     475      |      0
  17     |    546    |     NULL     |      0
  17     |    547    |     885      |      0
  17     |    548    |     901      |      1 (885)
  18     |    721    |     905      |      0
  18     |    834    |     906      |      1 (905)
  18     |    834    |     907      |      1 (905)
  19     |    856    |     908      |      0
  19     |    856    |     929      |      0
  19     |    857    |     931      |      1 (908)
  19     |    857    |     945      |      1 (908)
  19     |    858    |     NULL     |      1 (931)

这可以使用 LEAD、Rank、Dense-Rank 来完成吗?创建语句,包括以下数据

declare @table table (
    [clientID] [int] NULL,
    [projectID] [int] NULL,
    [invitationID] [int] NULL
)
INSERT @table ([clientID], [projectID], [invitationID]) VALUES
(14, 267, 489),
(14, 267, 325),
(16, 385, 475),
(17, 546, NULL),
(17, 547, 885),
(17, 548, 901),
(18, 721, 905),
(18, 834, 906),
(18, 834, 907),
(19, 856, 908),
(19, 856, 929),
(19, 857, 931),
(19, 857, 945),
(19, 858, NULL)

【问题讨论】:

  • 我认为你想在没有自加入或子查询的情况下获得这个(出于速度原因)?
  • 是的。它需要执行,它将运行超过 600 万条记录
  • @Henrov 。 . .您的表没有足够的信息。您提到“以前的”,但 SQL 表表示 无序 集。除非列指定排序,否则没有排序。
  • @GordonLinoff De ordering 可以从 id 的 order 中得出。这是一个简化的例子,在现实生活中我也可以使用日期列。然而,id 应该在 irder 中得到保证(由序列提供)

标签: sql sql-server tsql sql-server-2017 data-partitioning


【解决方案1】:

这有帮助吗?

declare @table table (
    [clientID] [int] NULL,
    [projectID] [int] NULL,
    [invitationID] [int] NULL
)
INSERT @table ([clientID], [projectID], [invitationID]) VALUES
(14, 267, 489),
(14, 267, 325),
(16, 385, 475),
(17, 546, NULL),
(17, 547, 885),
(17, 548, 901),
(18, 721, 905),
(18, 834, 906),
(18, 834, 907),
(19, 856, 908),
(19, 856, 929),
(19, 857, 931),
(19, 857, 945),
(19, 858, NULL);

--查询使用DENSE_RANK()和一个相关的子查询

WITH ranked AS
(
    SELECT t.* 
         ,DENSE_RANK() OVER(PARTITION BY t.clientID ORDER BY t.projectID) AS InvRank
    FROM @table t
)
SELECT r.*
      ,earlierProject.invitationID
FROM ranked r
OUTER APPLY(SELECT TOP 1 *
            FROM ranked r2 
            WHERE r2.clientID=r.clientID
             AND  r2.projectID<r.projectID 
             AND  r2.InvRank=r.InvRank-1   
            ORDER BY invitationID ASC
            ) earlierProject
ORDER BY r.clientID,r.projectID,r.invitationID;

如果您的表中为“0”,则邀请 ID 将为 NULL,并在找到项目时设置为所需的值。

提示

实际上不需要APPLY。如果您只需要invitationID,您可以直接将子查询作为列放置(稍微快一些)。但这更容易阅读,您也可以了解其他专栏...

【讨论】:

  • 做它需要做的事情。我将在实际数据集上对此进行测试:)
  • @Henrov 如果性能很重要(很多行),将中间集写入临时表并为所有相关列(也在InvRank)放置索引可能会有所帮助。
  • Dense_RANKI() 在这种情况下就像一个魅力。使用具有适当索引的中间表可以获得良好的性能。我发誓我的 SSIS 服务器在计算数字时正在哼着愉快的曲子:D
【解决方案2】:

您需要一个指定排序的列。让我假设有一个邀请日期以及其他列。

有了这些信息,你的标志很容易通过比较两个值来计算:

  • 客户的最短邀请日期
  • 客户/项目 ID 的最短邀请日期

当这些相同时,这是第一个有邀请的项目。

所以:

select t.*,
       (case when min(invitationDate) over (partition by clientId order by invitationDate) =
                  min(invitationDate) over (partition by clientId, projectId order by invitationDate)
             then 0 else 1                  
        end) as InvitedForPreviousProject
from @table t;

【讨论】:

  • 这似乎是一个聪明的解决方案,谢谢。我也会测试一下
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-08
  • 2011-08-26
  • 1970-01-01
  • 2021-01-08
  • 1970-01-01
相关资源
最近更新 更多