简体   繁体   English

即使没有结果,也返回单个 MySql 表中所有日期的数据

[英]Return data for all dates within a single MySql table even when no results

I have a single table of data (simplified below)我有一个数据表(简化如下)

| id | date        | product |
|----|-------------|---------|
| 1  | 2019-01-02  | prod 1  |
| 2  | 2019-01-02  | prod 2  |
| 3  | 2019-01-22  | prod 1  |
| 4  | 2019-02-02  | prod 1  |
| 5  | 2019-02-02  | prod 1  |
| 6  | 2019-03-02  | prod 1  |

I would like the end result to look like this...我希望最终结果看起来像这样......

| product | 2019-01 | 2019-02 | 2019-03 |
|---------|---------|---------|---------|
| prod 1  | 2       | 3       | 0       |
| prod 2  | 1       | 0       | 0       | 

In short, I would like to loop over all products between a date range (all dates are contained in a the single table).简而言之,我想遍历日期范围内的所有产品(所有日期都包含在单个表中)。 When there's no result for a month return 0. I believe COALESCE may be the way to tackle this.当一个月没有结果时返回 0。我相信 COALESCE 可能是解决这个问题的方法。

I've attempted this two separate ways... return all data in a single query and loop over that data, but I if there's no data it never returns a string or something I can use (null).我已经尝试了这两种不同的方式......在单个查询中返回所有数据并遍历该数据,但是如果没有数据,它永远不会返回字符串或我可以使用的东西(空)。

SELECT 
    product_name, 
    count(product_name) AS count
    DATE_FORMAT(date,'%Y-%m') AS date
FROM products
GROUP BY DATE_FORMAT(date,'%Y-%m'), company_name
ORDER BY date;

| product | date    | count |
|---------|---------|-------|
| prod 1  | 2019-01 | 2     |
| prod 2  | 2019-01 | 1     |
| prod 1  | 2019-02 | 3     |
| prod 2  | 2019-02 | 0     | <--- this row doesn't return

Alternatively I've tried looping over all products and then individually looping through the dates but again no data is returned if there's no date in the table so my table would become skewed.或者,我尝试遍历所有产品,然后单独遍历日期,但如果表中没有日期,则同样不会返回任何数据,因此我的表会倾斜。

I've seen others store dates in a different table and then loop over that, this felt like overkill as I have all dates in this table, so I tried joining the table onto itself but that didn't work either.我见过其他人将日期存储在不同的表中,然后循环遍历,这感觉有点矫枉过正,因为我将所有日期都放在了这个表中,所以我尝试将表加入到自己的表中,但这也不起作用。

Thanks in advance for any help.在此先感谢您的帮助。

To get the second result you want you first need to make a CROSS JOIN between all the distinct values of date and product_name in the table.要获得您想要的第二个结果,您首先需要在表中所有不同的dateproduct_name值之间进行CROSS JOIN This can then be LEFT JOIN ed to the table again to get all the values for each product for each month, and the results grouped by product_name and month:然后可以再次LEFT JOIN到表中以获取每个月每个产品的所有值,以及按 product_name 和月份分组的结果:

SELECT 
    p.product_name, 
    count(p2.product_name) AS count,
    d.date
FROM (SELECT DISTINCT DATE_FORMAT(date,'%Y-%m') AS date
      FROM products) d
CROSS JOIN (SELECT DISTINCT product_name
            FROM products) p
LEFT JOIN products p2 ON DATE_FORMAT(p2.date,'%Y-%m') = d.date AND p2.product_name = p.product_name
GROUP BY p.product_name, d.date
ORDER BY p.product_name, d.date

Output输出

product_name    count   date
prod 1          2       2019-01
prod 1          3       2019-02
prod 2          1       2019-01
prod 2          0       2019-02

Demo on dbfiddle dbfiddle 上的演示

To make the first result is doable, but requires the use of a stored procedure to dynamically create a pivot table query.使第一个结果是可行的,但需要使用存储过程来动态创建数据透视表查询。 It's a lot easier to take the result above and process it in PHP to achieve the same result.取上面的结果并在 PHP 中处理它以达到相同的结果要容易得多。

This basically is a pivot table.这基本上是一个数据透视表。

A nice tutorial on how to achieve this can be found here: http://www.artfulsoftware.com/infotree/qrytip.php?id=78关于如何实现这一点的一个很好的教程可以在这里找到: http : //www.artfulsoftware.com/infotree/qrytip.php?id=78

Here is your dynamic solution for pivot table.这是您的数据透视表的动态解决方案。 Just use query suggested by @Nick and make temporary table of that只需使用@Nick 建议的查询并制作临时表

create temporary table tmp as 
SELECT 
    p.product_name, 
    count(p2.product_name) AS count,
    d.date
FROM (SELECT DISTINCT DATE_FORMAT(date,'%Y-%m') AS date
      FROM products) d
CROSS JOIN (SELECT DISTINCT product_name
            FROM products) p
LEFT JOIN products p2 ON DATE_FORMAT(p2.date,'%Y-%m') = d.date AND p2.product_name = p.product_name
GROUP BY p.product_name, d.date
ORDER BY p.product_name, d.date;

Now here is dynamic SP that will convert Row into column regard any new product come in future.现在这里是动态 SP,它将在未来的任何新产品中将行转换为列。

delimiter $$
DROP PROCEDURE IF EXISTS pivot $$
CREATE PROCEDURE pivot(IN schema_name VARCHAR(64) 
                       , IN table_name VARCHAR(64)  
                       , IN id_name VARCHAR(64)     
                       , IN key_name VARCHAR(64)    
                       , IN value_name VARCHAR(64))
pivot:BEGIN

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET @error := 1;
    SET @error := 0;


    SELECT MAX(character_maximum_length) 
         INTO @maxlen 
        FROM information_schema.columns
        WHERE table_schema = schema_name
          AND table_name = table_name
          AND column_name = key_name
          AND data_type IN ('char', 'varchar');

          SET @maxlen = IFNULL(@maxlen,500);

    IF @error OR !@maxlen OR @maxlen IS NULL THEN
        SELECT '@error OR @maxlen=0 OR @maxlen IS NULL', @error, @maxlen;
        LEAVE pivot;
    END IF; 
    DROP TEMPORARY TABLE IF EXISTS temp_pivot;
    SET @sql := CONCAT('CREATE TEMPORARY TABLE temp_pivot (key_name VARCHAR(',
                       @maxlen,
                       ')) ENGINE=Memory SELECT DISTINCT `',
                       key_name,
                       '` key_name FROM `',
                       schema_name,
                       '`.`',
                       table_name,
                       '`;');
    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DROP PREPARE stmt;

    SELECT GROUP_CONCAT(CONCAT( ', MAX(CASE `',
                                key_name,
                                '` WHEN ''',
                                temp_pivot.key_name,
                                ''' THEN `',
                                value_name,
                                '`   else 0  END) `',
                                temp_pivot.key_name,
                                '`') SEPARATOR '')
        INTO @sql
        FROM temp_pivot;

      DROP TEMPORARY TABLE IF EXISTS Pivot_Check;
    SET @sql := CONCAT('CREATE TEMPORARY TABLE Pivot_Check AS ','SELECT `',
                       id_name,
                       '`',
                       @sql,
                       ' FROM `',
                       schema_name,
                       '`.`',
                       table_name,
                       '` GROUP BY `',
                       id_name,
                       '`;');


    PREPARE stmt FROM @sql;
    EXECUTE stmt;
    DROP PREPARE stmt;
    SET @error := NULL;
    SET @maxlen := NULL;
    SET @sql := NULL;
END $$
delimiter 

and just call SP然后打电话给SP

call pivot('schema_name','table_name','product','count','date');

And your pivot table ready and just use您的数据透视表已准备就绪,只需使用

select * from Pivot_Check

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

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