Вопрос SQL для начинающих: запрос значков с золотыми и серебряными метками в Stack Exchange Data Explorer - PullRequest
3 голосов
/ 06 июня 2010

Я использую Stack Exchange Data Explorer для изучения SQL, но я думаю, что основы этого вопроса применимы к другим базам данных.

Я пытаюсь запросить таблицу Badges, которая согласно Stexdex (это то, что я буду называть ее теперь) имеет следующую схему:

  • Значки
    • Id
    • UserId
    • Имя
    • Дата

Это хорошо работает для значков, таких как [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 года 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 ]

4 голосов
/ 06 июня 2010

Это типичный дизайн, или есть более дружелюбная схема / нормализация / как вы это называете?

Конечно, вы можете добавить код типа, чтобы сделать его более явным. Но если учесть, что нельзя получить золотой значок раньше, чем серебряный, штамп даты имеет большой смысл различать между ними.

В текущем дизайне, как бы вы запросили серебряные и золотые значки отдельно? GROUP BY Id и выбор минимальной / максимальной или первой / второй по дате как-нибудь?

Да - присоединение к производной таблице (встроенное представление AKA), которая представляет собой список пользователей, и минимальная дата выдаст серебряные значки. Использование HAVING COUNT(*) >= 1 тоже подойдет. Чтобы получить золотые значки, вам придется использовать комбинацию GROUP BY и HAVING COUNT (*) = 2` - максимальная дата не гарантирует, что для идентификатора пользователя существует более одной записи ...

Как вы можете написать запрос, в котором сначала перечислены все серебряные значки, а затем все золотые значки?

Извините - по пользователям, или все серебро сначала, а потом золото? Первое можно сделать просто с помощью ORDER BY t.userid, t.date; последний, вероятно, я бы использовал аналитические функции (IE: ROW_NUMBER (), RANK ()) ...

Возможно, более типично вместо этого делать два совершенно разных запроса?

Смотри выше о том, насколько расплывчаты твои требования, для меня в любом случае ...

Как называется эта идиома? Запрос на "разбиение" строки, чтобы поместить их в "ведра" или что-то в этом роде?

То, о чем вы спрашиваете, обозначается следующими синонимами: аналитика, управление окнами, ранжирование ...

3 голосов
/ 06 июня 2010

Вы будете делать что-то подобное и полагаться только на дату или рассчитывать в совокупности.

Возможно, также нет смысла запрашивать серебро, за которым следует золото, а скорее получать данные бок о бок, как это:

К сожалению, вы на самом деле не указали, что хотите, но хорошей отправной точкой для агрегатов является выражение на простом английском языке

Пример: "Дайте мне даты серебряных и золотых наград за каждый пользователь для тега 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
...