SQL 使用 CRM 本地数据使用子查询和连接变量进行查询

SQL query with subquery and concatenating variable using CRM on-premise data

我正在处理一份报告,我需要为特定“activities/tasks”提供注释摘要​​。

由于activity可以接受多个笔记,所以我必须搜索与activity相关的所有笔记。然后我按日期排序(从新到旧),并将它们与其他一些字符串连接起来:

[Tom Smith wrote on 9/23/2016 1:21 pm] Client was out of office, left message. [Jane Doe wrote on 9/21/2016 3:24 pm] Client called asking about pricing.

数据来自我们内部 CRM 系统的复制表,我使用的是 SQL Server 2012。 我使用的表是: AnnotationBase(包含注释)、ActivityPointerBase(包含 activities/tasks)和 SystemUserID (查找用户名)。由于数据不匹配,我必须对数据类型进行一些转换,以便我可以正确地连接它们,所以这就是为什么有很多 CAST 和 CONVERT。此外,并非所有活动都有与之关联的 NoteText,有时 NoteText 字段为 NULL,因此我必须捕获并过滤掉 NULL(否则它会破坏我的连接字符串)。

我写了下面的查询:

DECLARE @Notes VarChar(Max)
  SELECT 
(   SELECT TOP 5 @Notes = COALESCE(@Notes+ ', ', '') +  '[' + CONVERT(varchar(max), ISNULL(sUB.FullName, 'N/A')) + ' wrote on ' + CONVERT(varchar(10), CAST(Anno.ModifiedOn AS DATE), 101) + RIGHT(CONVERT(varchar(32),Anno.ModifiedOn,100),8) + '] ' + CONVERT(varchar(max), ISNULL(Anno.NoteText, '')) --+ CONVERT(varchar(max), CAST(ModifiedOn AS varchar(max)), 101)--+ CAST(ModifiedOn AS varchar(max))
    FROM [CRM_rsd].[dbo].[AnnotationBase] AS Anno
    LEFT OUTER JOIN [CRM_rsd].[dbo].[systemUserBase] AS sUB
    ON Anno.ModifiedBy = sUB.SystemUserId 
    WHERE Anno.ObjectId = Task.ActivityId--'0B48AB28-C08F-419A-8D98-9916BDFFDE4C' 
    ORDER BY Anno.ModifiedOn DESC
    SELECT LEFT(@Notes,LEN(@Notes)-1) 
) AS Notes
,Task.*
  FROM [CRM_rsd].[dbo].[ActivityPointerBase] AS Task
  WHERE Task.Subject LIKE '%Project On Hold%'

我知道上述方法可能不是很有效,但“搁置项目”列表相当小(少于 500 个),因此性能不是优先事项。优先考虑的是能够为每个 activity 获得一个合并和串联的注释列表。我一直在网上搜索解决方案,并且尝试了很多不同的方法。但是我收到以下错误:

Msg 102, Level 15, State 1, Line 3
Incorrect syntax near '='.
Msg 102, Level 15, State 1, Line 10
Incorrect syntax near ')'.

我设想了两种可能的解决方案:

  1. 我的子查询错误已修复
  2. 仅创建串联的 NotesText 的“视图”,按 ActivityId(可用作键)分组,然后仅从中查询。

尽管我很确定我的想法会奏效,但我似乎无法弄清楚如何同时连接列和组。

您想要做的是显示来自一个 table 的记录(在您的例子中是 ActivityPointerBase),并且您想要在内部添加一个计算列,其中包含来自另一个 table 的多个记录的信息(在你的情况下 AnnotationBase) 合并在行中。

实现此目的的方法有多种,它们对性能的影响各不相同:

选项 1。您可以编写一个标量函数,该函数接收任务 ID 作为参数,并在 select 前 5 条记录中,以程序方式连接它们并返回 varchar(max)

选项 2:您可以将子查询与 FOR XML 子句结合使用。

SELECT 
    SUBSTRING(
        CAST(
               (SELECT TOP 5 ', ' +  
                       '[' + CONVERT(varchar(max), ISNULL(FullName, 'N/A')) +
                       ' wrote on ' + 
                       CONVERT(varchar(10), CAST(ModifiedOn AS DATE), 101) +
                       RIGHT(CONVERT(varchar(32),ModifiedOn,100),8) + '] ' +
                       CONVERT(varchar(max), ISNULL(NoteText, ''))
                FROM [CRM_rsd].[dbo].[AnnotationBase] AS Anno
                     LEFT OUTER JOIN [CRM_rsd].[dbo].[systemUserBase] AS sUB ON Anno.ModifiedBy = sUB.SystemUserId 
                WHERE Anno.ObjectId = Task.ActivityId 
                ORDER BY Anno.ModifiedOn DESC
                FOR XML PATH(''),TYPE
               ) AS VARCHAR(MAX)
        ),3,99999) AS Notes
   ,Task.*
FROM [CRM_rsd].[dbo].[ActivityPointerBase] AS Task
WHERE Task.Subject LIKE '%Project On Hold%'

这里发生的是,通过使用 CAST() 中的构造,我们获取前 5 行并使 SQL 服务器生成一个没有元素名称的 XML,从而导致元素值,我们添加逗号作为分隔符。然后我们将 XML 转换为 varchar(max) 并删除第一条记录之前的初始分隔符。

我更喜欢选项 2,它比使用标量函数的性能要好得多。