我尝试过创建一个规范化的数据库,但是我无法以表格格式显示数据。
在下面的示例中,数据库用于按日期跟踪数字(以各种任意命名的类别)。作为示例,用户可以跟踪每天向他的杂货店递送多少水果和蔬菜。用户定义水果和蔬菜类别的名称以及存在的类别数量。以下是与此示例对应的表:
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
由单个表表示时,条目很容易显示(条目是行,值是列)。但在这种情况下,最大类别数量受到表格中列数的限制。我更喜欢规范化方法如何允许每个“跟踪器”表示任意数量的类别。
答案 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.
。但是,我将categories
和results
都传递给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 ' ';
}
echo '</td>';
}
echo '</tr>';
}
// End our table.
echo '</tbody></table>';