SQL Script для поиска Подсчитать количество: username Вхождения строки - PullRequest
0 голосов
/ 15 июня 2019

У меня есть таблица, в которой хранится информация всякий раз, когда пользователь вносит изменения в БД. Я хочу узнать, сколько раз пользователь вносит изменения в дату в приложении. Информация обычно хранится для каждого пользователя в одной строке, например:

2019-06-15randomname1:YES I DID IT  2019-06-14randomname2:HHHHHHH  JJJJJJ   2019-06-14Urandomnamexxxxxx: COMMENT OF PEOPLE

Я хочу найти: имя пользователя, чтобы определить, сколько раз пользователь менялся. В этом случае. Ответ должен быть 3. Как я могу это сделать

DECLARE @logEntry           VARCHAR(4000);
SET @logEntry       =  ':' + (SELECT PERSON_NAME FROM P_PERSON WHERE PERSON = logged_person) 

SELECT 
       id
       ,value
       ,COUNT = (LEN(value) - LEN(REPLACE(value, @logEntry  , '')))/LEN(@logEntry)
FROM table

Буду ли я использовать регулярное выражение, потому что для этого конкретного примера ответ будет 3, поскольку у нас есть 3.

Я решил использовать: username У меня возникла проблема с подзапросом, которое вернуло более 1 значения:

Ответы [ 3 ]

2 голосов
/ 15 июня 2019

Если я понимаю, вы хотите посчитать вхождение даты в строку

DECLARE @D VARCHAR(10) = '2019-01-01';

SELECT *, LEN(V) - (LEN(REPLACE(V, @D, '')) * 10) Occurrence
FROM (VALUES('A2019-01-01B2019-01-01C2019-01-01D2019-01-01E2019-01-01F2019-01-01'))T(V);

Возвращает:

+--------------------------------------------------------------------+------------+
|                                 V                                  | Occurrence |
+--------------------------------------------------------------------+------------+
| A2019-01-01B2019-01-01C2019-01-01D2019-01-01E2019-01-01F2019-01-01 |          6 |
+--------------------------------------------------------------------+------------+

Обратите внимание, что это будет работатьтолько если строка не содержит пробелов.

Если у вас есть пробелы, вам нужно сначала удалить их как

DECLARE @D VARCHAR(10) = '2019-01-01';

SELECT *, LEN(REPLACE(V, ' ', '')) - (LEN(REPLACE(REPLACE(V, ' ', ''), @D, '')) * 10) Occurrence
FROM (VALUES('A 2019-01-01 B 2019-01-01 C 2019-01-01 D 2019-01-01 E 2019-01-01 F 2019-01-01'))T(V);

Вы только что изменили свой вопрос, для поиска по имени пользователя, но поскольку ':' исправлено, и если у вас есть версия 2016+, вы можете сделать как

DECLARE @D VARCHAR(10) = 'UserName1';

SELECT *, 
      (SELECT COUNT(1) FROM STRING_SPLIT(V, ':') WHERE Value LIKE CONCAT('%', @D, '%'))
FROM (VALUES
      ('2019-06-15UserName1:YES I DID IT  2019-06-14UserName2:HHHHHHH  JJJJJJ   2019-06-14UserName1: COMMENT OF PEOPLE')
     ) T(V);

Наконец, я рекомендую переосмыслитьэтого дизайна, который является реальной проблемой здесь, и читайте больше о нормализации.


ОБНОВЛЕНИЕ:

Вот как подсчитать имя пользователя при присоединениидве таблицы

SELECT *,
       (
         SELECT COUNT(1) 
         FROM STRING_SPLIT(Col, ':') 
         WHERE Value LIKE CONCAT('%', UserName)
       ) Cnt
FROM Users U JOIN Data D
ON D.Col LIKE CONCAT('%', U.UserName, '%');

Возвращает:

+----------+----------------------------------------------+-----+
| UserName |                     Col                      | Cnt |
+----------+----------------------------------------------+-----+
| User1    | 2019-01-01User1:YES 2019-01-02User2:No       |   1 |
| User2    | 2019-01-01User1:YES 2019-01-02User2:No       |   1 |
| User1    | 2019-01-01User1:YES I 2019-01-02User1:No Way |   2 |
+----------+----------------------------------------------+-----+

Посмотрите, как это работает на живая демонстрация

1 голос
/ 15 июня 2019

Следующее будет делать, как вы просите, но вам серьезно нужно пересмотреть, как вы храните свои данные. Что, если вместо того, чтобы кто-то прокомментировал «Я сделал это», они ввели «Я сделал это 2019-01-01»?

-- DateCount
-- Return number of occurances of ####-##-## where # is a digit
create function dbo.DateCount(@s nvarchar(max)) 
returns int as
begin
  declare @k int = 0 -- @k holds the count so far
  declare @i int = 1 -- index into string, start at first character
  while @i < len(@s)-9 -- keep checking until we get to the end
    begin
      if substring(@s,@i,10) like '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
        set @k = @k + 1 -- increment count if these 10 characters match
      set @i = @i + 1 -- check the next character
    end
  return @k -- return the count
end
go
select dbo.DateCount(  '2019-06-15randomname1:YES I DID IT  2019-06-14random'
                     + 'name2:HHHHHHH  JJJJJJ   2019-06-14Urandomnamexxxxxx: '
                     + 'COMMENT OF PEOPLE'                                     )
-- Result is 3

Если вы хотите использовать решение на основе множеств вместо цикла while, вы можете попробовать это:

create function dbo.DateCount(@s nvarchar(max))
returns int as
begin
  declare @k int;
  with A as ( select 1 as I
              union all
              select I+1 as I from A where I<=len(@s)-9 )

  select @k=count(*) from A 
  where substring(@S,I,10) like '[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]'
  option (maxrecursion 0)

  return @k
end

Но в моих тестах производительности я обнаружил, что решение на основе наборов занимает на 50% больше времени.

1 голос
/ 15 июня 2019

Во-первых, у вас паршивая модель данных и обработка. Вы не должны просто добавлять подстроки в строку. Вы должны добавлять новые строки в таблицу. И вы не должны кодировать информацию в строку. Вы должны использовать столбцы для этого.

Мое самое сильное предложение - исправить вашу модель данных и обработку.

Тем не менее, вы можете застрять в этой ситуации. Самое простое решение - просто поискать

SELECT id, value,
       (LEN(REPLACE(value, 'XXXXXXXXXXXXX:', 'XXXXXXXXXXXXX:1') -
        LEN(value)
       ) as Num_Times
FROM Table;

Конечно, это предполагает, что 'XXXXXXXXXXXXX:' на самом деле не встречается в сообщении. Если это возможно, см. Мой оригинальный комментарий к структуре данных.

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