3

我正在使用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

(略带注释的)输出是:如 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 

这与撰写本文时的当前银牌金奖获得者名单是一致的,但用更永恒的术语来说,截至 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     // :

但是到目前为止,银和金的单独列的答案也很好,所以也可以随意追求这个角度。不过,我仍然很好奇你会怎么做。

4

2 回答 2

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...

于 2010-06-06T18:59:21.983 回答
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
于 2010-06-06T19:05:31.767 回答