初学者SQL问题:在Stack Exchange Data Explorer中查询黄金和白银标记徽章

时间:2010-06-06 18:42:08

标签: sql analytic-functions

我正在使用Stack Exchange Data Explorer来学习SQL,但我认为问题的基础知识适用于其他数据库。

我正在尝试查询Badges表,根据Stexdex(我将从现在开始称之为),它具有以下模式:

  • 徽章
    • 编号
    • 用户ID
    • 名称
    • 日期

这适用于具有唯一名称的[Epic][Legendary]等徽章,但银色和金色标记特定的徽章似乎通过具有相同的名称混合在一起。

以下是我为[mysql]标记撰写的示例查询:

SELECT
  UserId as [User Link],
  Date
FROM
  Badges
Where
  Name = 'mysql'
Order By
  Date ASC

(略带注释)输出为:as seen on stexdex

User Link       Date                    
--------------- -------------------     // all for silver except where noted
Bill Karwin     2009-02-20 11:00:25     
Quassnoi        2009-06-01 10:00:16     
Greg            2009-10-22 10:00:25     
Quassnoi        2009-10-31 10:00:24     // for gold
Bill Karwin     2009-11-23 11:00:30     // for gold
cletus          2010-01-01 11:00:23    
OMG Ponies      2010-01-03 11:00:48     
Pascal MARTIN   2010-02-17 11:00:29 
Mark Byers      2010-04-07 10:00:35     
Daniel Vassallo 2010-05-14 10:00:38 

这与撰写本文时silvergold收入者的当前列表一致,但是以更加永恒的方式发言,截至2010年5月底,只有2位用户获得了黄金[mysql]标签:Quassnoi和Bill Karwin,如上述结果所示,他们的名字是唯一出现两次的名字。

所以这就是我理解的方式:

  • Id第一次出现(按时间顺序)是银徽章
  • 第二次是黄金

现在,上述结果将银色和金色条目混合在一起。我的问题是:

  • 这是一个典型的设计,还是有更友好的架构/规范化/无论你怎么称呼它?
  • 在目前的设计中,您如何分别查询银色和金色徽章?
    • GROUP BY Id并以Date某种方式选择最小/最大或第一/秒?
    • 你如何编写一个查询,然后列出所有的银徽章,然后再列出所有的金徽章?
      • 想象一下,“真正的”查询可能会更复杂,即不仅仅按日期列出。
      • 你会怎么写它以使银子和金子子查询之间没有太多的重复?
    • 相反,做两个完全独立的查询可能更典型吗?
    • 这个成语叫什么?一行“分区”查询将它们放入“桶”或什么东西?

要求澄清

最初我想要以下输出,主要是:

User Link       Date                    
--------------- -------------------     
Bill Karwin     2009-02-20 11:00:25     // result of query for silver
Quassnoi        2009-06-01 10:00:16     // :
Greg            2009-10-22 10:00:25     // :
cletus          2010-01-01 11:00:23     // :
OMG Ponies      2010-01-03 11:00:48     // :
Pascal MARTIN   2010-02-17 11:00:29     // :
Mark Byers      2010-04-07 10:00:35     // :
Daniel Vassallo 2010-05-14 10:00:38     // :
------- maybe some sort of row separator here? can SQL do this? -------
Quassnoi        2009-10-31 10:00:24     // result of query for gold
Bill Karwin     2009-11-23 11:00:30     // :

但到目前为止,单独的银色和金色专栏的答案也很棒,所以也可以自由地追求这个角度。不过,我仍然很好奇你是怎么做的。

2 个答案:

答案 0 :(得分:4)

  

这是一个典型的设计,还是有更友好的架构/规范化/无论你怎么称呼它?

当然,您可以添加类型代码以使其更明确。但是当你认为在银牌之前无法获得金徽章时,日期戳很有意义区分它们。

  

在目前的设计中,您如何分别查询银色和金色徽章? GROUP BY Id并以某种方式按日期选择最小/最大或第一/秒?

是 - 加入派生表(AKA内联视图),这是一个用户列表&最短的日期将返回银徽章。使用HAVING COUNT(*) >= 1也可以。您必须使用GROUP BY和HAVING COUNT(*)= 2`的组合来获取金徽章 - 最大日期不能确保用户ID有多个记录...

  

如何编写一个查询,首先列出所有的银徽章,然后是所有的金徽章?

很抱歉 - 首先是用户,还是所有银牌,然后是金牌?前者可以简单地使用ORDER BY t.userid, t.date完成;后者我可能会使用分析函数(IE:ROW_NUMBER(),RANK())......

  

相反,做两个完全独立的查询可能更典型吗?

无论如何,请参阅上文,了解您的要求是多么模糊......

  

这个成语叫什么?一行“分区”查询将它们放入“桶”或其他东西?

您所询问的内容由以下同义词引用:Analytic,Windowing,ranking ...

答案 1 :(得分:3)

你会做这样的事情,只依赖日期或聚合计数。

可以说,查询白银后跟黄金也没有意义,而是像这样并排获取数据:

不幸的是,你还没有真正指定你想要的东西,但聚合的一个好的起点是用简单的英语表达

示例:“为每个用户提供标签mysql的银色和金色徽章奖励日期”。这是做什么的:

SELECT
  UserId as [User Link],
  min(Date) as [Silver Date],
  case when count(*) = 1 THEN NULL ELSE max(date) END
FROM
  Badges
Where
  Name = 'mysql'
group by
  UserId
Order By
  case when count(*) = 1 THEN NULL ELSE max(date) END DESC, min(Date)

更新后编辑:

您想要的输出不是SQL:它是2个独立的记录集。分离器是不行的。作为基于setb的操作,没有“自然”顺序,因此这引入了一个:

SELECT
  UserId as [User Link],
  min(Date) as [Date],
  0 as dummyorder
FROM
  Badges
Where
  Name = 'mysql'
group by
  UserId
union all
select
  UserId as [User Link],
  max(Date) as [Date],
  1 as dummyorder
FROM
  Badges
Where
  Name = 'mysql'
group by
  UserId
having
  count(*) = 2
Order By
  dummyorder, Date