我有一个表,该表在用户对数据库进行更改时存储信息。我想提取用户对应用程序上的日期进行更改的次数。通常将每个用户的信息存储在一行中,例如:
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个值的问题:
答案 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%。