2

我尝试创建一个规范化的数据库,但无法以表格格式显示数据。

在下面的示例中,数据库用于按日期跟踪数字(在各种任意命名的类别中)。例如,用户可以跟踪每天有多少水果和蔬菜被送到他的杂货店。用户定义水果和蔬菜类别的名称,以及存在多少类别。以下是与此示例对应的表格:

一张tracker表:

 id |      name       
----+---------------------
  1 | Grocery deliveries 
  2 | Sports cars

一张entries表:

 id |      datetime       | tracker_id 
----+---------------------+------------
  1 | 2013-10-01 00:00:00 |          1
  2 | 2013-10-02 00:00:00 |          1
  3 | 2013-10-03 00:00:00 |          1

一张values表:

 id | number | entry_id | category_id 
----+--------+----------+-------------
  1 |   10.0 |        1 |          1
  3 |   20.0 |        1 |          2
  5 |   21.0 |        1 |          3
  7 |   18.0 |        2 |          2
  8 |    4.0 |        3 |          1
  9 |    9.0 |        3 |          2

还有一张category桌子:

 id |     name        | tracker_id 
----+-----------------+------------
  1 | Tomatoes        |          1
  2 | Carrots         |          1
  3 | Brussel sprouts |          1
  4 | Ferraris        |          2

我想为跟踪器 1 打印一个表格,每一行对应一个日期(没有重复的日期)。这些列将是:日期、第 1 类(西红柿)、第 2 类(胡萝卜)、第 3 类(球芽甘蓝)。如果在给定日期没有给定类别的值,它将为空或显示为 null。所以,理想情况下,它看起来像这样:

 datetime            | Tomatoes | Carrots | Brussel sprouts
---------------------+----------+---------+-----------------
 2013-10-01 00:00:00 | 10.0     | 20.0    | 21.0
 2013-10-02 00:00:00 | Null     | 18.0    | Null
 2013-10-03 00:00:00 | 4.0      | Null    | 9.0

我不确定如何执行此操作,或者是否有更好的方法来存储这些数据。有什么建议么?

entries当和values由单个表表示(条目是行,值是列)时,显示数据很容易。但在这种情况下,最大类别数受到我表中列数的限制。我更喜欢标准化方法如何允许每个“跟踪器”表示任意数量的类别。

4

2 回答 2

1

crosstab正如@PM77-1 所建议的那样,我想出了一个使用 PostgreSQL 函数的替代方案。

具体来说,我使用的crosstab(text source_sql, text category_sql)是函数的形式,如下:

SELECT * FROM 
  crosstab('SELECT e.datetime, v.category_id, v.number 
            FROM entries e, values v 
            WHERE v.entry_id = e.id AND e.tracker_id = 1 ORDER BY 1, 2', 
           'SELECT id FROM categories WHERE tracker_id = 1 ORDER BY 1')
  AS (row_name timestamp without time zone,
      tomatoes numeric,
      carrots numeric,
      brussel_sprouts numeric);

使用这种方法,每个跟踪器的AS (...)术语必须是唯一的,因为每个跟踪器的类别数量及其名称可能不同。就我而言,我正在使用 Python 和 psycopg2 模块执行查询,因此动态生成查询很简单。例如,

# Retrieve the category names for the current tracker
cur.execute("SELECT name FROM categories WHERE tracker_id = " + 
            str(tracker_id) + ";")
categories = cur.fetchall()
category_count = len(categories)

# Generate category string
cat_str = '';
for n in range(category_count):
    cat_str = cat_str + ", cat_" + str(n) + " numeric"

cur.execute("SELECT * FROM crosstab("
            "'SELECT e.datetime, v.category_id, v.number FROM entries e, values v"
            " WHERE v.entry_id = e.id"
            " AND e.tracker_id = " + str(tracker_id) +
            " ORDER BY 1, 2;',"
            " 'SELECT id FROM categories WHERE tracker_id =" + 
            str(tracker_id) + "')"
            " AS (row_name timestamp without time zone" + cat_str + ");")

results = cur.fetchall()

结果具有通用列名cat_0, cat_1, etc.而不是tomatoes, carrots, etc.. 但是,我将categories和都传递results给 HTML 模板,以使用正确的标题呈现表格。

于 2013-10-23T17:55:57.457 回答
0

以下是我将如何定义您的表格:

deliveries
    id          unsigned int(P)
    good_id     unsigned int(F goods.id)
    qwhen       datetime
    quantity    double

+----+---------+------------+----------+
| id | good_id | qwhen      | quantity |
+----+---------+------------+----------+
|  1 |       1 | 2013-10-01 |     10.0 |
|  2 |       2 | 2013-10-01 |     20.0 |
|  3 |       3 | 2013-10-01 |     21.0 |
|  4 |       2 | 2013-10-02 |     18.0 |
|  5 |       1 | 2013-10-03 |      4.0 |
|  6 |       2 | 2013-10-03 |      9.0 |
|  7 |       1 | 2013-10-01 |      3.0 |
| .. | ....... | ...........| ........ |
+----+---------+------------+----------+

good_types
    id                  unsigned int(P)
    name                varchar(50)

+----+-------------+
| id | name        |
+----+-------------+
|  1 | Groceries   |
|  2 | Sports cars |
+----+-------------+

goods
    id              unsigned int(P)
    good_type_id    unsigned int(F good_types.id)
    name            varchar(50)

+----+--------------+-----------------+
| id | good_type_id | name            |
+----+--------------+-----------------+
|  1 |            1 | Tomatoes        |
|  2 |            1 | Carrots         |
|  3 |            1 | Brussel Sprouts |
|  4 |            2 | Ferraris        |
| .. | ............ | ............... |
+----+--------------+-----------------+

这是获取列名的 SQL:

SELECT id, name
FROM goods
WHERE good_type_id = 1

+----+-----------------+
| id | name            |
+----+-----------------+
|  1 | Tomatoes        |
|  2 | Carrots         |
|  3 | Brussel Sprouts |
+----+-----------------+

这是获取要在表中显示的数据的 SQL:

SELECT qwhen, good_id, sum(quantity) AS total
FROM deliveries d
LEFT JOIN goods g ON d.good_id = g.id
WHERE good_type_id = 1
GROUP BY qwhen, good_id

+------------+---------+-------+
| qwhen      | good_id | total |
+------------+---------+-------+
| 2013-10-01 |       1 |    13 |
| 2013-10-01 |       2 |    20 |
| 2013-10-01 |       3 |    21 |
| 2013-10-02 |       2 |    18 |
| 2013-10-03 |       1 |     4 |
| 2013-10-03 |       2 |     9 |
+------------+---------+-------+

因此,您将使用 PHP、Java 或任何您的高级语言来遍历两个查询结果以显示数据。下面是显示数据的 PHP 代码,PHP 代码下面是显示显示内容的图像。

// Get the column headers
$sql = 'SELECT id, name FROM goods WHERE good_type_id = 1';
$stmt = $pdo->prepare($sql);
$stmt->execute();

// Start our table.
echo '<table border="1" cellspacing="0"><thead>';

// Print out the headers.
echo '<tr>';
echo '<th>Date</th>';
while ($row = $stmt->fetch()){
    echo '<th>'. $row['name'] .'</th>';
    $columns[$row['id']] = $row['name'];
}
echo '</tr>';
echo '</thead><tbody>';

// Get the data.
$sql = 'SELECT qwhen, good_id, sum(quantity) AS total FROM deliveries d LEFT JOIN goods g ON d.good_id = g.id WHERE good_type_id = 1 GROUP BY qwhen, good_id';
$stmt = $pdo->prepare($sql);
$stmt->execute();

// Manipulate the data into an array.
$save_date = NULL;
while ($row = $stmt->fetch()){
    if ($save_date !== $row['qwhen']){
        $save_date = $row['qwhen'];
        $data[$row['qwhen']] = array();
    }
    $data[$row['qwhen']][$row['good_id']] = $row['total'];
}

// Print out the table data.
foreach ($data AS $date => $cell){
    echo '<tr>';
    echo '<td>'. $date .'</td>';
    foreach ($columns AS $id => $name){
        echo '<td align="right">';
        if (isset($cell[$id])){
            echo $cell[$id];
        }else{
            echo '&nbsp;';
        }
        echo '</td>';
    }
    echo '</tr>';
}

// End our table.
echo '</tbody></table>';

http://i.imgur.com/pX5IIum.jpg

于 2013-10-22T19:29:53.043 回答