您可能已经意识到这里存在多个数据库设计问题。
- 您不应在同一记录中有多个记录(备注)。
- 您不应在同一字段中包含多个字段(日期、用户名、评论)。
我假设您使用的是 SQL Server。如果没有,该解决方案应该能够适应您的平台。我冒昧地添加了一个名为 SomeID 的 ID 列。
IF OBJECT_ID('tempdb.dbo.#Notes', 'U') IS NOT NULL
DROP TABLE #Notes;
CREATE TABLE #Notes
(
SomeID INT
, Note VARCHAR(4000)
);
INSERT INTO #Notes
VALUES
(1, '3/4/2020 1:06:30 PM by username
Notes #1
3/4/2020 1:06:41 PM by username
Notes #2')
,
(2
, '3/4/2020 1:16:30 PM by username
Notes #1
3/4/2020 1:23:41 PM by username
Notes #2
3/4/2020 1:32:51 PM by username
Notes #3');
SELECT
*
FROM (
SELECT
y.SomeID
, CONVERT(DATETIME2, y.NoteDateText) AS NoteDate
, REPLACE(y.Line1, y.NoteDateText + ' by ', '') AS UserName
, y.Comment
, ROW_NUMBER() OVER (PARTITION BY y.SomeID
ORDER BY CONVERT(DATETIME2, y.NoteDateText) DESC
) AS RowNumber
FROM (
SELECT
x.SomeID
, LEFT(x.Line1, PATINDEX('%by%', x.Line1) - 2) AS NoteDateText
, x.Line1
, x.Line2 AS Comment
FROM (
SELECT
SomeID
, LEFT(value, PATINDEX('%' + CHAR(13) + CHAR(10) + '%', value) - 1) AS Line1
, RIGHT(value, LEN(value) - PATINDEX('%' + CHAR(13) + CHAR(10) + '%', value) - 1) AS Line2
, value
FROM #Notes
CROSS APPLY STRING_SPLIT(REPLACE(Note, CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10), '~'), '~')
) x
) y
) z
WHERE z.RowNumber = 1
基本思想是使用STRING_SPLIT() 表值函数将注释拆分为单独的记录。由于 STRING_SPLIT() 仅适用于单数字符的分隔符,并且您的笔记记录似乎由两组回车符 (CHAR(13)) 和换行符 (CHAR(10)) 分隔,因此我将它们替换为“~” .您应该使用一些不会出现在 Note 记录文本中的字符。现在我已经将笔记分开了,我可以使用字符串操作函数将每个笔记分成适当的字段,包括 NoteDate、UserName 和 Comment。
最后,我使用 ROW_NUMBER() 函数按 NoteDate 对 SomeID 的每个值按降序对 Notes 进行排序,这样我就可以只为每个 SomeID 选择第一行。
我当然可以进一步浓缩这一点,而不是使用那么多级别的子查询,但我想展示进展并希望让它更容易理解。另外,我尝试将它放在 SQL Fiddle 或 dbfiddle 中,但我一直收到以下错误。
传递给 LEFT 或 SUBSTRING 函数的长度参数无效。
此代码适用于 SQL Server 2016 或更高版本的 SQL Server Management Studio。
这是我基于此博客post 在 SQL Server 2012 服务器上工作的另一种方法。
IF OBJECT_ID('tempdb.dbo.#Notes', 'U') IS NOT NULL
DROP TABLE #Notes;
CREATE TABLE #Notes
(
SomeID INT
, Note VARCHAR(4000)
);
INSERT INTO #Notes
VALUES
(1, '3/4/2020 1:06:30 PM by username
Notes #1
3/4/2020 1:06:41 PM by username
Notes #2')
, (2
, '3/4/2020 1:16:30 PM by username
Notes #1
3/4/2020 1:23:41 PM by username
Notes #2
3/4/2020 1:32:51 PM by username
Notes #3');
SELECT
x6.SomeID
, x6.NoteDate
, x6.UserName
, x6.Comment
FROM (
SELECT
x5.SomeID
, x5.NoteDate
, x5.UserName
, x5.Comment
, ROW_NUMBER() OVER (PARTITION BY x5.SomeID
ORDER BY CONVERT(DATETIME2, x5.NoteDate) DESC
) AS RowNumber
FROM (
SELECT
x4.SomeID
, CONVERT(DATETIME, LEFT(x4.SingleNote, PATINDEX('%by%', x4.SingleNote) - 2)) AS NoteDate
, SUBSTRING(
x4.SingleNote
, PATINDEX('%by%', x4.SingleNote) + 3
, PATINDEX('%' + CHAR(10) + '%', x4.SingleNote)
- (PATINDEX('%by%', x4.SingleNote) + 3)
) AS UserName
, RIGHT(x4.SingleNote, LEN(x4.SingleNote) - PATINDEX('%' + CHAR(10) + '%', x4.SingleNote)) AS Comment
FROM (
SELECT
x2.SomeID
, x2.Note
, LTRIM(RTRIM(m.n.value('.[1]', 'varchar(8000)'))) AS [SingleNote]
FROM (
SELECT
SomeID
, Note
, CAST('<XMLRoot><RowData>'
+ REPLACE(
Note
, CHAR(13) + CHAR(10) + CHAR(13) + CHAR(10)
, '</RowData><RowData>'
) + '</RowData></XMLRoot>' AS XML) AS x1
FROM #Notes
) x2
CROSS APPLY x1.nodes('/XMLRoot/RowData') m(n)
) x4
) x5
) x6
WHERE x6.RowNumber = 1;
这很丑,我知道。而且我不确定我能否完全解释每一行。我只是想提供一个解决方案作为一种自我学习的方法。这个查询是一个完美的例子,说明了为什么好的数据库设计很重要。