编写脚本以获取平均时间(以秒为单位),以首次响应指定语言。
-- Could not get this so that the user entered
-- the language in a box at the bottom working.
-- so you have to edit the code here.
declare @Language nvarchar(20) = 'php' -- ##LanguageTag##
declare @AnswerTime table (Id int, CreationDate date, AnswerTime date, Elapse bigint, Slot int )
insert @AnswerTime
Select Q.Id,
max(Q.CreationDate) AS CreateTime,
min(A.CreationDate) AS AnswerTime,
DATEDIFF(second, max(Q.CreationDate), min(A.CreationDate)) AS Elapse,
Year(max(Q.CreationDate))*12+Month(max(Q.CreationDate)) AS Slot
From Posts Q, Posts A
Where Q.PostTypeId = 1
and Q.Id = A.ParentId
and CHARINDEX(@Language, Q.Tags) != 0
Group by Q.Id, Q.Tags
Order by Elapse
Select min(CreationDate),
count(*) as [Count of Tickets],
-- min(Elapse) as [Min Time to Answer],
-- max(Elapse) as [Max Time to Answer],
avg(Elapse) as [Average time to first Answer]
From @AnswerTime
Group by Slot
它正在运行。
#1 楼
此查询中有一些关闭的内容。按某种顺序浏览它们:我仍然是“老式”联接的粉丝,但是CTE概念在SQL Server中是真正的赢家,所以,而不是创建表变量,只需使用CTE。
我可以告诉您,您的代码经过了一些迭代,因此,您可以处理一些“内容”。删除那些您不选择的内容(例如选择从未使用过的
min(A.CreationDate) as AnswerTime
。SEDE表需要一些习惯。您已经在标签列上完成了CharIndex。这很好,但是,您的示例php还引入了php5标签和cakephp。要解决此问题,您应该意识到SEDE将标签名称存储在标签字段的
<...>
大括号中,因此您要搜索<php>
。 > 使用CharIndex是可以的,但是更多标准将使用
like
,例如:... and Tags like '%<php>%'
我实际上已经完全放弃了使用标签字段,而是改为加入
不需要Question的CreationDate上的聚合列,因为
max(Q.CreationDate)
是多余的(每个Question ID只有一个CreationDate)。 执行SQL查询时,应始终使用尽可能多的常量(至少要尽可能多,直到y ou发现性能原因不要过度使用它。...),因此,即使其ParentID是Q的ID,您也应该添加
A.PostTypeId = 2
。我不喜欢您的“投放”方式数月之内的数据。我发现,从给定日期中减去月中少一天的日期会使日期返回到月初,然后截断时间,使您可以将所有日期精确地表示为月度:
Convert(Date, DateAdd(day, 1 - DatePart(day, Q.CreationDate), Q.CreationDate)) as CreationMonth
一旦我也使用了标签表,使变量替换更容易工作。愚蠢的“意外错误,已经分配了责任”错误是PITA,但现在已经消失了。
最后,Y轴的范围很大,以至于问题的数量显然没有吸引力/明显的。通过将刻度从“秒”更改为“小时”,刻度会更好。
我在这里派生了您的查询,这就是SQL(我试图使大小写和其他样式约定与您的代码保持一致) :
declare @Language nvarchar(25) = ##LanguageTag:string##;
declare @tagid int;
select @tagid = Id
from Tags
where TagName = @Language
print @Language + '->' + Convert(NVarchar(max), @tagid);
with AnswerTime as (
Select Q.Id,
Convert(Date, DateAdd(day, 1 - DatePart(day, Q.CreationDate), Q.CreationDate)) as CreationMonth,
Convert(float, DATEDIFF(second, Q.CreationDate, min(A.CreationDate))) AS Elapsed
From PostTags T,
Posts Q,
Posts A
Where T.TagId = @tagid
and T.PostId = Q.Id
and Q.PostTypeId = 1
and A.PostTypeId = 2
and Q.ClosedDate is null
and Q.Id = A.ParentId
Group by Q.Id,
Q.CreationDate,
Convert(Date, DateAdd(day, 1 - DatePart(day, Q.CreationDate), Q.CreationDate))
)
Select CreationMonth,
count(*) as [Count of Tickets],
avg(Elapsed)/3600.0 as [Average hours to first Answer]
From AnswerTime
Group by CreationMonth
编辑/更新
我在Stack Overflow上运行了查询,但是它失败了,因为有太多问题,
avg(Elapsed)
失败了算术溢出。我已更新查询,以仅考虑过去68年的数据...(以秒为单位的日期差异较大),并将经过的数据转换为浮点数(不会在avg()
上溢出)评论
\ $ \ begingroup \ $
您添加的未公开条件是一个好主意。
\ $ \ endgroup \ $
– 200_success
14年7月31日在17:53
#2 楼
我认为您的整体代码不错!没有什么比这特别有效的了。挑剔
我发现您的关键字格式不一致:我看到
declare
和Select
和DATEDIFF
等。虽然确实SQL不区分大小写,但这是一个好的做法要一致。只需选择自己喜欢的一个并坚持下去即可。老式的连接
From Posts Q, Posts A
...
and Q.Id = A.ParentId
自ANSI-92起已弃用,应避免使用。有许多避免的理由。
而是写:
From Posts Q
Inner Join Posts A
On Q.Id = A.ParentId
秒?
虽然秒是一个精确的指标,但我认为它们有些难以理解,用人类的话来说。我认为可能最好选择几小时(或至少几分钟)。
#3 楼
逻辑正确性c不是c ++; Java不是JavaScript。使用
CHARINDEX(…, Q.Tags)
检查以逗号分隔的字符串是一个坏主意。请改为对PostTags
表进行连接。请记住,此查询只为有答案的问题提供第一答案的时间。从某种意义上说,从未收到答案的问题的响应时间是无限的。您已隐式滤除了这些异常值,这可能不是好事。在任何情况下,均值可能会由于应答时间较长而偏斜。也许中位数可能比平均值更有趣。
您如何看待迁移的问题?
由于X4312079q不一定一定会影响图的X轴,因此会有一些抖动属于该月的第一天,尤其是该网站的早期。您可能希望将日期截断到月初,而不是选择每个
min(CreationDate)
的min(CreationDate)
。您想要几秒钟的回答时间?这是代码检查,而不是堆栈溢出。要在同一Y轴上获得有意义的首次回答时间和问题数量,您需要以天为单位来衡量回答时间!
表达力
首选公用表表达式而不是变量。将整个查询表述为一个
Slot
更为优雅,并为查询计划者提供了更大的自由度。您不应在中间查询中放置一个
SELECT
。它不能保证最终结果的排序类似。我将使用两个CTE编写此代码:一个收集所有相关的问题-答案对,另一个计算时间跨度。函数调用的大小写一致。
标签是标签。不论它是一个语言标签都没有关系,因此我不会命名变量或将输入字段标记为“语言”。
建议的解决方案
WITH FirstAnswers AS (
SELECT Q.Id
, Q.CreationDate AS QuestionTime
, min(A.CreationDate) AS AnswerTime
FROM Posts AS Q
INNER JOIN Posts AS A
ON Q.Id = A.ParentId
WHERE EXISTS (
SELECT PostId
FROM PostTags
INNER JOIN Tags
ON TagId = Tags.Id
WHERE PostId = Q.Id AND TagName = ##Tag:string?php##
)
GROUP BY Q.Id, Q.CreationDate
), AnswerTime AS (
SELECT Id
, QuestionTime
, AnswerTime
, datediff(second, QuestionTime, AnswerTime) / 86400.0 AS ElapsedDays
, dateadd(m, datediff(m, 0, QuestionTime), 0) AS Month
FROM FirstAnswers
)
SELECT Month
, count(QuestionTime) AS [Question count]
, avg(ElapsedDays) AS [Average time to first answer (Days)]
FROM AnswerTime
GROUP BY Month
ORDER BY Month;
Stack Exchange数据资源管理器链接
#4 楼
代码看起来不错,可以做的改进很少:避免在charindex
子句中使用像WHERE
这样的函数/运算符,因为它将对每个单个值执行,随着时间的推移会降低性能。数据将会增加。避免使用交叉
JOIN
。相反,请指定要执行的JOIN
类型,否则首先将其加入每一行,然后它们将应用WHERE
子句。我使用LEFT JOIN
确保您得到正确的结果。 再读一遍有关temp表和表变量的信息。
SELECT A.Id ,
A.Tags ,
MAX(A.CreationDate) AS CreateTime ,
MIN(A.AnswerCreationDate) AS AnswerTime ,
DATEDIFF(second, MAX(A.CreationDate), MIN(A.CreationDate)) AS Elapse ,
YEAR(MAX(A.CreationDate)) * 12 + MONTH(MAX(A.CreationDate)) AS Slot
FROM ( SELECT Q.Id ,
Q.Tags ,
Q.CreationDate ,
A.CreationDate AS AnswerCreationDate ,
CHARINDEX(@Language, Q.Tags) languageIndex
FROM Posts Q
LEFT JOIN Posts A ON Q.Id = A.ParentId
WHERE Q.PostTypeId = 1
) A
WHERE A.languageIndex != 0
GROUP BY Q.Id ,
Q.Tags
ORDER BY Elapse
评论
\ $ \ begingroup \ $
您刚刚将CHARINDEX从WHERE子句移到SELECT部分。这真的会改变效率吗?
\ $ \ endgroup \ $
–马丁·约克
14年7月31日在17:23
\ $ \ begingroup \ $
是的,可以肯定,因为select就像对数据应用包装器一样
\ $ \ endgroup \ $
– Paraitosh
2014年7月31日在17:25
#5 楼
跳到我身上的一件事是WHERE Q.Id = A.ParentId
您可以
JOIN
代替该链接上的表格。格式化明智,(而且您可能已经知道这一点,大多数人觉得这有点乏味)是确保所有关键字都大写,以便于阅读。
#6 楼
我只想添加到@rolfl的答案中。我完全不喜欢CTE。它们几乎总是比使用临时表或从子查询中选择要慢。CTE通常也比表变量慢,至少以我的SQL Server经验来看,尽管没有什么理由使用如果您的行数不多,则使用表变量。
我很自由地从ROFL的答案中获取代码,并将CTE更改为直接从中选择的子查询,这大大提高了速度,从〜60ms降低到〜30ms。
这是我的改动:
declare @Language nvarchar(25) = ##LanguageTag:string##;
declare @tagid int;
declare @epoch datetime = DateDiff(yy, -68, current_timestamp);
select @tagid = Id
from Tags
where TagName = @Language
print @Language + '->' + Convert(NVarchar(max), @tagid);
Select CreationMonth,
count(*) as [Count of Tickets],
avg(Elapsed)/3600.0 as [Average hours to first Answer]
From
(
Select Q.Id,
Convert(Date, DateAdd(day, 1 - DatePart(day, Q.CreationDate), Q.CreationDate)) as CreationMonth,
Convert(float, DATEDIFF(second, Q.CreationDate, min(A.CreationDate))) AS Elapsed
From PostTags T,
Posts Q,
Posts A
Where T.TagId = @tagid
and T.PostId = Q.Id
and Q.PostTypeId = 1
and A.PostTypeId = 2
and Q.ClosedDate is null
and Q.Id = A.ParentId
and A.CreationDate > Q.CreationDate -- validation, and also merges.
and Q.CreationDate >= @epoch
and A.CreationDate < CURRENT_TIMESTAMP
Group by Q.Id,
Q.CreationDate,
Convert(Date, DateAdd(day, 1 - DatePart(day, Q.CreationDate), Q.CreationDate))
) As AnswerTime
Group by CreationMonth
order by CreationMonth
这是可行的
评论
我冒昧地让您的参数启动并运行;)您可能对此快速解答比率图表感兴趣
@rolfl:我在做实验的时候就想到了。我只是想在实验中取得一些微不足道的东西。