简体   繁体   English

计算非规范化数据库中按 id 和月份分组的条目

[英]Count entries grouped by id and month from denormalized database

I have a table (tbl_operations) with rows of where the id column values may be comma-delimited.我有一个表 (tbl_operations),其中 id 列值可能是逗号分隔的行。 I want to get the count of each OpId for each month.我想获取每个月每个OpId的计数。 I am trying to accomplish this through pure sql, but without success.我试图通过纯 sql 来实现这一点,但没有成功。

from this view从这个角度来看

OpId OpId OpDate手术日期
3 3个 2022-01-03 2022-01-03
5,3 5,3 2022-01-15 2022-01-15
4 4个 2022-01-27 2022-01-27
5 5个 2022-02-01 2022-02-01
7 7 2022-02-09 2022-02-09
3,2 3,2 2022-01-16 2022-01-16

to this对此

OpId OpId count数数 Month
2 2个 1 1个 01 01
3 3个 3 3个 01 01
4 4个 1 1个 01 01
5 5个 1 1个 01 01
5 5个 1 1个 02 02
7 7 1 1个 02 02

I am stuck here.我被困在这里。 Can someone enlighten me on how to do this with sql?有人可以告诉我如何使用 sql 执行此操作吗? If not, maybe use php to display the result?如果不是,也许使用 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

Here's a quick example.这是一个简单的例子。 The first part just creates an array of arrays which simulates what you'd get from the database.第一部分只是创建一个 arrays 数组,它模拟您从数据库中获得的内容。

The gist is that $counts is an array with a unique OpID for a keys.要点是$counts是一个数组,其中的键具有唯一的 OpID。 The values for those arrays are sub-arrays with keys of the month and values of how many times they are found.那些 arrays 的值是带有月份键和它们被找到次数的值的子数组。

Display should just be a simple loop again, however you might want to sort this.显示应该再次只是一个简单的循环,但是您可能想要对其进行排序。

$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]++;
    }
}

Demo here: https://3v4l.org/mVaBB演示在这里: https://3v4l.org/mVaBB

edit编辑

From @mickmackusa, you can shorten the inner loop by using isset :来自@mickmackusa,您可以使用isset缩短内部循环:

        if(!isset($counts[$id][$month])){
            $counts[$id][$month] = 0;
        }

See their comment for a demo link查看他们的评论以获取演示链接

If you're going to query the data in PHP, you might as well return a better result to work with in the first place:如果你要查询 PHP 中的数据,你还不如先返回一个更好的结果来处理:

SQL SQL

SELECT GROUP_CONCAT(OpId), MONTH(OpDate)
FROM tbl_operations
GROUP BY MONTH(OpDate)

PHP PHP

// Result from MySQL query
$rows = [
    ['3,5,3,4,3,2', 1],
    ['5,7', 2]
];

And you can perform a count of those grouped results like this:您可以像这样对这些分组结果进行计数:

$results = [];
foreach ($rows as $row) {
    $counts = array_count_values(explode(',', $row[0]));
    $results[$row[1]] = $counts;
}

Result结果

Array
(
    [1] => Array
        (
            [3] => 3
            [5] => 1
            [4] => 1
            [2] => 1
        )

    [2] => Array
        (
            [5] => 1
            [7] => 1
        )

)


What you really want to do though is normalise your data, then you can do this easily in SQL alone.你真正想做的是标准化你的数据,然后你可以单独在 SQL 中轻松地做到这一点。

If you are using at least MYSQL8 and you are not going to normalize your table design, then you can actually use the following CTE query to split, group, format, and sort your result set (no PHP processing).如果您至少使用 MYSQL8,并且您不打算规范化您的表设计,那么您实际上可以使用以下CTE查询来拆分、分组、格式化和排序您的结果集(没有 PHP 处理)。

This approach makes recursive calls on the denormalized table and progressively isolates the rightmost id from comma-delimited values and generates new rows for the individual id values.这种方法对非规范化表进行递归调用,并逐步将最右边的 id 从逗号分隔值中分离出来,并为各个 id 值生成新行。 The recursion continues until there are no commas left.递归继续,直到没有逗号为止。

This solution is built on top of the basic technique demonstrated here .该解决方案建立在此处演示的基本技术之上。

SQL: ( Demo ) SQL:( 演示

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

Result Set:结果集:

Id ID Mo Cnt计数
2 2个 1 1个 1 1个
3 3个 1 1个 3 3个
4 4个 1 1个 1 1个
5 5个 1 1个 1 1个
5 5个 2 2个 1 1个
7 7 2 2个 1 1个

That said, this is a lot of unnecessary voodoo mumbo jumbo for a task that is mega-easy once you've normalized your table --- just normalize it ASAP就是说,对于一项非常容易的任务来说,这是很多不必要的巫术胡言乱语,一旦您对表格进行了规范化 --- 尽快对其进行规范化

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM