【问题标题】:SQL - Remove most recent date and note from stringSQL - 从字符串中删除最近的日期和注释
【发布时间】:2020-04-10 00:41:38
【问题描述】:

我正在使用 SQL 中的一个表,该表有一个字段,每次添加注释时都会附加一个字段。出于报告目的,我只需要最新的日期/注释,老实说不知道从哪里开始。我已经完成了仅提取数字和字符索引等的功能 - 但这些似乎并不适用,因为该字段(在此示例中)有 3 个类似的值,我只需要最新的一个。

字段日期示例:

3/4/2020 1:06:30 PM by username
Notes #1

3/4/2020 1:06:41 PM by username
Notes #2

**3/12/2020 1:12:27 PM by username
Can enter a new note here**

我只需要提取粗体注释。我的报告是从存储过程中填充的,所以我需要找出 SQL 代码来仅提取最新的注释。任何帮助将不胜感激!

【问题讨论】:

  • 你用的是什么数据库?
  • 请分享样本数据和期望的结果。我假设您的意思不是“每次添加注释时都会附加的字段”,而是“附加的行……”您在此处添加的示例数据是否全部在单个列中(例如日期和文本存储在同一列)?
  • 数据存储在每条记录的单个列中。是的,随着字段的更新,该列也会更新,因此显示的数据只是一条记录的“注释”列。

标签: sql string


【解决方案1】:

您可能已经意识到这里存在多个数据库设计问题。

  1. 您不应在同一记录中有多个记录(备注)。
  2. 您不应在同一字段中包含多个字段(日期、用户名、评论)。

我假设您使用的是 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;

这很丑,我知道。而且我不确定我能否完全解释每一行。我只是想提供一个解决方案作为一种自我学习的方法。这个查询是一个完美的例子,说明了为什么好的数据库设计很重要。

【讨论】:

  • 谢谢!我意识到存在数据库设计问题!不是我的数据库,我只是在其中工作。如果您列出的问题不存在,我的生活会容易得多。感谢您的意见,我会向您报告。
  • 我完全理解必须在现有的框架内工作。祝你好运!
  • 嗨艾萨克 - 还没有。这个客户是 2012 年的,Cross_Apply 不起作用。假设我可以使用 Inner Join 代替,但还没有到达那里。
  • 我不认为 CROSS APPLY 是我的解决方案的问题,但 STRING_SPLIT() 肯定是。您可以使用 XQuery(我认为这就是它的名称)来实现类似的功能,例如 this。我能够让它在我拥有的 SQL Server 2012 服务器上工作,但不能在 dbfiddle 上工作。
猜你喜欢
  • 2017-01-19
  • 2012-01-27
  • 2014-04-22
  • 1970-01-01
  • 1970-01-01
  • 2013-04-12
  • 2013-08-05
  • 1970-01-01
  • 2021-09-11
相关资源
最近更新 更多