使用 HAVING GROUP_CONCAT 计算记录行
Posted
技术标签:
【中文标题】使用 HAVING GROUP_CONCAT 计算记录行【英文标题】:Counting record rows with HAVING GROUP_CONCAT 【发布时间】:2014-03-12 10:55:02 【问题描述】:我的问题(可能)有点复杂,我试着把它写下来,以便理解。 我在 php 中有一个类,它根据几种情况和参数生成 mysql 查询(然后在 dinamyc 表中显示查询结果)。 例如,这里是表格和示例查询:
很简单,有用户,每个用户可以分配一个或多个配置文件。 例如,我们只有一个用户,他有两个个人资料:
[USER]
userid login password status reg_date
------ ------- ---------- ------ -------------------
1 JohnDoe hd98349... 0 2014-01-09 16:00:55
[PROFILE]
profile_id profile_name
---------- ------------
1 admin
2 root
3 user
[USER_PROFILE]
relation_id user profile
----------- ----- -------
1 1 1
2 1 2
我的 PHP 代码会生成一个如下所示的查询:
SELECT
userid AS 'User id',
login AS 'User name',
GROUP_CONCAT(profile_name SEPARATOR ', ') AS 'Profile(s)',
`status`
FROM `user` LEFT JOIN user_profile ON `user`.userid = user_profile.`user`
LEFT JOIN `profile` ON user_profile.`profile` = `profile`.profile_id
WHERE
status IN (0, 1)
ORDER BY reg_date ASC
LIMIT 0 ,10
这个查询的结果是:
[RESULT]
User id User name Profile(s) status
------- --------- ---------- ------
1 JohnDoe root, admin 0
当我想对源自子查询或复杂表达式(如 GROUP_CONCAT(profile_name SEPARATOR ', ')
)的列使用过滤器时,我得到错误的结果:
SELECT COUNT(*) AS 'all_rows'
FROM `user` LEFT JOIN user_profile ON `user`.userid = user_profile.`user`
LEFT JOIN `profile` ON user_profile.`profile` = `profile`.profile_id WHERE status IN (0, 1)
HAVING GROUP_CONCAT(profile_name SEPARATOR ', ') LIKE '%root%'
这将导致:'all_rows' => 2。 这是因为 HAVING 中的 GROUP_CONCAT 不会分组,但是如果我将它放在 SELECT 中,例如: SELECT COUNT(GROUP_CONCAT(profile_name SEPARATOR ', ')) 我会收到错误
所以问题是如何在过滤器(WHERE / HAVING)表达式中使用 GROUP_CONCAT 获取所有行数?
编辑:在我运行主查询之前,我需要知道它会产生多少记录行,没有 LIMIT 限制。我使用它来计算 LIMIT 的起始行值以用于分页目的。
【问题讨论】:
你能在 SQL Fiddle (www.sqlfiddle.com) 上进行设置吗? 就个人而言,我倾向于完全避免使用 GROUP_CONCAT(和 CONCAT)。我更喜欢在应用程序级别处理给定问题的这些方面。 GROUP_CONCAT() row count when grouping by a text field的可能重复 您的查询中没有GROUP BY
子句。因此,您将所有与 WHERE
条件匹配的行聚合成一个结果。
SQL_CALC_FOUND_ROWS
选项不能解决您遇到的问题吗?
【参考方案1】:
你能试试这个结果吗?您没有告诉查询引擎要分组的内容,它似乎猜错了。
SELECT `user`.userid,
GROUP_CONCAT(profile_name SEPARATOR ', ') as roles,
COUNT(*) AS 'all_rows'
FROM `user`
LEFT JOIN user_profile ON `user`.userid = user_profile.`user`
LEFT JOIN `profile` ON user_profile.`profile` = `profile`.profile_id
WHERE status IN (0, 1)
GROUP BY `user`.userid
HAVING GROUP_CONCAT(profile_name SEPARATOR ', ') LIKE '%root%'
【讨论】:
答案还是一样,我的朋友..它的 2【参考方案2】:您有一个异常查询,其中计数将返回 0 或 1。您绝对可以通过使用子查询来解决此问题:
SELECT COUNT(*) AS 'all_rows'
FROM (SELECT GROUP_CONCAT(profile_name SEPARATOR ', ') as pns
FROM `user` LEFT JOIN
user_profile
ON `user`.userid = user_profile.`user` LEFT JOIN
`profile`
ON user_profile.`profile` = `profile`.profile_id
WHERE status IN (0, 1)
) t
WHERE pns LIKE '%root%';
您还可以通过将逻辑移至where
子句来解决问题:
SELECT COUNT(*)
FROM `user` LEFT JOIN
user_profile
ON `user`.userid = user_profile.`user` LEFT JOIN
`profile`
ON user_profile.`profile` = `profile`.profile_id
WHERE status IN (0, 1) and profile_name like '%root%';
【讨论】:
这些是好主意,它们应该可以工作,但在我的情况下,查询是动态生成的,无法将原始列与子查询或复杂表达式的结果列区分开来。我的班级将 SELECT 部分中的所有列一起处理,因此我不能将 GROUP_CONCAT(profile_name SEPARATOR ', ') 部分与其他部分分开。【参考方案3】:我喜欢一个解决方案: 这是记录计数查询的外观:
SELECT COUNT(*) AS 'all_rows'
FROM (SELECT
userid AS 'User id',
login AS 'User name',
GROUP_CONCAT(profile_name SEPARATOR ', ') AS 'Profile(s)',
status
FROM `user` LEFT JOIN user_profile ON `user`.userid = user_profile.`user`
LEFT JOIN `profile` ON user_profile.`profile` = `profile`.profile_id
HAVING
status IN (0, 1)
AND GROUP_CONCAT(profile_name SEPARATOR ', ') LIKE '%root%') t
这是主要的查询:
SELECT
userid AS 'User id',
login AS 'User name',
GROUP_CONCAT(profile_name SEPARATOR ', ') AS 'Profil(ok)',
status
FROM `user` LEFT JOIN user_profile ON `user`.userid = user_profile.`user`
LEFT JOIN `profile` ON user_profile.`profile` = `profile`.profile_id
HAVING
status IN (0, 1)
AND GROUP_CONCAT(profile_name SEPARATOR ', ') LIKE '%root%'
ORDER BY reg_date ASC
LIMIT 0 ,10
我必须在任何地方都使用HAVING
而不是WHERE
,因为正如我所说,主查询是通过程序生成的。这就是我在 SELECT 部分设置列的方式:
$listObj->setColumns(array('userid' => 'User id',
'login' => 'User name',
'GROUP_CONCAT(profile_name SEPARATOR \', \')' => 'Profile(s)'));
当用户在过滤器输入中键入 %root% 表达式时,我无法确定 GROUP_CONCAT(profile_name SEPARATOR ', ') LIKE %root%
是否进入 WHERE
部分或 HAVING
部分,因为它与任何其他列(如
userid AS 'User id'
或 login AS 'User name'
列)
剩下的技术问题是,在任何地方都使用HAVING
是不是一个错误,即使一个简单的WHERE
就足够了?
【讨论】:
以上是关于使用 HAVING GROUP_CONCAT 计算记录行的主要内容,如果未能解决你的问题,请参考以下文章
使用 GROUP_CONCAT、GROUP BY、HAVING 进行选择
为啥 HAVING 和 GROUP_CONCAT 在此查询中不能一起工作?
如何使用 group_concat 和左连接计算 mysql 查询的结果