我有一张表格,其中包含与员工缺勤详情有关的记录;更新缺席时,而不是更新现有行;而是创建一个新行,并使用新记录的Id填充前一记录的LinkedRecordId字段,LinkedRecordId字段中的NULL值表示该记录是链中的最新记录。
我需要能够检索所有缺席记录的数据集,并将每个父记录的注释组合在一个字符串中,然后列出链中最新记录的其他字段。
以下是包含数据的表格的简化版本:
CREATE TABLE [dbo].[AbsenceData](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Notes] [varchar](max) NULL,
[LinkedRecordId] [int] NULL,
[CreatedAt] [datetime] NULL
)
我的问题是效果;我目前的解决方案是使用以下表值函数来收集和连接链中所有父记录的字符串:
CREATE FUNCTION [dbo].[AbsenceNotesFor](@AbsenceDataId INT)
RETURNS @return TABLE
(
AbsenceDataId INT
,Notes VARCHAR(MAX)
)
AS
BEGIN
DECLARE @notes VARCHAR(MAX)
;WITH AbsenceNotes AS (
SELECT
ad.Id
,ad.Notes
,ad.CreatedAt
FROM
AbsenceData ad WITH (NOLOCK)
WHERE
ad.Id = @absenceDataId
UNION ALL
SELECT
ad.Id
,ad.Notes
,ad.CreatedAt
FROM
AbsenceData ad WITH (NOLOCK)
INNER JOIN AbsenceNotes an ON an.Id = ad.LinkedRecordId
)
SELECT @notes = CONVERT(VARCHAR(11),CreatedAt, 105) + ' ' + CONVERT(VARCHAR(5),CreatedAt, 114)+ CHAR(13)+CHAR(10) + CAST(Notes AS VARCHAR(MAX)) + CHAR(13)+CHAR(10) + CHAR(13)+CHAR(10) + COALESCE(@notes,'')
FROM AbsenceNotes
INSERT INTO @return
SELECT AbsenceDataId = @AbsenceDataId, Notes = @notes
RETURN;
END
以下是当前实施的简化版本:
SELECT
Id
,n.Notes
FROM AbsenceData
CROSS APPLY dbo.AbsenceNotesFor(Id) n
WHERE
LinkedRecordId IS NULL
当针对几百条记录的数据集运行此操作时,我已经可以看到性能问题,这似乎是由于表值函数内的逻辑。
我正在寻找一种更有效的方法来做到这一点,任何想法?
我们正在使用MS SQL Server 2016标准版
以下是SQL Fiddle的示例: http://sqlfiddle.com/#!6/b9834
答案 0 :(得分:1)
使用多语句 - 语法的表值函数以绝对糟糕的性能而众所周知。您应该尽可能避免使用BEGIN
和END
。
与内联或 ad-hoc 功能相同的功能要好得多。如果没有样本数据,这是一次盲目的飞行,但我认为你会得到相同的结果,但有更好的表现:
CREATE FUNCTION [dbo].[AbsenceNotesFor](@AbsenceDataId INT)
RETURNS TABLE
AS
RETURN
WITH AbsenceNotes AS (
SELECT
ad.Id
,ad.Notes
,ad.CreatedAt
FROM
AbsenceData ad WITH (NOLOCK)
WHERE
ad.Id = @absenceDataId
UNION ALL
SELECT
ad.Id
,ad.Notes
,ad.CreatedAt
FROM
AbsenceData ad WITH (NOLOCK)
INNER JOIN AbsenceNotes an ON an.Id = ad.LinkedRecordId
)
SELECT @AbsenceDataId AS AbsenceDataId
,(
REPLACE
(
STUFF
(
(
(
SELECT '|#|'+ '|#|'
+ CONVERT(VARCHAR(11),CreatedAt, 105) + ' '
+ CONVERT(VARCHAR(5),CreatedAt, 114)
+ '|#|'
+ CAST(Notes AS VARCHAR(MAX))
FROM AbsenceNotes
FOR XML PATH(''),TYPE
).value('.','nvarchar(max)')
),1,6,''
),'|#|',CHAR(13)+CHAR(10)
)
) AS Notes;
简短说明:
我没碰到你的CTE。
与SELECT @variable=@variable + Something
的字符串连接是一种表现非常糟糕的程序方法。我将其替换为FOR XML PATH('')
。如果您搜索Group concat
和Sql-Server ...
我用一个魔术值(|#|
)替换了换行符,以避免以后出现问题。
STUFF
函数除了删除开头的6个字符(断行的魔法值的两倍)外别无其他功能
REPLACE
函数将魔术值更改回实际换行符。
如果您希望Notes按降序排列(您的代码如下所示),只需向内部ORDER BY
添加适当的SELECT
。
答案 1 :(得分:0)
在更大的集合上仍然存在性能问题 - 但是在更详细地执行执行计划之后;我意识到我遗漏了一些索引并且查询未被完全覆盖',添加缺少的索引,其中包含Id
的密钥字段以及Notes
和{的包含字段{1}}大大提高了表现;但是我不确定这是明智的,也许我需要就这个问题提出另一个问题......