【发布时间】:2013-05-13 16:25:37
【问题描述】:
我需要执行一组相当复杂的 MySQL 查询,以从分配给不同音乐艺术家的标签数据库中生成适合在 D3 中绘制的数据。相关表(称为“lastfm_annotations”)中的行是:user_id、artist_id、tag_id 和 tag_month(即,我们记录了特定用户在特定时间用特定标签标记特定艺术家的许多实例)。这都嵌入在一个 php 脚本中。
最后我需要生成的是一个 JSON 对象,对于给定范围内的日期,该对象包含给定艺术家的每个唯一标签在该月中使用的次数(包括日期计数为零)没有使用给定的标签。
这是我目前所拥有的(假设 $itemID 和 artist_id 在这里可以互换):
$cal = array();
$result = mysql_query("select date from calendar;");
// this just gets all possible dates, but of course could be changed to get a different date range
if (!$result) {
echo 'Could not run query: ' . mysql_error();
exit;
}
for ($i = 0; $i < mysql_num_rows($result); $i++) {
$date =mysql_fetch_row($result)[0];
$result2 = mysql_query("select t2.tag_id, case when t1.freq is null then 0 else t1.freq end as freq from (select distinct tag_id from lastfm_annotations where artist_id='" . $itemID . "') t2 left join (select tag_id, count(*) as freq from lastfm_annotations where artist_id='" . $itemID . "' and tag_month='" . $date . "' group by tag_id) as t1 on t2.tag_id = t1.tag_id group by t2.tag_id");
$current = array();
$current['date'] = $date;
for ($j = 0; $j < mysql_num_rows($result2); $j++) {
$row = mysql_fetch_row($result2);
$tag = $row[0];
$freq = $row[1];
$result3 = mysql_query("select tag_name from lastfm_taglist where tag_id ='" . $tag . "' limit 1;");
$tagName = mysql_fetch_row($result3)[0];
$current[$tagName] = $freq;
}
array_push($data, $current);
}
echo json_encode($data);
(编辑:大查询如下)
select t2.tag_id
, case
when t1.freq is null then 0
else t1.freq
end as freq
from
(select distinct tag_id
from lastfm_annotations
where artist_id='$itemID') t2
left join
(select tag_id, count(*) as freq
from lastfm_annotations
where artist_id='$itemID'
and tag_month='$date'
group by tag_id) as t1
on t2.tag_id = t1.tag_id
group by t2.tag_id
(结束编辑。)
这可行,但(至少)有两个我无法弄清楚的大问题。首先,在大丑陋的 SQL 查询中,我正在做多余的工作调用
(select distinct tag_id from lastfm_annotations where artist_id='" . $itemID . "')
每次我通过循环,即使每次的值都是一样的。知道我该如何解决吗?也许可以以某种方式将唯一的 tag_ids 保存为 php 数组,然后将其插入到查询中?
其次,我需要确保标签始终按它们的整体频率排序(即在所有时间,而不仅仅是在特定月份内),但我不确定如何做到这一点。我可以使用如下查询获得正确的顺序:
select tag_id, count(*) as freq from lastfm_annotations where item_id=XXX order by freq desc
但我需要确保循环中的每个查询都以相同的顺序返回标签。有任何想法吗?当我实际开始绘制数据时,也许在 d3 中处理排序会更好,但如果在我进行 SQL 调用时数据以正确的顺序开始,那会更好。
抱歉这个大问题,感谢您的帮助!
【问题讨论】:
-
感谢 Alanyst 的编辑 - 现在可读性更高了。
-
旁注:您可以在查询中使用更简洁的
ifnull(t1.freq, 0)而不是case when t1.freq is null then 0 else t1.freq end。 -
如果标签 'foo' 在特定月份用于艺术家 A,但从未用于艺术家 B,如果艺术家 B 的结果中有标签 'foo' 的行(
freq== 0),还是 B 的结果应该只有 B 使用过的标签? -
B 的结果应该只包含艺术家 B 使用过的标签(至少一次)。原则上,我不介意在其中使用频率为零的其他标签,但数据库中有大约一百万个唯一标签,因此这会使数据结构变得不合理地大(和稀疏)。