SQL - Удалить самую последнюю дату и заметку из строки - PullRequest
1 голос
/ 16 марта 2020

Я работаю с таблицей в SQL, в которой есть поле, которое добавляется каждый раз при добавлении заметки. Для отчетности мне нужна только последняя дата / примечание и, честно говоря, не знаю, с чего начать. Я сделал функции для извлечения только чисел и символьных индексов и т. Д. c - но они, кажется, не применяются, если поле (в этом примере) имеет 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, чтобы извлечь только последнюю заметку. Любая помощь будет оценена!

1 Ответ

0 голосов
/ 16 марта 2020

У вас есть несколько проблем с дизайном базы данных, которые вы, возможно, уже поняли.

  1. У вас не должно быть нескольких записей (заметок) в одной записи.
  2. Вы не должны иметь несколько полей (Дата, Имя пользователя, Комментарий) в одном поле.

Я предполагаю, что вы используете SQL Сервер. Если нет, то это решение должно быть в состоянии адаптироваться к вашей платформе. Я позволил себе добавить столбец ID с именем SomeID .

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

Основная идея c состоит в том, чтобы разделить заметки на отдельные записи с помощью STRING_SPLIT () табличная функция. Поскольку STRING_SPLIT () работает только с разделителем единственного символа, а записи заметок, кажется, разделены двумя наборами возврата каретки (CHAR (13)) и перевода строки (CHAR (10)), я заменил их на '~' , Вы должны использовать некоторый символ, который не будет в тексте ваших записей заметок. Теперь, когда у меня есть разделенные заметки, я могу выделить каждую заметку в соответствующие поля NoteDate, UserName и Comment, используя функции манипуляции со строками.

Наконец, я использовал функцию ROW_NUMBER () , чтобы Сортируйте заметки в порядке убывания по NoteDate для каждого значения SomeID, чтобы я мог затем выбрать только первую строку для каждого SomeID.

Я, конечно, мог бы сократить это далее и не использовать столько уровней подзапросов, но я хочу показать прогрессию и, надеюсь, облегчить следить. Кроме того, я попытался поместить это в SQL Fiddle или dbfiddle, но я продолжал получать следующую ошибку:

Недопустимый параметр длины, переданный в функцию LEFT или SUBSTRING.

Этот код работает в SQL Server Management Studio на SQL Server 2016 или выше.

Вот альтернативный подход, который я получил для работы на SQL Server 2012 server на основе этого блога post .

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;

Это ужасно, я знаю. И я не уверен, что могу полностью объяснить каждую строчку. Я просто пытаюсь предложить решение как метод обучения самостоятельно. Этот запрос является прекрасным примером того, почему хороший дизайн базы данных имеет значение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...