我有三张桌子。 TB_Main是实体表。 TB_BoardMembers是一个人员表。 TB_BoardMembersLINK是一个桥接表,它通过ID引用其他两个,并且还有一个Person在一个实体的董事会上的开始和结束日期。这些日期通常不完整。
我被要求作为报告的一部分导出CSV,每个实体每年一行,其中我有一个当年的董事会成员列表,其职业位于由换行符分隔的单个字段中。
我在结果中不需要bml.Entity,但添加它以尝试调试。我得到一行,我期望85.尝试使用和没有GROUP BY,结果是相同的事实表明我滥用GROUP_CONCAT。我应该如何构建它以获得他们想要的结果?
SELECT
GROUP_CONCAT(
DISTINCT CONCAT(bm.First, ' ', bm.Last,
IF (bm.Occupation != '', ' - ', ''),
bm.Occupation) SEPARATOR "\n") as Board,
bml.Entity
FROM
TB_Main arfe,
TB_BoardMembers bm,
TB_BoardMembersLINK bml
WHERE YEAR(bml.start) <= 2011
AND (YEAR(bml.end) >= 2011 OR bml.end IS NULL)
AND bml.start > 0
AND bml.Entity = arfe.ID
GROUP BY bml.Entity
ORDER BY Board
答案 0 :(得分:2)
此查询存在一些问题。主要问题似乎是您缺少将董事会成员链接到链接表的条件,因此您有一个交叉联接,即您将返回每个宽带成员,无论其开始/结束日期如何,并假设您有85行在标准匹配的情况下,您实际上将返回每个董事会成员85次。这突出了从您使用的ANSI 89隐式连接切换到ANSI 92显式连接语法的一个很好的理由。 This article强调了进行转换的一些非常好的理由。
所以你的查询会变成(我不得不猜测你的字段名称):
SELECT *
FROM TB_Main arfe
INNER JOIN TB_BoardMembersLINK bml
ON bml.Entity = arfe.ID
INNER JOIN TB_BoardMembers bm
ON bm.ID = bml.BoardMemberID
我注意到你的查询的下一件事是使用where子句中的函数根本不是很有效,所以因为这个:
WHERE YEAR(bml.start) <= 2011
AND (YEAR(bml.end) >= 2011 OR bml.end IS NULL)
您正在为每一行操作YEAR
函数两次,并删除在bml.Start
或bml.End
(如果存在)上使用索引的任何可能性。然而,Aaron Bertrand在查询日期范围时写了a nice article突出显示良好做法,它是SQL-Server的目标,但原则仍然相同,因此你的where子句将成为:
WHERE bml.Start <= '20110101'
AND (bml.End >= '20110101' OR bml.End IS NULL)
AND bml.start > 0
您的最终查询应该是:
SELECT bml.Entity,
GROUP_CONCAT(DISTINCT CONCAT(bm.First, ' ', bm.Last,
IF (bm.Occupation != '', ' - ', ''), bm.Occupation)
SEPARATOR "\n") as Board
FROM TB_Main arfe
INNER JOIN TB_BoardMembersLINK bml
ON bml.Entity = arfe.ID
INNER JOIN TB_BoardMembers bm
ON bm.ID = bml.BoardMemberID
WHERE bml.Start <= '20110101'
AND (bml.End >= '20110101' OR bml.End IS NULL)
AND bml.start > 0
GROUP BY bml.Entity
ORDER BY Board;
<强> Example on SQL Fiddle 强>
答案 1 :(得分:0)
如果你读了Group_Concat
“此函数返回一个字符串结果,其中包含来自组的连接非NULL值。”
在这种情况下,该组似乎只是一个组,正如您所说,只有一个实体?我不确定你的描述是否属于这种情况。为什么不按名字,姓氏和职业分组,这可能会给你所有的成员。
我也不确定你的连接,没有真正的数据很难解释那个部分,因为每个连接都适用于某些数据集,即使它不是编写查询的最佳方式