【问题标题】:Creating an "average grade list" for ranked IDs in SQL在 SQL 中为排名 ID 创建“平均成绩列表”
【发布时间】:2012-11-16 11:20:10
【问题描述】:

问题描述

我正在尝试为每个推荐获取一个以逗号分隔的平均成绩列表,其中包含另一个以逗号分隔的推荐内容 ID 列表。推荐是一个对象,它由将接收推荐的内容 (ContentID) 和将推荐的其他内容列表 (RecommendedContentIDs) 组成。

表结构、样本数据和其他限制

我有一个两表数据库结构。第一个表包含保存为逗号分隔的排名列表的推荐内容 ID。第二个表包含每个推荐内容 ID 的等级。排名列表最多有 10 个逗号分隔值,等级范围从 0 到 5。

为了更好地说明问题,以下是表结构和一些示例数据:

Table Recommendations

|ID    |ContentID    |RecommendedContentIDs |Type |
+------+-------------+----------------------+-----+
|1     |2051         |9706,14801,13354,...  |a    |
+------+-------------+----------------------+-----+
|67    |2051         |8103,16366,8795,...   |b    |
+------+-------------+----------------------+-----+
|133   |2051         |8795,8070,15341,...   |c    |
+------+-------------+----------------------+-----+
|22    |1234         |4782,283,33,...       |a    |
+------+-------------+----------------------+-----+
...

Table Grades

|ID    |RecommendationID |RecommendedDocumentID |Grade |EvaluatorHash|
+------+-----------------+----------------------+------+-------------+
|1     |1                |9706                  |4     |123456789    |
+------+-----------------+----------------------+------+-------------+
|2     |1                |14801                 |5     |123456789    |
+------+-----------------+----------------------+------+-------------+
|3     |1                |13354                 |3     |987654321    |
+------+-----------------+----------------------+------+-------------+
|3     |1                |9706                  |3     |987654321    |
+------+-----------------+----------------------+------+-------------+
|4     |67               |8103                  |5     |123456789    |
+------+-----------------+----------------------+------+-------------+
|1     |67               |16366                 |4     |987654321    |
+------+-----------------+----------------------+------+-------------+
|1     |133              |8795                  |2     |123456789    |
+------+-----------------+----------------------+------+-------------+
...

我已将 Recommendations 表中的 RecommendedContentIDs 列转换为一个单独的表,如下所示:

Table RecommendedContent

|ID    |RecommendationID |RecommendedContentID |Rank |
+------+-----------------+---------------------+-----+
|1     |1                |9706                 |1    |
+------+-----------------+---------------------+-----+
|2     |1                |14801                |2    |
+------+-----------------+---------------------+-----+
|3     |1                |13354                |3    |
+------+-----------------+---------------------+-----+
|4     |1                |12787                |4    |
+------+-----------------+---------------------+-----+
...

+------+-----------------+---------------------+-----+
|11    |2                |19042                |1    |
+------+-----------------+---------------------+-----+
|12    |2                |13376                |2    |
+------+-----------------+---------------------+-----+
|13    |2                |9853                 |3    |
+------+-----------------+---------------------+-----+

预期结果

我现在想进行一个查询,该查询将返回一个结果集,该结果集包含两个对应的逗号分隔列表,以便我能够显示每个推荐内容 ID 的平均成绩。它应该看起来像这样:

|ContentID    |RecommendedContentIDs    |RecommendedContentAverageGrades   |Type  |
+-------------+-------------------------+----------------------------------+------+
|2051         |9706,14801,13354,...     |3.5,5.0,3.0,...                   |a     |
+-------------+-------------------------+----------------------------------+------+
|2051         |8103,16366,8795,...      |5.0,4.0,0.0,...                   |b     |
+-------------+-------------------------+----------------------------------+------+
|2051         |8795,8070,15341,...      |2.0,0.0,0.0,...                   |c     |
+-------------+-------------------------+----------------------------------+------+
...

如您所见,RecommendedContentAverageGrades 列包含RecommendedContentIDs 列中每个对应 ContentID 的平均评分(ID 为 9706 的内容被评分了两次,一次是 4,一次是 3因此平均值为 3.5)。如果内容没有评分,平均评分应该是0。这里真正重要的是两个逗号分隔的列表是correspondent,因为RecommendedContentIDs中的列表是一个排名列表。

我通常会在 C# 中实现类似的东西,但我想知道它是否可以用 SQL 来完成。我正在考虑使用GROUP_CONCAT,但我无法获得正确的结果集。如果有人能为 MySQL 和/或 T-SQL 提供一个有效的 SQL 查询,我将非常感激,但只是建议也可以。

编辑

#1 - Laurence 提到使用单独的表格而不是逗号分隔的列表。由于旧设计,我正在使用它们,我无法更改。但是,我愿意接受假设逗号分隔列表中的数据存储在单独的表中的答案。

#2 - 像 Laurence 建议的那样更改了结构(使用分隔表 - 请参阅更新的结构)。

【问题讨论】:

  • 有什么特殊原因不能使用单独的表格而不是逗号分隔的列表吗?这样做只会让自己痛苦。
  • 这也是我的想法,但不幸的是这是一个旧设计(不是我的),我目前无法更改。如果您有一个适用于单独表格的解决方案,请将其作为答案分享。我将根据逗号分隔列表中的数据位于单独表格中的假设来编辑问题以鼓励回答。

标签: mysql sql tsql csv average


【解决方案1】:

这只是跟进@Laurence 给出的答案:

http://sqlfiddle.com/#!2/7d236/6

【讨论】:

  • 谢谢你,没有意识到 group_concat 处理空值的方式。您可以使用合并简化平均值:sqlfiddle.com/#!2/7d236/8
  • 我尝试了 Laurence 的解决方案和你的解决方案,这与我需要的非常接近,现在唯一的缺点是之前的RecommendedContentID 排名顺序被搞砸了。有没有办法保持原来的顺序?
  • @brozo SQL 对表中的行顺序没有真正的概念。推荐中是否有一些列组合可以提供您需要的顺序?
  • @Laurence,订单是从之前的流程中获得的。之前的开发人员认为最好将有序列表保存为逗号分隔的 ID 字符串。我现在已经对此进行了转换,以反映您对使用单独表格的建议。但是,我可以运行一个查询,该查询将 ContentID 作为输入并返回以 SQL 结果集的形式排列的这些 ID。您对如何将其添加到您的解决方案有任何建议吗?
  • @brozo 如果值的序列在逗号分隔列表中很重要,那么新表也需要一个序列号。
【解决方案2】:

更新了 Akrigg 的修复和 sql fiddle,以及如何按推荐表中的值排序 还根据 brozo 的修复在 group_concat 子句中使用 order by 进行了更新:

Table RecommendedContent

+-----------------+----------------------+
|RecommendationID | RecommendedContentID |
+-----------------+----------------------+
| 1               | 9706                 |
| 1               | 14801                |
| 1               | 13354                |
| 67              | 8103                 |
| ...             | ...                  |
+-----------------+----------------------+

Select
  a.RecommendationID,
  a.ContentID,
  Group_Concat(a.RecommendedContentId Order By a.Rank),
  Group_Concat(Trim(Trailing '.' From Trim(Trailing '0' From a.AverageGrade)) Order By a.Rank),
  a.Type
From (
  Select
    r.RecommendationID,
    r.ContentID,
    r.Type,
    rc.RecommendedContentID,
    rc.Rank,
    Coalesce(Avg(g.Grade), 0) As AverageGrade
  From
    Recommendations r
      Left Outer Join
    RecommendedContent rc
      On r.RecommendationID = rc.RecommendationID
      Left Outer Join
    Grades g
      On rc.RecommendedContentID = g.RecommendedDocumentID And
         rc.RecommendationID = g.RecommendationID
  Group By
    r.RecommendationID,
    r.ContentID,
    r.Type,
    rc.RecommendedContentID,
    rc.Rank
  ) as a
Group By
  a.RecommendationID,
  a.ContentID,
  a.Type
Order By
  a.ContentID, -- Or other way round if that's what you prefer
  a.RecommendationID

http://sqlfiddle.com/#!2/ca8b8/8

【讨论】:

    【解决方案3】:

    您可以在 sql server 中 create a custom aggreate 进行逗号分隔的字符串连接,然后像这样使用它:

    SELECT ContentID, RecommendedContentIDs, CustomToCsv(AvgGrade), Type FROM
    (
        SELECT ContentID, RecommendedContentIDs, AVG(Grade) AvgGrade, Type 
        FROM Recommendations r INNER JOIN  Grades g ON r.ID = g.RecommendationID
        GROUP BY ContentID, RecommendedContentIDs, RecommendedDocumentID, Type
    ) as t
    GROUP BY ContentID, RecommendedContentIDs, Type
    

    【讨论】:

    • 我试过了,但我没有在 MySQL 中使用自定义聚合,而是使用了 GROUP_CONCAT。结果是正确的格式,但对于未评分的推荐内容 ID,我没有得到零。此外,它对平均成绩进行排序的顺序与RecommendedContentIDs 列表不对应。您对此有什么建议吗?
    • 我会尝试使用 LEFT JOIN insted of INNER JOIN 并将 AVG(Grade) 替换为 AVG(ISNULL(Grade,0) 以包括“未分级”的
    • RecommendedContentIDs 命令背后有什么逻辑吗?在应用 GROUP_CONCAT 之前,您必须确保聚合结果中的顺序相同
    • 是的,RecommendedContentIDs 已订购。如何确保 GROUP_CONCAT 之前的聚合结果中的顺序相同?
    • id 的顺序似乎反映了成绩表的行顺序。在这种情况下,在您的联接中交换表就足够了->“Grades g RIGHT JOIN Recommendations r ON r.ID = g.RecommendationID”->
    【解决方案4】:

    这是在 oracle 中完成的

    WITH count_number AS
      (SELECT 
        ContentID,
        ','
        ||RecommendedContentIDs
        ||',' new_ContentIDs,
        RecommendedContentIDs,
        type ,
        LENGTH(RECOMMENDEDCONTENTIDS )-LENGTH(REPLACE(RECOMMENDEDCONTENTIDS ,','))+1 COUNT_ID
      FROM Recommendations
      ) ,
      RecommendedContentIDs_postion AS
      (SELECT A1.*,
        B1.CONTENTIDS_OCCURANCE_POSITION ,
        SUBSTR(new_ContentIDs,instr(new_ContentIDs,',',1,ContentIDs_OCCURANCE_POSITION)+1 , INSTR(new_ContentIDs,',',1,ContentIDs_OCCURANCE_POSITION+1)-instr(new_ContentIDs,',',1,ContentIDs_OCCURANCE_POSITION)-1) ContentIDs
      FROM count_number a1,
        (SELECT I ContentIDs_OCCURANCE_POSITION
        FROM DUAL model dimension BY (1 i) measures (0 X) (X[FOR I
        FROM 2 TO 1000 increment 1] = 0)
        ) b1
      WHERE b1.ContentIDs_OCCURANCE_POSITION<=a1.count_id
      )
    SELECT 
      CONTENTID,
      WM_CONCAT(CONTENTIDS) RECOMMENDEDCONTENTIDS ,
      WM_CONCAT(GRADE) avg_grade_contentid ,
      type
    FROM RECOMMENDEDCONTENTIDS_POSTION RCI,
      (SELECT RECOMMENDEDDOCUMENTID,
        AVG(GRADE) GRADE
      FROM Grades
      GROUP BY RECOMMENDEDDOCUMENTID
      ) GRD
    WHERE TRIM(RCI.CONTENTIDS)=TRIM(GRD.RECOMMENDEDDOCUMENTID)
    GROUP BY 
      ContentID,
      type;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-25
      • 2015-05-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多