4

我需要查询此数据库以获取每一行,以及结果的列值之一的 SUM。我可以使用 php 来获取总值,但是我需要运行两个循环,一个来获取总值(位于结果上方的顶部)。所以我更喜欢查询来捕获它并只创建一个“总”行,但我让它工作的唯一方法是使用基本上是原始查询重复的子查询。有没有更好的办法?

SELECT 
CONCAT(u.firstname, ' ', u.lastname ) name, u.id, s.description, s.shiftstart, s.shiftend, 
    (SELECT 
    SUM( TIME_TO_SEC( TIMEDIFF( shiftend, shiftstart ) ) ) /3600
    FROM shifts
    WHERE id =  '$user'
    AND DATE( shiftstart )
    BETWEEN '$start'
    AND '$end') total
FROM shifts s
INNER JOIN users u ON ( s.id = u.id )
WHERE s.id = '$user'
AND DATE( shiftstart )
BETWEEN '$start'
AND '$end'
ORDER BY shiftstart

上述工作和输出:

name        id     description  shiftstart             shiftend               total
Joe User    joeuser    Stuff    2009-01-05 07:45:00    2009-01-05 12:15:00    39.5000
Joe User    joeuser    Stuff    2009-01-05 13:00:00    2009-01-05 17:00:00    39.5000
Joe User    joeuser    Stuff    2009-01-06 07:45:00    2009-01-06 10:45:00    39.5000
Joe User    joeuser    Stuff    2009-01-06 10:45:00    2009-01-06 12:45:00    39.5000
Joe User    joeuser    Stuff    2009-01-06 13:30:00    2009-01-06 14:30:00    39.5000
Joe User    joeuser    Stuff    2009-01-06 14:30:00    2009-01-06 17:00:00    39.5000
Joe User    joeuser    Stuff    2009-01-07 09:45:00    2009-01-07 14:00:00    39.5000
Joe User    joeuser    Stuff    2009-01-07 15:00:00    2009-01-07 17:00:00    39.5000
Joe User    joeuser    Stuff    2009-01-08 08:00:00    2009-01-08 12:15:00    39.5000
Joe User    joeuser    Stuff    2009-01-08 13:15:00    2009-01-08 17:00:00    39.5000
Joe User    joeuser    Stuff    2009-01-09 07:45:00    2009-01-09 10:45:00    39.5000
Joe User    joeuser    Stuff    2009-01-09 11:45:00    2009-01-09 15:15:00    39.5000
Joe User    joeuser    Stuff    2009-01-09 15:15:00    2009-01-09 17:00:00    39.5000

这是我需要的,但可能不是获得它的最佳方式。

4

4 回答 4

5

MySQL 支持一种特殊的 group-by 修饰符,称为ROLLUP

SELECT CONCAT(u.firstname, ' ', u.lastname ) name, u.id, 
  s.description, s.shiftstart, s.shiftend, 
  SUM( TIME_TO_SEC( TIMEDIFF( shiftend, shiftstart ) ) ) /3600 total
FROM shifts s INNER JOIN users u ON ( s.id = u.id )
WHERE s.id = ? AND DATE( shiftstart ) BETWEEN ? AND ?
GROUP BY u.id, s.shiftstart WITH ROLLUP
ORDER BY shiftstart;
于 2009-01-29T00:08:31.537 回答
3

更好的方法是使用代码来做到这一点。人们一直坚持使用关系代数 SQL 来完成程序性任务。就复杂性和性能而言,试图将过程性硬塞到 SQL 上总是一个坏主意。听取专业 DBA 的建议。

从您的代码中运行两个查询。首先输出较大的集合,然后以您想要的任何格式输出总行。您的查询将变得更小更简单,您的性能将得到提高,您将获得所需的输出。

其他一些建议 - 磁盘空间很便宜,并且大多数数据库表的读取频率远高于写入频率。设置一个插入/更新触发器(如果可能在 MySQL 中)以使用“ CONCAT(u.firstname,' ',u.lastname)”之类的计算字段填充单独的列,并将其用于查询。每行函数是不可扩展的,当它变大时会破坏你的 DBMS 性能。

于 2009-01-28T23:50:44.973 回答
0

相比之下,任何事情都可以更好;问题是与什么比较,对吧?

您指出的关键问题是您希望总数位于结果的顶部;Crystal Reports 和其他应用程序通过作为 2 通道引擎来管理这种魔法。第一次通过获得总数。

任何解决方案都需要权衡取舍。如果您得到一个单独的“总计”行,那么接收应用程序将需要将其从结果中删除或使用其他技巧来隐藏它。

如果您不是为每小时点击量为 100 万的网站编写代码,则一种可能性是简单地进行 2 次调用 - 一次调用开销信息,例如名称、总时间等,然后调用详细信息...从查询中显示您正在选择一个人的结果。

我们都是为了节省开销和带宽,但有时,简单更好......

编辑:Pax 将我击败到“保存”按钮...大声笑...

于 2009-01-28T23:52:33.940 回答
0

使用专门为此目的提供的 mysql 扩展,如 Bill Karwin 的回复(我自己赞成)中所述。

如果这不可用,选项 2 将是另一个 SQL 语句:SELECT SUM ...) 与您可能编写的任何过程代码循环相比,SQL 确实经过优化,对于这类事情非常有效。

于 2009-01-29T00:19:25.073 回答