计算非规范化数据库中按 id 和月份分组的条目
Count entries grouped by id and month from denormalized database
我有一个 table (tbl_operations) 的行,其中 id 列值可能是逗号分隔的。我想获得每个月每个 OpId
的计数。我试图通过纯粹的 sql 来实现这一点,但没有成功。
从这个角度来看
OpId
OpDate
3
2022-01-03
5,3
2022-01-15
4
2022-01-27
5
2022-02-01
7
2022-02-09
3,2
2022-01-16
至此
OpId
count
Month
2
1
01
3
3
01
4
1
01
5
1
01
5
1
02
7
1
02
我被困在这里了。有人可以告诉我如何用 sql 做到这一点吗?如果不是,也许使用php来显示结果?
SELECT tbl_operations.OpId,
tbl_operations.OpDate ,
COUNT(tbl_operations.OpId) AS `count`
FROM tbl_operations
WHERE MONTH(OpDate)=1
GROUP BY tbl_operations.OpId
这是一个简单的例子。第一部分只是创建一个数组数组,模拟您从数据库中获得的内容。
要点是 $counts
是一个数组,其中的键具有唯一的 OpID。这些数组的值是 sub-arrays,其中包含月份的键和找到它们的次数的值。
显示应该再次只是一个简单的循环,但是您可能需要对其进行排序。
$rows = [
['3', '2022-01-03'],
['5,3', '2022-01-15'],
['4', '2022-01-27'],
['5', '2022-02-01'],
['7', '2022-02-09'],
['3,2', '2022-01-16'],
];
$counts = [];
foreach($rows as $row){
$ids = explode(',', $row[0]);
$month = date('m', strtotime($row[1]));
foreach($ids as $id){
if(!array_key_exists($id, $counts)){
$counts[$id] = [];
}
if(!array_key_exists($month, $counts[$id])){
$counts[$id][$month] = 0;
}
$counts[$id][$month]++;
}
}
编辑
来自@mickmackusa,您可以使用 isset
:
来缩短内部循环
if(!isset($counts[$id][$month])){
$counts[$id][$month] = 0;
}
查看他们对演示的评论link
如果您要查询 PHP 中的数据,您最好 return 首先使用更好的结果:
SQL
SELECT GROUP_CONCAT(OpId), MONTH(OpDate)
FROM tbl_operations
GROUP BY MONTH(OpDate)
PHP
// Result from MySQL query
$rows = [
['3,5,3,4,3,2', 1],
['5,7', 2]
];
您可以像这样对这些分组结果进行计数:
$results = [];
foreach ($rows as $row) {
$counts = array_count_values(explode(',', $row[0]));
$results[$row[1]] = $counts;
}
结果
Array
(
[1] => Array
(
[3] => 3
[5] => 1
[4] => 1
[2] => 1
)
[2] => Array
(
[5] => 1
[7] => 1
)
)
你真正想做的是规范化你的数据,然后你可以在 SQL 中轻松做到这一点。
如果您至少使用 MYSQL8 并且您不打算规范化您的 table 设计,那么您实际上可以使用以下 CTE 查询来拆分、分组、格式化,并对结果集进行排序(无 PHP 处理)。
这种方法对非规范化 table 进行递归调用,并逐渐将最右边的 id 从 comma-delimited 值中分离出来,并为各个 id 值生成新行。递归一直持续到没有逗号为止。
此解决方案建立在基本技术 demonstrated here 之上。
SQL: (Demo)
WITH RECURSIVE norm AS (
SELECT OpId,
OpDate
FROM tbl_operations
UNION ALL
SELECT REGEXP_REPLACE(OpId, '^[^,]*,', '') AS OpId,
OpDate
FROM norm
WHERE OpId LIKE '%,%'
)
SELECT Id,
Mo,
COUNT(*) AS Cnt
FROM (
SELECT REGEXP_REPLACE(norm.OpId, ',.*', '') AS Id,
MONTH(norm.OpDate) AS Mo
FROM norm
) formatted
GROUP BY formatted.Id,
formatted.Mo
结果集:
Id
Mo
Cnt
2
1
1
3
1
3
4
1
1
5
1
1
5
2
1
7
2
1
就是说,对于一项 mega-easy 的任务来说,一旦您规范化了 table --- 只需将其规范化 A.S.A.P 即可。
我有一个 table (tbl_operations) 的行,其中 id 列值可能是逗号分隔的。我想获得每个月每个 OpId
的计数。我试图通过纯粹的 sql 来实现这一点,但没有成功。
从这个角度来看
OpId | OpDate |
---|---|
3 | 2022-01-03 |
5,3 | 2022-01-15 |
4 | 2022-01-27 |
5 | 2022-02-01 |
7 | 2022-02-09 |
3,2 | 2022-01-16 |
至此
OpId | count | Month |
---|---|---|
2 | 1 | 01 |
3 | 3 | 01 |
4 | 1 | 01 |
5 | 1 | 01 |
5 | 1 | 02 |
7 | 1 | 02 |
我被困在这里了。有人可以告诉我如何用 sql 做到这一点吗?如果不是,也许使用php来显示结果?
SELECT tbl_operations.OpId,
tbl_operations.OpDate ,
COUNT(tbl_operations.OpId) AS `count`
FROM tbl_operations
WHERE MONTH(OpDate)=1
GROUP BY tbl_operations.OpId
这是一个简单的例子。第一部分只是创建一个数组数组,模拟您从数据库中获得的内容。
要点是 $counts
是一个数组,其中的键具有唯一的 OpID。这些数组的值是 sub-arrays,其中包含月份的键和找到它们的次数的值。
显示应该再次只是一个简单的循环,但是您可能需要对其进行排序。
$rows = [
['3', '2022-01-03'],
['5,3', '2022-01-15'],
['4', '2022-01-27'],
['5', '2022-02-01'],
['7', '2022-02-09'],
['3,2', '2022-01-16'],
];
$counts = [];
foreach($rows as $row){
$ids = explode(',', $row[0]);
$month = date('m', strtotime($row[1]));
foreach($ids as $id){
if(!array_key_exists($id, $counts)){
$counts[$id] = [];
}
if(!array_key_exists($month, $counts[$id])){
$counts[$id][$month] = 0;
}
$counts[$id][$month]++;
}
}
编辑
来自@mickmackusa,您可以使用 isset
:
if(!isset($counts[$id][$month])){
$counts[$id][$month] = 0;
}
查看他们对演示的评论link
如果您要查询 PHP 中的数据,您最好 return 首先使用更好的结果:
SQL
SELECT GROUP_CONCAT(OpId), MONTH(OpDate)
FROM tbl_operations
GROUP BY MONTH(OpDate)
PHP
// Result from MySQL query
$rows = [
['3,5,3,4,3,2', 1],
['5,7', 2]
];
您可以像这样对这些分组结果进行计数:
$results = [];
foreach ($rows as $row) {
$counts = array_count_values(explode(',', $row[0]));
$results[$row[1]] = $counts;
}
结果
Array
(
[1] => Array
(
[3] => 3
[5] => 1
[4] => 1
[2] => 1
)
[2] => Array
(
[5] => 1
[7] => 1
)
)
你真正想做的是规范化你的数据,然后你可以在 SQL 中轻松做到这一点。
如果您至少使用 MYSQL8 并且您不打算规范化您的 table 设计,那么您实际上可以使用以下 CTE 查询来拆分、分组、格式化,并对结果集进行排序(无 PHP 处理)。
这种方法对非规范化 table 进行递归调用,并逐渐将最右边的 id 从 comma-delimited 值中分离出来,并为各个 id 值生成新行。递归一直持续到没有逗号为止。
此解决方案建立在基本技术 demonstrated here 之上。
SQL: (Demo)
WITH RECURSIVE norm AS (
SELECT OpId,
OpDate
FROM tbl_operations
UNION ALL
SELECT REGEXP_REPLACE(OpId, '^[^,]*,', '') AS OpId,
OpDate
FROM norm
WHERE OpId LIKE '%,%'
)
SELECT Id,
Mo,
COUNT(*) AS Cnt
FROM (
SELECT REGEXP_REPLACE(norm.OpId, ',.*', '') AS Id,
MONTH(norm.OpDate) AS Mo
FROM norm
) formatted
GROUP BY formatted.Id,
formatted.Mo
结果集:
Id | Mo | Cnt |
---|---|---|
2 | 1 | 1 |
3 | 1 | 3 |
4 | 1 | 1 |
5 | 1 | 1 |
5 | 2 | 1 |
7 | 2 | 1 |
就是说,对于一项 mega-easy 的任务来说,一旦您规范化了 table --- 只需将其规范化 A.S.A.P 即可。