概述:
我构建了一个我在本地运行的应用程序,这使我能够跟踪我的孩子每天的家务和行为。该系统具有我可以分配给它们的消极和积极行为,这些行为对应于100
点量表上的点值。
逻辑:
100
。100
。方案:
100
点开始。他们收到的行为具有-3
值。这会将totalPoints
作为97
返回。2
积分的正面评分,这些评分会为99
提升totalPoints
。5
分的积极评分。由于我们最高为100,因此我们会将totalPoints
作为100
返回,无论其超出100
多少。问题:
我构建了查询,并认为一切正常,但似乎有一个轻微的数学问题。如果孩子获得-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
小提琴:
以下是SQL小提琴的链接:http://sqlfiddle.com/#!9/fa06c/1/0
我希望看到Child 2(Brynlee)的结果是100
而不是99
。
她从100
开始,收到了-3
,收到了+4
。虽然我知道这个操作顺序的数学运算是正确的,但我需要对其进行调整以反映我对它的预期反映。 100 - 3 = 97
然后97 + 4 = 101
(我们在100
最多,因此100
将为totalPoints
。
答案 0 :(得分:1)
试试这个
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)为空时,只会给你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
答案 1 :(得分:0)
不要让事情变得更加复杂。为您的任务选择合适的语言。在你的情况下它是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 =&gt; 100,-3 =&gt; 97,+ 4 =&gt; 100,-3 =&gt; 97)
http://sqlfiddle.com/#!9/751c51/28
如果您更改新设置&#34; StartPoints&#34;到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开发人员明确警告过这样使用用户变量。
作为一般规则,除了在SET语句中,你永远不应该 为用户变量赋值并读取其中的值 声明。例如,要增加变量,这没关系:
SET @a = @a + 1;
对于其他语句,例如SELECT,您可能会得到结果 期待,但这不能保证。在以下声明中,您 可能会认为MySQL会首先评估@a然后做一个 第二个任务:
SELECT @a, @a:=@a+1, ...;
但是,涉及用户的表达式的评估顺序 变量未定义。
我只使用&#34;技巧&#34;就像单向报告一样 - 但从来没有出现在生产代码中。
所以我的建议是:更改规则或使用过程语言(PHP)。