【问题标题】:MySQL Gurus: How to pull a complex grid of data from MySQL database with one query?MySQL 大师:如何通过一次查询从 MySQL 数据库中提取复杂的数据网格?
【发布时间】:2010-04-10 21:03:27
【问题描述】:

希望这没有我想象的那么复杂。

我有一个公司表,另一个工作表,以及第三个表,其中包含每个公司每个工作中每个员工的单个条目。 注意:有些公司在某些工作岗位上没有员工,而有些公司在某些工作岗位上会有不止一名员工。

company表有companyidcompanyname字段,job表有jobidjobtitle字段,employee表有employeeidcompanyidjobid和@987654328 @字段。

我想建一个这样的表:

+-----------+------------+------------+ | A公司| B公司| C公司| ------+------------+-----------+------------+ 工作A |雇员 1 |雇员 2 | | ------+------------+-----------+------------+ 工作乙 |雇员 3 | |雇员 4 | | | |雇员 5 | ------+------------+-----------+------------+ 工作 C | |雇员 6 | | | |员工 7 | | | |雇员 8 | | ------+------------+-----------+------------+

我之前一直在遍历一组工作的结果集,对于每个工作,循环遍历每个公司的结果集,对于每个公司,循环遍历每个员工并将其打印在表格中(粗略,但性能不应该是一个考虑)。该应用程序越来越受欢迎,现在我们有 100 家公司和数百个工作岗位,而服务器正在崩溃(所有 id 字段都已编入索引)。

关于如何编写单个查询来获取此数据的任何建议?我不需要公司名称或职位(显然),但我确实需要一些方法来确定结果中每一行的打印位置。我在想象一个只包含一长串加入的员工的结果集,我可以编写一个循环来使用companyidemployeeid 值来告诉我何时创建新的单元格或表格行。只要没有零员工,这就是有效的;我认为我需要一个 NULL 员工姓名?我完全走错了吗?

提前感谢您的任何想法!

【问题讨论】:

  • 我想您可能会发现执行此操作的查询会非常慢...

标签: mysql


【解决方案1】:

诀窍不在于查询(类似于

SELECT jobtitle, companyname, employeename 
  FROM employee AS e
    RIGHT JOIN company AS c ON e.companyid = c.companyid
    RIGHT JOIN job AS j ON e.jobid = j.jobid
  WHERE ...
  ORDER BY j.jobtitle, c.companyname

) 作为您处理结果的方式。对于每个结果行,在公司发生变化时开始一个新的表格单元格,在职位名称发生变化时开始一个新的表格行。最棘手的部分是确定何时需要输出关闭标记。

【讨论】:

  • 感谢您的帮助--我已经完成了这项工作,并且在我的开发服务器上看起来不错,但是一旦我放入我的共享托管服务器(16M 内存,30 秒执行),它内存不足。我要检查我的 PHP 是否有泄漏。
  • @iopener:如果可能,尝试无缓冲查询(stackoverflow.com/questions/131139/…stackoverflow.com/questions/1448661/…;如果使用 PHP,PDO 默认无缓冲),这样脚本就不必保存整个结果设置(尽管在 ent 之前您将无法获得结果行的计数)。还可以尝试 SeanJA 的建议来限制/分页结果。分析查询以查看它在脚本中和自身的执行情况 (dev.mysql.com/doc/refman/5.1/en/explain.html)。
  • 最后,我将表内连接在一起,并允许跳过 NULL 匹配。在我的 PHP 中,我遍历 companyid 和 jobid 的每个组合,并检查它是否与当前结果记录匹配。如果是这样,我打印它;如果没有,我关闭单元格并移动到 companyid 和 jobid 的下一个组合。由于数据的排序顺序与打印单元格的顺序相同,因此效果很好:使用 32MB 内存无法在两分钟内完成的页面现在使用 16MB 内存在 6 秒内完成。感谢大家的意见!
【解决方案2】:

我认为这可能更接近您的要求:

SELECT j.jobtitle, e1.employeeName, e2.employeename, e3.employeename
FROM jobs AS j
LEFT OUTER JOIN employee AS e1 ON e1.jobid = j.jobid AND e1.companyid = 1
LEFT OUTER JOIN employee AS e2 ON e2.jobid = j.jobid AND e2.companyid = 2
LEFT OUTER JOIN employee AS e3 ON e3.jobid = j.jobid AND e3.companyid = 3
ORDER BY j.jobtitle

LEFT OUTER JOIN 将在没有匹配的情况下为您提供 NULL。

添加几家公司后,您会看到它可能会变得丑陋。如果你已经索引了你的 id 列,这不会很慢。不要害怕加入 - 数据库就是为此而生的!

【讨论】:

  • 我明白了这个查询的要点,这是一个非常聪明的主意。我在 PHP 代码中生成了 SQL,打印出来并在 phpMyAdmin 中运行,它运行了 15 多分钟而没有完成。我想知道这是否与数据中存在大量 NULL 值有关?
【解决方案3】:

我认为Joins 可能是您正在寻找的答案。

【讨论】:

  • Left Joins 实际上...这可能不存在,可能不存在...结果是一个缓慢的查询
  • @SeanJA:如果表格被正确索引,结果不一定很慢。
  • 它仍然(通常)比取回数据并使用代码将其处理到表中要慢。
  • 如果表格设置正确,您将节省大量时间和精力来制作正确的查询,而不是提取大量数据并使用服务器将其处理成表格。
  • 我并不是在暗示他应该从所有表中提取所有数据,然后以他使用的任何语言对其进行处理,限制查询,索引事物,而不是显示所有立即获取数据[show more results for this company]... 如果不进行一些处理,他将无法获得与他想要的完全一样的结果集。
【解决方案4】:

将变量替换为对应的公司id,如果需要增加@@group_concat_max_len

SELECT
    jobtitle as '',
    GROUP_CONCAT(IF(companyid=@companyA_id, employeename,NULL) SEPARATOR '\n') as `Company A`,
    GROUP_CONCAT(IF(companyid=@companyB_id, employeename,NULL) SEPARATOR '\n') as `Company B`,
    GROUP_CONCAT(IF(companyid=@companyC_id, employeename,NULL) SEPARATOR '\n') as `Company C`
FROM employee
LEFT JOIN company
    USING (companyid)
LEFT JOIN job
    USING (jobid)
GROUP BY jobid;

UPD:似乎您甚至不需要 JOIN 到 company 表,因此可以简化为:

SELECT
    jobtitle as '',
    GROUP_CONCAT(IF(companyid=@companyA_id, employeename,NULL) SEPARATOR '\n') as `Company A`,
    GROUP_CONCAT(IF(companyid=@companyB_id, employeename,NULL) SEPARATOR '\n') as `Company B`,
    GROUP_CONCAT(IF(companyid=@companyC_id, employeename,NULL) SEPARATOR '\n') as `Company C`
FROM employee
LEFT JOIN job
    USING (jobid)
GROUP BY jobid;

但在这种情况下,您可能需要在员工表中的 companyid 字段上建立索引。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-09-18
    • 2018-09-06
    • 2014-04-24
    • 2010-10-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多