SQL脚本以查找计算:username字符串的出现次数

时间:2019-06-15 11:07:55

标签: sql sql-server tsql

我有一个表,该表在用户对数据库进行更改时存储信息。我想提取用户对应用程序上的日期进行更改的次数。通常将每个用户的信息存储在一行中,例如:

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

我想要的是搜索:username来检测用户已更改了多少次。在这种情况下。答案应该是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 个答案:

答案 0 :(得分:2)

据我了解,您想计算字符串中日期的出现

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 |
+----------+----------------------------------------------+-----+

查看其在 live demo

上的工作方式

答案 1 :(得分:1)

首先,您有一个糟糕的数据模型和处理程序。您不应该只是在字符串中添加子字符串。您应该将新行添加到表中。并且,您不应该在字符串中编码信息。您应该为此使用列。

我最强烈的建议是您修复数据模型和处理。

也就是说,您可能会遇到这种情况。最简单的解决方案就是寻找

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

当然,这假定'XXXXXXXXXXXXX:'实际上不在消息中出现。如果可能的话,请参阅我对数据结构的原始注释。

答案 2 :(得分:1)

以下内容将按照您的要求进行,但是您非常需要重新考虑如何存储数据。如果他们没有输入“我在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%。