我有一个SQL查询,它返回关于单个团队的下表:
date gameid pointsfor pointsagainst
2011-03-20 15 1 10
2011-03-27 17 7 3
2011-04-03 23 6 5
2011-04-10 30 5 4
2011-04-17 35 4 8
2011-05-01 38 8 1
2011-05-08 43 3 7
2011-05-15 48 6 2
2011-05-22 56 10 2
2011-05-29 59 4 5
2011-06-05 65 2 3
2011-06-19 71 5 6
2011-06-19 74 12 2
2011-06-19 77 5 2
2011-06-19 80 5 4
从这张表中,有人可以帮我计算最长的输赢情节吗?
我在这里看了几个其他的例子但是很难跟随它们,因为它们和我的不一样。任何帮助将不胜感激。谢谢!
答案 0 :(得分:2)
您必须实现一些MySQL变量来帮助更有效地处理这个问题,而不是多个查询/加入/分组。这只有一次通过所有记录,然后再次获得每种类型的赢/输(或平局)的最大值。我假设您提供的数据是一次性的,并且日期是游戏的明显顺序......积分是您感兴趣的团队,并且反对的是对手的任何人是。那就是说,我的别名" name将是" YourResultSingleTeam"。
内部查询将预先确定游戏的状态为" W" in或" L" oss。然后,查看该值是否与团队的上一个实例相同。如果是,请将1添加到现有的赢/输计数器。如果没有,请将计数器设置回1.然后,将当前游戏的状态保留回" LastStatus"用于与下一场比赛进行比较的价值。
在完成之后,它是一个简单的游戏结果,max()按游戏结果状态分组
select
StreakSet.GameResult,
MAX( StreakSet.WinLossStreak ) as MaxStreak
from
( select YR.Date,
@CurStatus := if( YR.PointsFor > YR.PointsAgainst, 'W', 'L' ) as GameResult,
@WinLossSeq := if( @CurStatus = @LastStatus, @WinLossSeq +1, 1 ) as WinLossStreak,
@LastStatus := @CurStatus as carryOverForNextRecord
from
YourResultSingleTeam YR,
( select @CurStatus := '',
@LastStatus := '',
@WinLossSeq := 0 ) sqlvars
order by
YR.Date ) StreakSet
group by
StreakSet.GameResult
正如Nikola所提出的,如果你想考虑" tie"游戏,我们可以通过将@CurStatus更改为case / when condition to
来进行调整@CurStatus := case when YR.PointsFor > YR.PointsAgainst then 'W'
when YR.PointsFor < YR.PointsAgainst then 'L'
else 'T' end as GameResult,
答案 1 :(得分:1)
有一个解决方案,但我不认为你会喜欢它,因为它需要一个自我连接,你的表不是一个表而是查询。
内部查询将日期转换为范围 - 对于表格中的每个日期,它将找到具有不同战胜的第一个日期,或者,对于上一个游戏,它将是该游戏的日期。这些数据将在不同条纹的第一个日期汇总,以平整和计算条纹;然后外部查询按结果查找极值。
select case Outcome
when -1 then 'Losses'
when 1 then 'Wins'
else 'Undecided'
end Title
, max(Streak) Streak
from
(
select min(date) date, date_to, Outcome, count(*) Streak
from
(
select t1.date,
sign (t1.pointsfor - t1.pointsagainst) Outcome,
ifnull (min(t2.date), t1.date) date_to
from table1 t1
left join table1 t2
on t1.date < t2.date
and sign (t1.pointsfor - t1.pointsagainst)
<> sign (t2.pointsfor - t2.pointsagainst)
group by t1.date, sign (t1.pointsfor - t1.pointsagainst)
) a
group by date_to, Outcome
) a
group by Outcome
为了避免需要替换table1
- 可能 - 繁琐的查询,您可以使用临时表,或者在辅助表中使用适当格式的数据。
在Sql fiddle进行实时测试,以及可能表现更好的另一个子查询驱动版本 - 你应该尝试它们。
答案 2 :(得分:1)
MySQL没有CTE或窗口函数(例如SUM OVER,ROW_NUMBER OVER等)。但它有一个救赎因素。变量!
使用此:
select
min(date) as start_date,
max(date) as end_date,
count(date) as streak,
group_concat(gameid) as gameid_list
from
(
select *,
IF(
pointsfor > pointsagainst
and
@pointsfor > @pointsagainst,
@gn, @gn := @gn + 1)
as group_number,
@date as old_date, @gameid as old_gameid,
@pointsfor as old_pointsfor,
@pointsagainst as old_pointsagainst,
@date := date, @gameid := gameid,
@pointsfor := pointsfor, @pointsagainst := pointsagainst
from tbl
cross join
(
select
@date := CAST(null as date) as xa,
@gameid := null + 0 as xb, -- why CAST(NULL AS INT) doesn't work?
@pointsfor := null + 0 as xc, @pointsagainst := null + 0 as xd, @gn := 0
) x
order by date
) as y
group by group_number
order by streak desc;
输出:
START_DATE END_DATE STREAK GAMEID_LIST
March, 27 2011 08:00:00-0700 April, 10 2011 08:00:00-0700 3 17,23,30
June, 19 2011 08:00:00-0700 June, 19 2011 08:00:00-0700 3 74,77,80
May, 15 2011 08:00:00-0700 May, 22 2011 08:00:00-0700 2 48,56
March, 20 2011 08:00:00-0700 March, 20 2011 08:00:00-0700 1 15
April, 17 2011 08:00:00-0700 April, 17 2011 08:00:00-0700 1 35
May, 01 2011 08:00:00-0700 May, 01 2011 08:00:00-0700 1 38
May, 08 2011 08:00:00-0700 May, 08 2011 08:00:00-0700 1 43
May, 29 2011 08:00:00-0700 May, 29 2011 08:00:00-0700 1 59
June, 05 2011 08:00:00-0700 June, 05 2011 08:00:00-0700 1 65
June, 19 2011 08:00:00-0700 June, 19 2011 08:00:00-0700 1 71
实时测试:http://www.sqlfiddle.com/#!2/bbe78/8
请注意我在sqlfiddle上的解决方案,它有两个查询。 1.模拟在顶部。 2.下面的最终查询
答案 3 :(得分:0)
你在这里处理的是跟踪胜利和失败的趋势,这需要使用运行计数器的某种循环来计算,而不是在SQL中。 SQL查询处理单个行,分组,排序等;您正在尝试使用一种无意解决此类问题的语言。
答案 4 :(得分:0)
每次想要获得最长的条纹时,你必须创建一个光标,读取所有行,计算数据......
我建议一种可以让事情变得更轻松的解决方法。 您在表“streakFor”中添加了一列。每次插入一行时:
//pseudo code
if pointsFor > pointsAgainst
if last_streakFor > 0
then streakFor++
else streakFor = 1
else
if last_streakFor > 0
then streakFor = -1
else streakFor--
last_streakFor是最后插入的行中的streakFor 然后插入带有streakFor列的行
现在你可以
select max(streakFor) from yourTable where yourConditions
这将为你带来“pointsFor”的最长连胜纪录和“pointsAgainst”最长的连败纪录select min(streakFor) from yourTable where yourConditions
将为你带来“积极反对”最长连胜纪录和“积分兑换”最长连胜纪录答案 5 :(得分:0)
感谢所有帮助人员。我按照建议最终使用php循环。万一有人想知道,这是我的代码:
$streakSQL = "SELECT date, gameid, pointsfor, pointsagainst FROM result WHERE teamid = ".$_GET['teamid']." AND bye = 0 AND COMPLETED = 1 AND seasonid > 7 AND roundwd = 0 AND (pointsfor != 0 OR pointsagainst != 0)";
$streak = mysql_query($streakSQL);
$winstreak = 0;
$maxwinstreak = 0;
$losestreak = 0;
$maxlosestreak = 0;
while($streakRow = mysql_fetch_array($streak))
{
//calculate winning streak
if($streakRow['pointsfor'] > $streakRow['pointsagainst'])
{
$winstreak++;
if($winstreak > $maxwinstreak)
{
$maxwinstreak = $winstreak;
}
}
else{ $winstreak = 0; }
//calculate losing streak
if($streakRow['pointsfor'] < $streakRow['pointsagainst'])
{
$losestreak++;
if($losestreak > $maxlosestreak)
{
$maxlosestreak = $losestreak;
}
}
else{ $losestreak = 0; }
}
echo "Biggest Winning Streak: ".$maxwinstreak;
echo "<br />Biggest Losing Streak: ".$maxlosestreak;
答案 6 :(得分:0)
最新版本的MySQL具有CTE,并且具有窗口功能。
这是一个解决方案。
第一步,通过为他们分配自己的streak_group号来分组获胜和失败:
with t as
(
select
*,
pointsfor - pointsagainst > 0 is_winner,
case when pointsfor - pointsagainst > 0
and lag(pointsfor) over(order by date, pointsfor - pointsagainst desc)
- lag(pointsagainst) over(order by date, pointsfor - pointsagainst desc) > 0
then
0
else
1
end as is_new_group
from tbl
)
select *, sum(is_new_group) over(order by date, pointsfor - pointsagainst desc) as streak_group
from t
输出:
date |gameid |pointsfor |pointsagainst |is_winner |is_new_group |streak_group |
--------------------|-------|----------|--------------|----------|-------------|-------------|
2011-03-20 15:00:00 |15 |1 |10 |0 |1 |1 |
2011-03-27 15:00:00 |17 |7 |3 |1 |1 |2 |
2011-04-03 15:00:00 |23 |6 |5 |1 |0 |2 |
2011-04-10 15:00:00 |30 |5 |4 |1 |0 |2 |
2011-04-17 15:00:00 |35 |4 |8 |0 |1 |3 |
2011-05-01 15:00:00 |38 |8 |1 |1 |1 |4 |
2011-05-08 15:00:00 |43 |3 |7 |0 |1 |5 |
2011-05-15 15:00:00 |48 |6 |2 |1 |1 |6 |
2011-05-22 15:00:00 |56 |10 |2 |1 |0 |6 |
2011-05-29 15:00:00 |59 |4 |5 |0 |1 |7 |
2011-06-05 15:00:00 |65 |2 |3 |0 |1 |8 |
2011-06-19 15:00:00 |74 |12 |2 |1 |1 |9 |
2011-06-19 15:00:00 |77 |5 |2 |1 |0 |9 |
2011-06-19 15:00:00 |80 |5 |4 |1 |0 |9 |
2011-06-19 15:00:00 |71 |5 |6 |0 |1 |10 |
最终查询。计算获胜的连胜数:
with t as
(
select
*,
pointsfor - pointsagainst > 0 is_winner,
case when pointsfor - pointsagainst > 0
and lag(pointsfor) over(order by date, pointsfor - pointsagainst desc)
- lag(pointsagainst) over(order by date, pointsfor - pointsagainst desc) > 0
then
0
else
1
end as is_new_group
from tbl
)
, streak_grouping as
(
select
*, sum(is_new_group) over(order by date, pointsfor - pointsagainst desc) as streak_group
from t
)
select
min(date) as start_date,
max(date) as end_date,
count(*) as streak,
group_concat(gameid order by gameid) as gameid_list
from streak_grouping
group by streak_group
order by streak desc, start_date
输出:
start_date |end_date |streak |gameid_list |
--------------------|--------------------|-------|------------|
2011-03-27 15:00:00 |2011-04-10 15:00:00 |3 |17,23,30 |
2011-06-19 15:00:00 |2011-06-19 15:00:00 |3 |74,77,80 |
2011-05-15 15:00:00 |2011-05-22 15:00:00 |2 |48,56 |
2011-03-20 15:00:00 |2011-03-20 15:00:00 |1 |15 |
2011-04-17 15:00:00 |2011-04-17 15:00:00 |1 |35 |
2011-05-01 15:00:00 |2011-05-01 15:00:00 |1 |38 |
2011-05-08 15:00:00 |2011-05-08 15:00:00 |1 |43 |
2011-05-29 15:00:00 |2011-05-29 15:00:00 |1 |59 |
2011-06-05 15:00:00 |2011-06-05 15:00:00 |1 |65 |
2011-06-19 15:00:00 |2011-06-19 15:00:00 |1 |71 |