MySQL 数学查询问题未产生预期输出
MySQL Query Issues with Math not resulting in expected output
概览:
我在本地 运行 构建了一个应用程序,它允许我跟踪孩子们每天的家务和行为。该系统具有我可以分配给它们的消极和积极行为,这些行为对应于 100
点量表上的点值。
逻辑:
- 查询仅查看当天来计算积分。如果收视率是前一天收到的,则这些收视率不会计入他们的每日总计。
- 100 分是 child 当天可以拥有的最大值,即使他们的收视率导致他们超过了这一点,它也总是 return 为
100
。
- 如果他们当天没有任何评分(正面或负面),系统会将他们的分数默认为起点
100
。
- 当他们获得分数时,他们的总分数将相应调整,根据为行为设置的值上升或下降。
场景:
- 没有任何收视率的新一天意味着 child 从
100
点开始。他们收到具有 -3
值的负面行为。这将 return 他们的 totalPoints
作为 97
.
- 以上 child 获得正面评价,价值
2
分,这使他们的 totalPoints
达到 99
。
- 他们又获得了
5
分的好评。由于我们的最大值为 100,因此我们会 return 他们的 totalPoints
为 100
,而不管它超出了多少 100
。
问题:
我构建了查询并认为一切正常,但它似乎存在一个轻微的数学问题。当 child 获得 -3
点评级时,他们达到了预期的 97
。然后我给了他们一个积极的 4
,这使他们的分数达到 99
而不是我预期的 100
。
查询:
SELECT c.id,
c.NAME,
Date_format(From_days(Datediff(CURRENT_DATE, c.age)),
'%y Years %m Months %d Days') AS age,
c.photoname,
c.photonamesmall,
(SELECT CASE
WHEN ( Ifnull(Sum(t.points), (SELECT settingvalue
FROM settings
WHERE settingname = 'MaxPoints')
) >= (
SELECT
settingvalue
FROM
settings
WHERE
settingname = 'MaxPoints') ) THEN 100
WHEN ( Sum(t.points) <= 0 ) THEN ( (SELECT settingvalue
FROM settings
WHERE settingname =
'MaxPoints')
+ Sum(t.points) )
ELSE ( (SELECT settingvalue
FROM settings
WHERE settingname = 'MaxPoints') -
Ifnull(Sum(t.points), (SELECT
settingvalue
FROM settings
WHERE
settingname = 'MaxPoints')) )
END
FROM behaviorratings AS r
JOIN behaviortypes AS t
ON r.behaviorid = t.behaviortypeid
WHERE r.childid = c.id
AND Date_format(r.timestamp, '%Y-%m-%d') = Curdate()) AS
totalPoints,
(SELECT definitionname
FROM behaviordefinitions AS d
WHERE totalpoints BETWEEN d.min AND d.max) AS
behaviorRating
FROM children AS c
Fiddle:
这里是 link 到 SQL fiddle: http://sqlfiddle.com/#!9/fa06c/1/0
我希望看到 Child 2 (Brynlee) 的结果是 100
而不是 99
。
她从 100
开始,收到 -3
并收到 +4
。虽然我知道这个操作顺序的数学是正确的,但我需要对其进行调整以反映我期望的反映方式。 100 - 3 = 97
然后是 97 + 4 = 101
(我们在 100
处达到最大值,所以 100
将是 totalPoints
。
试试这个
SELECT c.id,
c.name,
DATE_FORMAT(
FROM_DAYS(
DATEDIFF(CURRENT_DATE, c.age)
),
'%y Years %m Months %d Days'
) AS age,
c.photoName,
c.photoNameSmall,
(SELECT CASE
WHEN ( Ifnull(Sum(t.points), 0
) + (SELECT settingValue
FROM settings
WHERE settingName = 'MaxPoints') >= (
SELECT
settingValue
FROM
settings
WHERE
settingName = 'MaxPoints') ) THEN 100
WHEN ( Sum(t.points) <= 0 ) THEN ( (SELECT settingValue
FROM settings
WHERE settingName =
'MaxPoints')
+ Sum(t.points) )
ELSE ( (SELECT settingValue
FROM settings
WHERE settingName = 'MaxPoints') -
Ifnull(Sum(t.points), (SELECT
settingvalue
FROM settings
WHERE
settingName = 'MaxPoints')) )
END
FROM behaviorRatings AS r
JOIN behaviorTypes AS t
ON r.behaviorID = t.behaviorTypeID
WHERE r.childid = c.id
AND Date_format(r.timestamp, '%Y-%m-%d') = Curdate()) AS
totalPoints,
(SELECT definitionName
FROM behaviorDefinitions AS d
WHERE totalPoints BETWEEN d.min AND d.max) AS
behaviorRating
FROM children AS c
基本上,使用
WHEN ( Ifnull(Sum(t.points), (SELECT settingvalue
FROM settings
WHERE settingname = 'MaxPoints')
)
当 sum(t.points) 为 null 时, 只会给你 100。要获得总分,您需要做
Ifnull(Sum(t.points), 0) + (SELECT settingvalue
FROM settings
WHERE settingname = 'MaxPoints')
这个sql可能更容易看
SET @maxPoints := (SELECT settingValue
FROM settings
WHERE settingName = 'MaxPoints');
SELECT c.id,
c.name,
DATE_FORMAT(
FROM_DAYS(
DATEDIFF(CURRENT_DATE, c.age)
),
'%y Years %m Months %d Days'
) AS age,
c.photoName,
c.photoNameSmall,
(SELECT CASE
WHEN ( Ifnull(Sum(t.points), 0) + @maxPoints > @maxPoints ) THEN 100
ELSE ( Ifnull(Sum(t.points), 0) + @maxPoints )
END
FROM behaviorRatings AS r
JOIN behaviorTypes AS t
ON r.behaviorID = t.behaviorTypeID
WHERE r.childid = c.id
AND Date_format(r.timestamp, '%Y-%m-%d') = Curdate()) AS
totalPoints,
(SELECT definitionName
FROM behaviorDefinitions AS d
WHERE totalPoints BETWEEN d.min AND d.max) AS
behaviorRating
FROM children AS c
以 50 为起点:
SET @maxPoints := (SELECT settingValue
FROM settings
WHERE settingName = 'MaxPoints');
SET @startingPoint := 50;
SELECT c.id,
c.name,
DATE_FORMAT(
FROM_DAYS(
DATEDIFF(CURRENT_DATE, c.age)
),
'%y Years %m Months %d Days'
) AS age,
c.photoName,
c.photoNameSmall,
(SELECT CASE
WHEN ( Ifnull(Sum(t.points), 0) + @startingPoint > @maxPoints ) THEN 100
ELSE ( Ifnull(Sum(t.points), 0) + @startingPoint )
END
FROM behaviorRatings AS r
JOIN behaviorTypes AS t
ON r.behaviorID = t.behaviorTypeID
WHERE r.childid = c.id
AND Date_format(r.timestamp, '%Y-%m-%d') = Curdate()) AS
totalPoints,
(SELECT definitionName
FROM behaviorDefinitions AS d
WHERE totalPoints BETWEEN d.min AND d.max) AS
behaviorRating
FROM children AS c
Sql 总点数超过限制后申请封顶
SET @maxPoints := (SELECT settingValue
FROM settings
WHERE settingName = 'MaxPoints');
SET @startingPoint := 50;
SELECT
c.id,
c.name,
DATE_FORMAT(
FROM_DAYS(DATEDIFF(CURRENT_DATE, c.age)), '%y Years %m Months %d Days') AS age,
c.photoName,
c.photoNameSmall,
(
select x.tp
from
(
SELECT t.childid,
@rn:=CASE WHEN @cid <> t.childid THEN 0 ELSE @rn+1 END AS rn,
@startingPoint + @tp:= CASE
WHEN @cid <> t.childid
THEN ifnull(t.points, 0)
ELSE (
case when @startingPoint + t.points + @tp > @maxPoints
then @maxPoints - @startingPoint
else t.points + @tp end)
END AS tp,
@cid:=t.childid AS clset,
t.timestamp
FROM
(SELECT @tp:= -1) p,
(SELECT @rn:= -1) n,
(SELECT @cid:= -1) cd,
(
SELECT r.childid, t.points, r.timestamp
FROM behaviorRatings AS r
JOIN behaviorTypes AS t ON r.behaviorID = t.behaviorTypeID
ORDER BY r.childid, r.timestamp
) t
) x
where x.childid = c.id AND Date_format(x.timestamp, '%Y-%m-%d') = Curdate()
order by x.childid, x.rn desc
limit 1
) AS totalPoints,
(
SELECT definitionName
FROM behaviorDefinitions AS d
WHERE totalPoints BETWEEN d.min AND d.max
) AS behaviorRating
FROM children AS c
不要让事情变得更复杂。为您的任务选择正确的语言。在你的情况下是 PHP:
$query = "select settingValue from settings where settingName = 'MaxPoints'";
$result = $this->db->query($query);
$row = $result->fetchAssoc();
$maxPoints = $row['settingValue'];
$query = "select * from children";
$result = $this->db->query($query);
$children = array();
while ($row = $result->fetchAssoc()) {
$row['totalPoints'] = $maxPoints;
$children[$row['id']] = $row;
}
$query = "
select c.id, coalesce(bt.points, 0) as points
from children c
join behaviorRatings br on br.childID = c.id
join behaviorTypes bt on bt.behaviorTypeID = br.behaviorID
where date(br.timestamp) = current_date()
order by c.id, br.timestamp
";
$result = $this->db->query($query);
while ($row = $result->fetchAssoc()) {
$childId = $row['id'];
$totalPoints = $children[$row['id']]['totalPoints'];
$totalPoints = $totalPoints + $row['points'];
$totalPoints = min($totalPoints, $maxPoints);
$children[$row['id']]['totalPoints'] = $totalPoints;
}
var_dump($children);
所有获取总分的逻辑都在最后一个循环中。现在将其与您的查询进行比较。
但是 - 如果您更改规则,允许在一天中超过限制并仅在一天结束时削减点数,这可以在单个查询中完成:
select c.*, sub.totalPoints, bd.definitionName
from (
select c.id, least(100+coalesce(sum(bt.points), 0), mp.settingValue) as totalPoints
from children c
join settings mp on settingName = 'MaxPoints'
left join behaviorRatings br
on br.childID = c.id
and date(br.timestamp) = current_date()
left join behaviorTypes bt on bt.behaviorTypeID = br.behaviorID
group by c.id
) sub
join children c on c.id = sub.id
join behaviorDefinitions bd on sub.totalPoints between bd.min and bd.max
http://sqlfiddle.com/#!9/fa06c/71
虽然这不是一个简单的查询,但它远没有您尝试的那么复杂。公认的解决方案是忽略规则,即每次获得点数时总点数必须减少到 100 (Sum(t.points)
)。
正如我在评论中所写:要遵循该规则,您需要进行某种迭代。 MySQL 中有一个使用用户变量的技巧:
select c.id, c.name, sub.totalPoints, bd.definitionName
from (
select sub.childId, sum(sub.cuttedPoints) + sp.settingValue as totalPoints
from (
select
@points := coalesce(bt.points,0) as points,
@lastTotalPoints := case when (c.id = @childId)
then @totalPoints
else sp.settingValue
end lastTotalPoints,
@totalPoints := least(@lastTotalPoints + @points, mp.settingValue) as totalPoints,
@totalPoints - @lastTotalPoints as cuttedPoints,
@childId := c.id as childId
from children c
join settings sp on sp.settingName = 'StartPoints'
join settings mp on mp.settingName = 'MaxPoints'
left join behaviorRatings br
on br.childID = c.id
and date(br.timestamp) = current_date()
left join behaviorTypes bt on bt.behaviorTypeID = br.behaviorID
cross join (select @childId := null) init_var
order by c.id, br.timestamp
) sub
join settings sp on sp.settingName = 'StartPoints'
group by sub.childId
) sub
join children c on c.id = sub.childId
join behaviorDefinitions bd on sub.totalPoints between bd.min and bd.max
结果(Brynlee 行为:+4 -3 +4 -3):
| id | name | totalPoints | definitionName |
|----|---------|-------------|------------------------|
| 2 | Brynlee | 97 | Having an amazing day! |
| 1 | Maya | 100 | Having an amazing day! |
Brynlee 获得 97 分(+4 => 100,-3 => 97,+4 => 100,-3 => 97)
http://sqlfiddle.com/#!9/751c51/28
如果您将新设置 "StartPoints" 更改为 50,您将获得:
| id | name | totalPoints | definitionName |
|----|---------|-------------|------------------|
| 2 | Brynlee | 52 | Not looking good |
| 1 | Maya | 50 | Not looking good |
此处 Brynlee 得到 52 分,因为从未达到 100 的限制(+4 => 54,-3 => 51,+4 => 55,-3 => 52)。
http://sqlfiddle.com/#!9/db020/13
这是因为 MySQL 的处理顺序。但是这个顺序取决于 MySQL 的内部实现。此实现可能会在未来版本中更改,而不会发出任何警告。事实上 - MySQL 开发人员明确警告以这种方式使用用户变量。
As a general rule, other than in SET statements, you should never
assign a value to a user variable and read the value within the same
statement. For example, to increment a variable, this is okay:
SET @a = @a + 1;
For other statements, such as SELECT, you might get the results you
expect, but this is not guaranteed. In the following statement, you
might think that MySQL will evaluate @a first and then do an
assignment second:
SELECT @a, @a:=@a+1, ...;
However, the order of evaluation for expressions involving user
variables is undefined.
我只将 "tricks" 用于单向报告 - 但从不用于生产代码。
所以我的建议是:改变规则或使用程序语言(PHP)。
概览:
我在本地 运行 构建了一个应用程序,它允许我跟踪孩子们每天的家务和行为。该系统具有我可以分配给它们的消极和积极行为,这些行为对应于 100
点量表上的点值。
逻辑:
- 查询仅查看当天来计算积分。如果收视率是前一天收到的,则这些收视率不会计入他们的每日总计。
- 100 分是 child 当天可以拥有的最大值,即使他们的收视率导致他们超过了这一点,它也总是 return 为
100
。 - 如果他们当天没有任何评分(正面或负面),系统会将他们的分数默认为起点
100
。 - 当他们获得分数时,他们的总分数将相应调整,根据为行为设置的值上升或下降。
场景:
- 没有任何收视率的新一天意味着 child 从
100
点开始。他们收到具有-3
值的负面行为。这将 return 他们的totalPoints
作为97
. - 以上 child 获得正面评价,价值
2
分,这使他们的totalPoints
达到99
。 - 他们又获得了
5
分的好评。由于我们的最大值为 100,因此我们会 return 他们的totalPoints
为100
,而不管它超出了多少100
。
问题:
我构建了查询并认为一切正常,但它似乎存在一个轻微的数学问题。当 child 获得 -3
点评级时,他们达到了预期的 97
。然后我给了他们一个积极的 4
,这使他们的分数达到 99
而不是我预期的 100
。
查询:
SELECT c.id,
c.NAME,
Date_format(From_days(Datediff(CURRENT_DATE, c.age)),
'%y Years %m Months %d Days') AS age,
c.photoname,
c.photonamesmall,
(SELECT CASE
WHEN ( Ifnull(Sum(t.points), (SELECT settingvalue
FROM settings
WHERE settingname = 'MaxPoints')
) >= (
SELECT
settingvalue
FROM
settings
WHERE
settingname = 'MaxPoints') ) THEN 100
WHEN ( Sum(t.points) <= 0 ) THEN ( (SELECT settingvalue
FROM settings
WHERE settingname =
'MaxPoints')
+ Sum(t.points) )
ELSE ( (SELECT settingvalue
FROM settings
WHERE settingname = 'MaxPoints') -
Ifnull(Sum(t.points), (SELECT
settingvalue
FROM settings
WHERE
settingname = 'MaxPoints')) )
END
FROM behaviorratings AS r
JOIN behaviortypes AS t
ON r.behaviorid = t.behaviortypeid
WHERE r.childid = c.id
AND Date_format(r.timestamp, '%Y-%m-%d') = Curdate()) AS
totalPoints,
(SELECT definitionname
FROM behaviordefinitions AS d
WHERE totalpoints BETWEEN d.min AND d.max) AS
behaviorRating
FROM children AS c
Fiddle:
这里是 link 到 SQL fiddle: http://sqlfiddle.com/#!9/fa06c/1/0
我希望看到 Child 2 (Brynlee) 的结果是 100
而不是 99
。
她从 100
开始,收到 -3
并收到 +4
。虽然我知道这个操作顺序的数学是正确的,但我需要对其进行调整以反映我期望的反映方式。 100 - 3 = 97
然后是 97 + 4 = 101
(我们在 100
处达到最大值,所以 100
将是 totalPoints
。
试试这个
SELECT c.id,
c.name,
DATE_FORMAT(
FROM_DAYS(
DATEDIFF(CURRENT_DATE, c.age)
),
'%y Years %m Months %d Days'
) AS age,
c.photoName,
c.photoNameSmall,
(SELECT CASE
WHEN ( Ifnull(Sum(t.points), 0
) + (SELECT settingValue
FROM settings
WHERE settingName = 'MaxPoints') >= (
SELECT
settingValue
FROM
settings
WHERE
settingName = 'MaxPoints') ) THEN 100
WHEN ( Sum(t.points) <= 0 ) THEN ( (SELECT settingValue
FROM settings
WHERE settingName =
'MaxPoints')
+ Sum(t.points) )
ELSE ( (SELECT settingValue
FROM settings
WHERE settingName = 'MaxPoints') -
Ifnull(Sum(t.points), (SELECT
settingvalue
FROM settings
WHERE
settingName = 'MaxPoints')) )
END
FROM behaviorRatings AS r
JOIN behaviorTypes AS t
ON r.behaviorID = t.behaviorTypeID
WHERE r.childid = c.id
AND Date_format(r.timestamp, '%Y-%m-%d') = Curdate()) AS
totalPoints,
(SELECT definitionName
FROM behaviorDefinitions AS d
WHERE totalPoints BETWEEN d.min AND d.max) AS
behaviorRating
FROM children AS c
基本上,使用
WHEN ( Ifnull(Sum(t.points), (SELECT settingvalue
FROM settings
WHERE settingname = 'MaxPoints')
)
当 sum(t.points) 为 null 时,只会给你 100。要获得总分,您需要做
Ifnull(Sum(t.points), 0) + (SELECT settingvalue
FROM settings
WHERE settingname = 'MaxPoints')
这个sql可能更容易看
SET @maxPoints := (SELECT settingValue
FROM settings
WHERE settingName = 'MaxPoints');
SELECT c.id,
c.name,
DATE_FORMAT(
FROM_DAYS(
DATEDIFF(CURRENT_DATE, c.age)
),
'%y Years %m Months %d Days'
) AS age,
c.photoName,
c.photoNameSmall,
(SELECT CASE
WHEN ( Ifnull(Sum(t.points), 0) + @maxPoints > @maxPoints ) THEN 100
ELSE ( Ifnull(Sum(t.points), 0) + @maxPoints )
END
FROM behaviorRatings AS r
JOIN behaviorTypes AS t
ON r.behaviorID = t.behaviorTypeID
WHERE r.childid = c.id
AND Date_format(r.timestamp, '%Y-%m-%d') = Curdate()) AS
totalPoints,
(SELECT definitionName
FROM behaviorDefinitions AS d
WHERE totalPoints BETWEEN d.min AND d.max) AS
behaviorRating
FROM children AS c
以 50 为起点:
SET @maxPoints := (SELECT settingValue
FROM settings
WHERE settingName = 'MaxPoints');
SET @startingPoint := 50;
SELECT c.id,
c.name,
DATE_FORMAT(
FROM_DAYS(
DATEDIFF(CURRENT_DATE, c.age)
),
'%y Years %m Months %d Days'
) AS age,
c.photoName,
c.photoNameSmall,
(SELECT CASE
WHEN ( Ifnull(Sum(t.points), 0) + @startingPoint > @maxPoints ) THEN 100
ELSE ( Ifnull(Sum(t.points), 0) + @startingPoint )
END
FROM behaviorRatings AS r
JOIN behaviorTypes AS t
ON r.behaviorID = t.behaviorTypeID
WHERE r.childid = c.id
AND Date_format(r.timestamp, '%Y-%m-%d') = Curdate()) AS
totalPoints,
(SELECT definitionName
FROM behaviorDefinitions AS d
WHERE totalPoints BETWEEN d.min AND d.max) AS
behaviorRating
FROM children AS c
Sql 总点数超过限制后申请封顶
SET @maxPoints := (SELECT settingValue
FROM settings
WHERE settingName = 'MaxPoints');
SET @startingPoint := 50;
SELECT
c.id,
c.name,
DATE_FORMAT(
FROM_DAYS(DATEDIFF(CURRENT_DATE, c.age)), '%y Years %m Months %d Days') AS age,
c.photoName,
c.photoNameSmall,
(
select x.tp
from
(
SELECT t.childid,
@rn:=CASE WHEN @cid <> t.childid THEN 0 ELSE @rn+1 END AS rn,
@startingPoint + @tp:= CASE
WHEN @cid <> t.childid
THEN ifnull(t.points, 0)
ELSE (
case when @startingPoint + t.points + @tp > @maxPoints
then @maxPoints - @startingPoint
else t.points + @tp end)
END AS tp,
@cid:=t.childid AS clset,
t.timestamp
FROM
(SELECT @tp:= -1) p,
(SELECT @rn:= -1) n,
(SELECT @cid:= -1) cd,
(
SELECT r.childid, t.points, r.timestamp
FROM behaviorRatings AS r
JOIN behaviorTypes AS t ON r.behaviorID = t.behaviorTypeID
ORDER BY r.childid, r.timestamp
) t
) x
where x.childid = c.id AND Date_format(x.timestamp, '%Y-%m-%d') = Curdate()
order by x.childid, x.rn desc
limit 1
) AS totalPoints,
(
SELECT definitionName
FROM behaviorDefinitions AS d
WHERE totalPoints BETWEEN d.min AND d.max
) AS behaviorRating
FROM children AS c
不要让事情变得更复杂。为您的任务选择正确的语言。在你的情况下是 PHP:
$query = "select settingValue from settings where settingName = 'MaxPoints'";
$result = $this->db->query($query);
$row = $result->fetchAssoc();
$maxPoints = $row['settingValue'];
$query = "select * from children";
$result = $this->db->query($query);
$children = array();
while ($row = $result->fetchAssoc()) {
$row['totalPoints'] = $maxPoints;
$children[$row['id']] = $row;
}
$query = "
select c.id, coalesce(bt.points, 0) as points
from children c
join behaviorRatings br on br.childID = c.id
join behaviorTypes bt on bt.behaviorTypeID = br.behaviorID
where date(br.timestamp) = current_date()
order by c.id, br.timestamp
";
$result = $this->db->query($query);
while ($row = $result->fetchAssoc()) {
$childId = $row['id'];
$totalPoints = $children[$row['id']]['totalPoints'];
$totalPoints = $totalPoints + $row['points'];
$totalPoints = min($totalPoints, $maxPoints);
$children[$row['id']]['totalPoints'] = $totalPoints;
}
var_dump($children);
所有获取总分的逻辑都在最后一个循环中。现在将其与您的查询进行比较。
但是 - 如果您更改规则,允许在一天中超过限制并仅在一天结束时削减点数,这可以在单个查询中完成:
select c.*, sub.totalPoints, bd.definitionName
from (
select c.id, least(100+coalesce(sum(bt.points), 0), mp.settingValue) as totalPoints
from children c
join settings mp on settingName = 'MaxPoints'
left join behaviorRatings br
on br.childID = c.id
and date(br.timestamp) = current_date()
left join behaviorTypes bt on bt.behaviorTypeID = br.behaviorID
group by c.id
) sub
join children c on c.id = sub.id
join behaviorDefinitions bd on sub.totalPoints between bd.min and bd.max
http://sqlfiddle.com/#!9/fa06c/71
虽然这不是一个简单的查询,但它远没有您尝试的那么复杂。公认的解决方案是忽略规则,即每次获得点数时总点数必须减少到 100 (Sum(t.points)
)。
正如我在评论中所写:要遵循该规则,您需要进行某种迭代。 MySQL 中有一个使用用户变量的技巧:
select c.id, c.name, sub.totalPoints, bd.definitionName
from (
select sub.childId, sum(sub.cuttedPoints) + sp.settingValue as totalPoints
from (
select
@points := coalesce(bt.points,0) as points,
@lastTotalPoints := case when (c.id = @childId)
then @totalPoints
else sp.settingValue
end lastTotalPoints,
@totalPoints := least(@lastTotalPoints + @points, mp.settingValue) as totalPoints,
@totalPoints - @lastTotalPoints as cuttedPoints,
@childId := c.id as childId
from children c
join settings sp on sp.settingName = 'StartPoints'
join settings mp on mp.settingName = 'MaxPoints'
left join behaviorRatings br
on br.childID = c.id
and date(br.timestamp) = current_date()
left join behaviorTypes bt on bt.behaviorTypeID = br.behaviorID
cross join (select @childId := null) init_var
order by c.id, br.timestamp
) sub
join settings sp on sp.settingName = 'StartPoints'
group by sub.childId
) sub
join children c on c.id = sub.childId
join behaviorDefinitions bd on sub.totalPoints between bd.min and bd.max
结果(Brynlee 行为:+4 -3 +4 -3):
| id | name | totalPoints | definitionName |
|----|---------|-------------|------------------------|
| 2 | Brynlee | 97 | Having an amazing day! |
| 1 | Maya | 100 | Having an amazing day! |
Brynlee 获得 97 分(+4 => 100,-3 => 97,+4 => 100,-3 => 97)
http://sqlfiddle.com/#!9/751c51/28
如果您将新设置 "StartPoints" 更改为 50,您将获得:
| id | name | totalPoints | definitionName |
|----|---------|-------------|------------------|
| 2 | Brynlee | 52 | Not looking good |
| 1 | Maya | 50 | Not looking good |
此处 Brynlee 得到 52 分,因为从未达到 100 的限制(+4 => 54,-3 => 51,+4 => 55,-3 => 52)。
http://sqlfiddle.com/#!9/db020/13
这是因为 MySQL 的处理顺序。但是这个顺序取决于 MySQL 的内部实现。此实现可能会在未来版本中更改,而不会发出任何警告。事实上 - MySQL 开发人员明确警告以这种方式使用用户变量。
As a general rule, other than in SET statements, you should never assign a value to a user variable and read the value within the same statement. For example, to increment a variable, this is okay:
SET @a = @a + 1;
For other statements, such as SELECT, you might get the results you expect, but this is not guaranteed. In the following statement, you might think that MySQL will evaluate @a first and then do an assignment second:
SELECT @a, @a:=@a+1, ...;
However, the order of evaluation for expressions involving user variables is undefined.
我只将 "tricks" 用于单向报告 - 但从不用于生产代码。
所以我的建议是:改变规则或使用程序语言(PHP)。