0

我需要执行一组相当复杂的 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 调用时数据以正确的顺序开始会更好。

抱歉这个大问题,感谢您的帮助!

4

1 回答 1

0

这是一个将为每个艺术家执行一次的查询,而不是每个艺术家月的组合。artist_id它通过加入子查询然后artist_id = $itemID在 where 子句中过滤来解决您的第一个问题。数据库引擎在处理查询时应该将条件下推到子查询中,因此它不会像看起来那样效率低下,而且由于它不是在一个月循环内调用的,因此总体上应该做的工作更少。

第二个问题是通过从第一个子查询中获取总体频率,并按该频率降序排列整个结果集来解决的。这会将标记最多的月份放在首位。

这样做的缺点是没有标签的月份不会出现在结果中。您可以在应用程序逻辑中解决此问题(例如,通过跟踪每个标签在您的日期范围内没有看到哪些月份,然后合成“0”行)。也应该可以扩展查询以包括缺失的月份,但由于复杂性,除非您感兴趣,否则我不会详细说明。

select t1.tag_id
     , t2.tag_month
     , t2.freq as month_freq
     , t1.freq as total_freq
  from (select tag_id
             , artist_id
             , count(*) as freq
          from lastfm_annotations
         group by tag_id, artist_id) t1
       inner join
       (select tag_id
             , tag_month
             , artist_id
             , count(*) as freq
          from lastfm_annotations 
         group by tag_id, tag_month, artist_id) t2
       on t1.artist_id = t2.artist_id and t1.tag_id = t2.tag_id
 where t2.tag_month between '$dateRangeStart' and '$dateRangeEnd'
   and t1.artist_id = '$itemID'
 order by total_freq desc, t1.tag_id
于 2013-05-13T20:41:59.490 回答