【问题标题】:Efficient way to query SQL and format to CSV查询 SQL 并格式化为 CSV 的有效方法
【发布时间】:2012-06-30 23:01:49
【问题描述】:

我有一个 MySQL 数据库,用于保存来自多个不同传感器的温度读数。我最初想使用三个不同的表来存储我的数据:

mysql> select * from sensor_info;
+----+------------------+------+--------+
| id | address          | name | active |
+----+------------------+------+--------+
|  1 | 28D684CD02000057 | NULL |      1 |
|  2 | 28099B49030000D8 | NULL |      1 |
|  3 | 28339ACD0200004B | NULL |      1 |
+----+------------------+------+--------+

mysql> select * from data_period limit 4;
+----+---------------------+
| id | ts                  |
+----+---------------------+
|  1 | 2012-06-30 09:35:02 |
|  2 | 2012-06-30 09:36:22 |
|  3 | 2012-06-30 09:37:46 |
|  4 | 2012-06-30 09:40:36 |
+----+---------------------+

mysql> select * from data_points limit 4;
+----+-------------+-----------+-------+
| id | data_period | sensor_id | data  |
+----+-------------+-----------+-------+
|  1 |           1 |         1 | 77.90 |
|  2 |           1 |         2 | 77.34 |
|  3 |           1 |         3 | 77.56 |
|  4 |           2 |         1 | 78.01 |
+----+-------------+-----------+-------+

我想要做的是获取我存储的数据并将其放入 CSV 文件中,以便我可以使用 dygraphs Javascript library 显示它。我需要把我的数据变成这样的格式:

date,temp1,temp2,temp3
2012-06-30 09:35:02,77.90,77.34,77.56
2012-06-30 09:36:22,78.01,77.36,77.59
....

我开始执行此操作的每一种方式(使用 PHP),似乎都使这变得过于复杂,并且必须将查询放在循环内的循环内。我是否让自己变得比我需要的更难?

大部分工作是使用查询还是使用 PHP 完成?以后,我还想添加代码,如果特定时间戳中缺少温度读数,则会在 CSV 中放置 NULL。

我不是在寻找一个非常具体的答案,我只是想知道我应该往哪个方向走。我什至不知道如何开始格式化数据库中的数据,或者我是否应该尝试查看不同的格式来将我的信息存储在数据库中。

【问题讨论】:

标签: php mysql


【解决方案1】:

我会运行一个单一的选择查询来将很多东西连接在一起。您可以在可能没有数据的地方使用外连接。

SELECT data_period.ts AS date, dp1.data AS temp1, dp2.data AS temp2, dp3.data AS temp3
FROM data_period
LEFT OUTER JOIN data_points AS dp1 ON dp1.data_period=data_period.id AND dp1.sensor_id=1
LEFT OUTER JOIN data_points AS dp2 ON dp2.data_period=data_period.id AND dp2.sensor_id=2
LEFT OUTER JOIN data_points AS dp3 ON dp3.data_period=data_period.id AND dp3.sensor_id=3

(参见:SQL Fiddle Example

这应该会给你一组结果,你可以循环遍历。

如果你真的想让 MySQL 做大部分工作,你可以把第一行改成(我认为) SELECT data_period.ts +','+ IFNULL(dp1.data,'NULL') +','+ IFNULL(dp2.data,'NULL') +','+ IFNULL(dp3.data,'NULL')

综合评论和答案并将其保存到文件中:

SELECT data_period.ts AS date, dp1.data AS temp1, dp2.data AS temp2, dp3.data AS temp3
INTO OUTFILE '/home/user/output.csv'
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
ESCAPED BY '\\'
LINES TERMINATED BY '\n'
FROM data_period LEFT OUTER JOIN data_points AS dp1 ON dp1.data_period=data_period.id      
AND dp1.sensor_id=1 LEFT OUTER JOIN data_points AS dp2 ON  
dp2.data_period=data_period.id   AND dp2.sensor_id=2 LEFT OUTER JOIN data_points AS dp3 
ON dp3.data_period=data_period.id AND dp3.sensor_id=3;

【讨论】:

    【解决方案2】:

    为此,您可以使用两个 SQL 语句,给出一个外循环和一个内循环。第一条语句

    select  ts from data_points as point 
    inner join data_period on data_period = data_period.id 
    group by ts 
    order by ts
    

    将获得您有数据的日期列表。

    内部循环中的第二个将获得按传感器 ID 排序的数据值列表 - 如果您在 data_points 中有缺失值,则此列表将是错误的并且有间隙;因此,如果确实存在差距,您可能需要执行额外的步骤来获取传感器列表,然后从查询返回的数据中填充此列表。

    以下示例说明了如何进行此操作。

    // get the outer list
    $sql = "select  ts from data_points as point 
            inner join data_period on data_period=data_period.id 
            group by ts 
            order by ts";
    
    $result = mysql_query($sql);
    
    $result || die(mysql_error());
    
    while($row = mysql_fetch_row($result))
    {
        // get the date for the second query
        $date = $row[0];
    
        $sql = "select  data from data_points as point
                inner join data_period on data_period=data_period.id
                inner join sensor_info on sensor_id = sensor_info .id
                where ts = '$date'
                order by sensor_id";
    
        $result_1 = mysql_query($sql);
    
        // now create an array from the values - we could use mysql_fetch_array to do this
        // but this way allows us the possibility of extending the processing to 
        // take into account missing values.
        $vals = array();
        while($row_1 = mysql_fetch_row($result_1)) {
            $vals[]=$row_1[0];
        }
        print "$date,".join(",",$vals)."\n";
    }
    

    【讨论】:

    • 这正是我希望完成的。我总是卡在 SQL 查询上。内连接/外连接/等是我很难掌握的概念。
    • 有关连接的详细说明,请参阅codinghorror.com/blog/2007/10/…
    【解决方案3】:

    我建议事后在 php 中进行 csv 格式设置。

    这是我在网上找到并且非常喜欢使用的一个方便的功能。如果您将 SQL 调用包装在一个类中,那么您可以在查询后使用此方法。 :)

    请注意,我将其修改为 MSSQL,但很容易用 mysql 替换它。

      public final function SQL2CSV() {
            // set initial .csv file contents
            $CSV = array();
            $rowid = 0;
            //This would be your query.
            $res = $this->fetchQuery();
    
            // get column captions and enclose them in doublequotes (") if $print_captions is not set to false
                for ($i = 0; $i < mssql_num_fields($res); $i++) {
                    $fld = mssql_fetch_field($res, $i);
                    $colnames[] = $fld->name;
                }
                // insert column captions at the beginning of .csv file
                $CSV[$rowid] = implode(",", $colnames);
    
    
            // iterate through each row
            // replace single double-quotes with double double-quotes
            // and add values to .csv file contents
            if (mssql_num_rows($res) > 0) {
                while ($row = mssql_fetch_array($res, MSSQL_NUM)) {
                    $rowid++;
                    for ($i = 0; $i < sizeof($row); $i++)
                        //$row[$i] = '"'.str_replace('"', '""', $row[$i]).'"';
                        $row = str_replace (',', '', $row);
    
                    $CSV[$rowid] = "\n".implode(",", $row);
    
                }
            }
    
    
            RETURN $CSV;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-15
      • 1970-01-01
      • 2018-05-03
      • 2014-02-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多