标准化SQL数据库中数据的表格视图

时间:2013-10-22 17:49:58

标签: sql database postgresql database-design normalization

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

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

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

我不知道如何做到这一点,或者是否有更好的方法来存储这些数据。有什么建议吗?

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

2 个答案:

答案 0 :(得分:1)

根据@ PM77-1的建议,我想出了一个使用PostgreSQL的crosstab函数的替代方法。

具体来说,我正在使用函数的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.。但是,我将categoriesresults都传递给HTML模板,以使用正确的标题呈现表格。

答案 1 :(得分: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

相关问题