有一个存储IIS日志的简单表。
表模式如下:
CREATE TABLE [dbo].[TBL_iisLog](
[cdate] [varchar](50) NULL,
[ctime] [varchar](50) NULL,
[serverip] [varchar](50) NULL,
[uri] [varchar](255) NULL,
[port] [varchar](50) NULL,
[username] [varchar](50) NULL,
[clientip] [varchar](50) NULL,
[useragent] [nvarchar](max) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
请注意,我已在以下字段中添加了这三个索引:
username
clientip
cdate
和ctime
)对于该表,以下查询执行大约需要50-60秒:
SELECT TOP 10
username Usename,
(SELECT COUNT(DISTINCT SUBSTRING(useragent, PATINDEX('%SIG:+%',useragent) + 5, 36))
FROM tbl_iislog AS SoftwareSignatureCountTempTable
WHERE (PATINDEX('%SIG:+%',useragent) > 0)
AND SoftwareSignatureCountTempTable.username = MainTBL.username
) AS SoftwareSignatureCount
,
(SELECT COUNT(DISTINCT
LEFT(useragent, IIF((PATINDEX('%VDB%',useragent) -1) > 0, PATINDEX('%VDB%',useragent) -1, 0))
+
RIGHT(useragent, IIF((PATINDEX('%BPC%',useragent) -1) > 0, PATINDEX('%BPC%',useragent) -1, 0))
)
FROM TBL_iislog UseragentTempTable
WHERE UseragentTempTable.username = MainTBL.username
) AS UserAgentCount
,
(SELECT COUNT(DISTINCT clientip)
FROM tbl_iislog AS IPTempTable
WHERE IPTempTable.username = MainTBL.username
) AS IPCount
,
(SELECT COUNT(clientip)
FROM tbl_iislog ConnectionsTempTable
WHERE ConnectionsTempTable.uri = '/version_checker.ver'
AND ConnectionsTempTable.username = MainTBL.username
) AS Connections
FROM TBL_iisLog AS MainTBL
WHERE (username LIKE 'softgsg-%') OR (username LIKE 'sg-%')
GROUP BY username HAVING COUNT(clientip) > 0
ORDER BY SoftwareSignatureCount DESC, Connections DESC
我会感谢任何帮助我优化查询的建议。
答案 0 :(得分:2)
首先,其他人评论并且确实......查询是一团糟,并且有很多字段级选择语句可以杀死性能,因为每个查询都是针对每个记录执行的,每个列都是您正在运行的。它们都基于当前用户的任何内容,恰好是某些标准的IIF()。我已经简化了,可能需要一点点调整,但是应该给你很大的帮助。
首先,作为HAVING子句基础的特定clientIP的计数与该列无关,但最好只用*表示无论特定列如何都存在记录。此外,在同一查询级别中,通过执行SUM(IIF())可以处理您的版本检查器“连接”计数。
对于您的签名条目,我在同一次运行中做了两次。首先,我通过MAX(IIF())获取一个标志,看看是否有任何记录没有'%SIG:+%'引用,然后还得到一个COUNT(与该签名发现截然不同)。这样,COUNT 减少任何非签名(这将被视为空格''因此,无论有多少非“SIG”条目将给予ACTUAL最终计数,所以最多扣除1个条目。
我也不知道为什么你有大量的IP数据> 0 ...从IIS日志文件中,一切都应该有一个客户端IP,所以我认为这是无关紧要的,因此被删除。
我还认为我严重简化了您的用户代理字段。如果找不到'%VDB%'或“%BPC%”条目,只需使用空字符串,否则获取每个字符串的子字符串上下文。
所以,现在只需一次通过表,所有聚合完成ONCE应该有所帮助。现在,索引。因为你在where子句中使用了LIKE,但好的是你不喜欢领先的'%',否则表明你正在寻找字符串“softgsg-”或“sg-”字符串中的任何地方,你是在字符串的LEADING部分寻找它,可以通过索引进行优化。
话虽如此,我会在
表上有一个复合索引(用户名,clientip)
由于您的列处理255甚至MAX列宽文本,我不想仅仅为了使其成为“覆盖”索引而终止索引。
select
PQ.*
from
( SELECT
L.username,
COUNT(*) TotalRecs,
COUNT(DISTINCT clientip) DistinctIPs,
SUM( IIF( L.uri = '/version_checker.ver', 1, 0 )) as Connections,
MAX( IIF( PATINDEX('%SIG:+%', L.useragent) = 0, 1, 0 )) as NonSigEntries,
COUNT( DISTINCT IIF( PATINDEX('%SIG:+%', L.useragent) = 0, ' ',
SUBSTRING( L.useragent, PATINDEX('%SIG:+%', L.useragent) + 5, 36))) SoftwareSignatureCount,
COUNT( DISTINCT
IIF(PATINDEX('%VDB%', L.useragent) = 0, '', LEFT( L.useragent, PATINDEX('%VDB%', L.useragent) -1))
+ IIF(PATINDEX('%BPC%', L.useragent) = 0, '', RIGHT( L.useragent, PATINDEX('%BPC%', L.useragent) -1))
) AS UserAgentCount
FROM
tbl_iislog L
WHERE
L.username LIKE 'softgsg-%'
OR L.username LIKE 'sg-%'
GROUP BY
L.username ) PQ
ORDER BY
PQ.SoftwareSignatureCount - PQ.NonSigEntries DESC,
PQ.Connections DESC
查询运行时,我根据查询提供的空表结构进行测试。由于顺序,我预先包装了查询(别名PQ for PreQuery),所以我会得到软件签名计数和非签名条目标志的最终值,然后连接,所以我没有重新复制相同的COUNT(复杂语句)到字段列表和顺序中...有些引擎允许您使用列结果名称别名与整个表达式。