简体   繁体   English

格式化 MySQL 查询的结果,就好像它是从控制台运行一样

[英]Formatting the results of a MySQL query as if it were run from the console

I'm writing a quick and dirty reporting script that queries a report and emails the results.我正在编写一个快速而肮脏的报告脚本,用于查询报告并通过电子邮件发送结果。 When using the MySQL console the results are in a nicely formatted table:使用 MySQL 控制台时,结果在一个格式良好的表中:

mysql> select * from users;
+-----------+------------+-------+
| firstname | city       | zip   |
+-----------+------------+-------+
| Maria     | Holland    | 12345 |
| Rene      | Doylestown | 65432 |
| Helen     | Conway     | 98745 |
+-----------+------------+-------+
3 rows in set (0.01 sec)

Is there an easy way to replicate this formatting when fetching the results with PHP?使用 PHP 获取结果时,是否有一种简单的方法来复制这种格式? Obviously I could achieve this by writing my own report formatter but I was hoping for something a little more elegant.显然,我可以通过编写自己的报告格式化程序来实现这一点,但我希望有一些更优雅的东西。

You could do this quite easily using the Console_Table PEAR package.您可以使用Console_Table PEAR 包轻松完成此操作。 Just loop through your MySQL results, and add rows to your table.只需遍历您的 MySQL 结果,并将行添加到您的表中。 You can use the Console_Table::setHeaders() method to add the headers for your columns, then the Console_Table::addRow() method to add each row, and finally Console_Table::getTable() to display it.您可以使用Console_Table::setHeaders()方法为您的列添加标题,然后使用Console_Table::addRow()方法添加每一行,最后使用Console_Table::getTable()来显示它。

There is nothing built into PHP to do this. PHP 没有内置任何东西来做到这一点。 If you don't want to use/write code to draw console tables, just pass -e query to mysql via PHP using passthru() .如果您不想使用/编写代码来绘制控制台表,只需使用passthru()通过 PHP 将-e query传递给 mysql。 This will work queries terminated with both ;这将工作以两者终止的查询; and \\G :\\G

passthru("mysql -e '$query;' database_name");

You have to do it yourself.你必须自己做。

do a loop to find the max size for each column.做一个循环来找到每列的最大大小。 Then output each row padding to that size +2 with a space at the beginning and end.然后将每一行填充输出到该大小+2,并在开头和结尾处留一个空格。 seperate each column with a |.用 | 分隔每一列。

Use + and - to create your top and bottom.使用 + 和 - 创建顶部和底部。

It's hard to give a concrete example without knowing what you're using to get your results.在不知道您使用什么来获得结果的情况下,很难给出一个具体的例子。 But assuming you're using mysql_query.但假设您使用的是 mysql_query。 Here's an example.这是一个例子。

$conn = mysql_connect("localhost", "mysql_user", "mysql_password");
mysql_select_db("mydbname");
$result = mysql_query("SELECT * FROM myTable");
//first get your sizes
$sizes = array();
$row = mysql_fetch_assoc($result);
foreach($row as $key=>$value){
    $sizes[$key] = strlen($key); //initialize to the size of the column name
}
while($row = mysql_fetch_assoc($result)){
    foreach($row as $key=>$value){
        $length = strlen($value);
        if($length > $sizes[$key]) $sizes[$key] = $length; // get largest result size
    }
}
mysql_data_seek($result, 0); //set your pointer back to the beginning.

//top of output
foreach($sizes as $length){
    echo "+".str_pad("",$length+2,"-");
}
echo "+\n";

// column names
$row = mysql_fetch_assoc($result);
foreach($row as $key=>$value){
    echo "| ";
    echo str_pad($key,$sizes[$key]+1);
}
echo "|\n";

//line under column names
foreach($sizes as $length){
    echo "+".str_pad("",$length+2,"-");
}
echo "+\n";

//output data
do {
    foreach($row as $key=>$value){
        echo "| ";
        echo str_pad($value,$sizes[$key]+1);
    }
    echo "|\n";
} while($row = mysql_fetch_assoc($result));

//bottom of output
foreach($sizes as $length){
    echo "+".str_pad("",$length+2,"-");
}
echo "+\n";

That would do it (I hope I didn't miss a semicolon in there :) ).这样就可以了(我希望我没有错过那里的分号:))。

Hope that helps!希望有帮助!

You could use exec or backticks and actually run it from the command line through php.您可以使用 exec 或反引号,并通过 php 从命令行实际运行它。 Apparently the mysql command has a -H switch you can use, and it will output HTML formatted.显然 mysql 命令有一个 -H 开关你可以使用,它会输出 HTML 格式。 Haven't tried it though, but that might look good too.还没有尝试过,但这可能看起来也不错。

echo '<pre>';
echo `mysql -u user -ppass -e "select * from table;" database_name`;

2 lines, no pear packages, how much more elegant can it get for a quick and dirty stats page. 2 行,没有梨包,对于一个快速而肮脏的统计页面,它可以变得更加优雅。

Building on mfonda's answer, you can really easily load the Console_Table pear package with composer now: https://packagist.org/packages/pear/console_table基于 mfonda 的回答,您现在可以很容易地使用 composer 加载Console_Table pear 包https : //packagist.org/packages/pear/console_table

$ composer require pear/console_table

<?php
//Suppress E_DEPRECATED errors for statically calling a non-static method (this package is pretty old!)
error_reporting(E_ALL & ~E_DEPRECATED);
require __DIR__ . '/vendor/autoload.php';

//echo "<pre>"; #uncomment this line if running script in a browser

//The class isn't namespaced so just call it directly like so:
echo Console_Table::fromArray(
    ['column', 'headings'], 
    [
        ['1st row', 'values'], 
        ['2nd row', 'values'], 
        ['...', '...']
    ]
);

This outputs:这输出:

+---------+----------+
| column  | headings |
+---------+----------+
| 1st row | values   |
| 2nd row | values   |
| ...     | ...      |
+---------+----------+

I optimized the answer of @ehudokai so it uses less loops (5 vs 9).我优化了@ehudokai 的答案,因此它使用更少的循环(5 对 9)。 And for completeness I added the command line, stats and error output, too:为了完整起见,我还添加了命令行、统计信息和错误输出:

<pre>
<?php
$db = mysqli_connect('localhost', 'my_user', 'my_password', 'my_db');
$start = microtime(true);
$sql = "SELECT * FROM myTable";
$result = mysqli_query($db, $sql);
$exec_time = microtime(true) - $start;
// obtain the maximum string length of all column headings and rows
$colwidths = array();
while ($row = mysqli_fetch_assoc($result)) {
    foreach ($row as $key => $value) {
        // heading
        if (!isset($colwidths[ $key ])) {
            $colwidths[ $key ] = strlen($key) + 2;
        }
        // rows
        $colwidths[ $key ] = max($colwidths[ $key ], strlen($value) + 2);
    }
}
echo 'mysql>' . trim($sql) . PHP_EOL;
// SELECT, SHOW, DESCRIBE, EXPLAIN = resource
// INSERT, UPDATE, DELETE, DROP = true
// Error = false
if (!is_bool($result)) {
    if ($colwidths) {
        mysqli_data_seek($result, 0);
        while ($row = mysqli_fetch_assoc($result)) {
            // create and display horizontal line and column headings
            if (!isset($header)) {
                $header = '| ';
                $line = '+';
                foreach ($row as $key => $value) {
                    $line .= str_repeat('-', $colwidths[ $key ] + 2) . '+';
                    $header .= str_pad($key, $colwidths[ $key ]) . ' | ';
                }
                echo $line . PHP_EOL;
                echo $header . PHP_EOL;
                echo $line . PHP_EOL;
            }
            // display row values
            foreach ($row as $key => $value) {
                echo '| ' . str_pad($value, $colwidths[ $key ] + 1);
            }
            echo '|' . PHP_EOL;
        }
        echo $line . PHP_EOL;
    }
    mysqli_free_result($result);
}
$affectedrows = mysqli_affected_rows($db);
if ($result === false) {
    echo PHP_EOL . 'ERROR ' . mysqli_errno($db) . ': ' . mysqli_error($db);
}
else if ($result === true) {
    echo 'Query OK, ' . $affectedrows . ' rows affected (' . round($exec_time / $iterations * 1000) . ' ms)';
}
else if ($affectedrows) {
    echo $affectedrows . ' rows in set (' . round($exec_time / $iterations * 1000) . ' ms)';
}
else {
    echo 'Empty set (' . round($exec_time / $iterations * 1000) . ' ms)';
}
?>
</pre>

Examples例子

SELECT选择

mysql>SELECT
        topic_id,
        MATCH(text) AGAINST('tuning') AS score
    FROM
        topics
    WHERE
        MATCH(text) AGAINST('tuning' IN BOOLEAN MODE)
    ORDER BY
        score DESC
    LIMIT 10
+----------+--------------------+
| topic_id | score              |
+----------+--------------------+
| 153257   | 5.161948204040527  |
| 17925    | 4.781417369842529  |
| 66459    | 4.648380279541016  |
| 373176   | 4.570812702178955  |
| 117173   | 4.55166482925415   |
| 167016   | 4.462575912475586  |
| 183286   | 4.4519267082214355 |
| 366132   | 4.348565101623535  |
| 95502    | 4.293642520904541  |
| 29615    | 4.178250789642334  |
+----------+--------------------+
10 rows in set (141 ms)

Error:错误:

mysql>SELECT * WHERE 1=1

ERROR 1064: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE 1=1' at line 1

UPDATE更新

mysql>UPDATE topics_search SET topic_id = topic_id WHERE topic_id = 2
Query OK, 0 rows affected (0 ms)

This doesn't make any sense bearing in mind the manner in which you fetch data from MySQL in PHP.考虑到您在 PHP 中从 MySQL 获取数据的方式,这没有任何意义。 (ie: You generally fetch onw row of data at a time either as an array ( mysql_fetch_array ) or an object ( mysql_fetch_object ).) (即:您通常以数组( mysql_fetch_array )或对象( mysql_fetch_object )的形式一次获取一行数据。)

As such, you'd need to write your own hack to grab all of the rows and format the output in this manner.因此,您需要编写自己的 hack 来获取所有行并以这种方式格式化输出。 (That said, it should be trivial to grab the data and output it as an HTML table - you could get the field names via array_keys if you use mysql_fetch_array, etc. ) (也就是说,获取数据并将其输出为 HTML 表应该很简单 - 如果您使用 mysql_fetch_array 等,您可以通过array_keys获取字段名称。)

it sounds like you just need to use any one of the exec methods or backticks.听起来您只需要使用任何一种 exec 方法或反引号。 I'm not sure about the '\\G' thingy... but, I published a php function called query2Table() a few months ago @ http://www.logicwizards.net/php-query2table -- based on a function I've been recycling for years.我不确定“\\G”这个东西......但是,几个月前我发布了一个名为 query2Table() 的 php 函数@http: //www.logicwizards.net/php-query2table——基于一个函数我已经回收多年了。 I have a bunch that I've accumulated over the years: query2xml, query2excel, query2json, etc. I think I still have the old perl & asp versions somewhere, too.我有一些我多年来积累的东西:query2xml、query2excel、query2json 等。我想我仍然有旧的 perl 和 asp 版本。

Basically, in my solution, you can just pass it the query string and it dynamically spit's out an html table using column names fetched from the results as the table's header row.基本上,在我的解决方案中,您只需将查询字符串传递给它,它就会使用从结果中提取的列名作为表的标题行动态地吐出一个 html 表。 It also grows to fill the width of it's inherited container object.它还会增长以填充其继承的容器对象的宽度。

  query2table("select * from table;");

I have a more up to date version query2AjaxTable() which wraps everything up nicely in a class and adds jQuery sorting & animations -- but it's not ready for publishing yet.我有一个更新的版本 query2AjaxTable(),它把所有东西都很好地封装在一个类中,并添加了 jQuery 排序和动画——但它还没有准备好发布。

If my silly little function doesn't help you, in your dilemma, maybe someone else will find it useful...如果我这个愚蠢的小功能对你没有帮助,在你的困境中,也许别人会发现它有用......

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

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