SQL Server: динамические столбцы, возвращаемые из запроса

У меня довольно сложный SQL-запрос, работающий с data.stackexchange.com. Запрос можно найти по этой ссылке (и вставлен ниже).

DECLARE @Location varchar(128) = ##Location:string##
DECLARE @RepLimit int = ##RepLimit:int##

SELECT Users.DisplayName,
       Users.Id,
       Users.WebsiteUrl,
       Users.Reputation,
       (
           SELECT COUNT(*)
           FROM Posts
           JOIN PostTags ON Posts.ParentId = PostTags.PostId
           JOIN Tags ON PostTags.TagId = Tags.Id
           WHERE Posts.PostTypeId = 2
           AND Tags.Id = 3 -- Javascript
           AND Posts.OwnerUserId = Users.Id
       ) AS JavascriptCount,

       (
           SELECT COUNT(*)
           FROM Posts
           JOIN PostTags ON Posts.ParentId = PostTags.PostId
           JOIN Tags ON PostTags.TagId = Tags.Id
           WHERE Posts.PostTypeId = 2
           AND Tags.Id = 5 -- PHP
           AND Posts.OwnerUserId = Users.Id
       ) AS PhpCount,

       (
           SELECT COUNT(*)
           FROM Posts
           JOIN PostTags ON Posts.ParentId = PostTags.PostId
           JOIN Tags ON PostTags.TagId = Tags.Id
           WHERE Posts.PostTypeId = 2
           AND Tags.Id = 820 -- jQuery
           AND Posts.OwnerUserId = Users.Id
       ) AS jQueryCount,

       (
           SELECT COUNT(*)
           FROM Posts
           JOIN PostTags ON Posts.ParentId = PostTags.PostId
           JOIN Tags ON PostTags.TagId = Tags.Id
           WHERE Posts.PostTypeId = 2
           AND Tags.Id = 21 -- MySQL
           AND Posts.OwnerUserId = Users.Id
       ) AS MySqlCount,

       (
           SELECT COUNT(*)
           FROM Posts
           JOIN PostTags ON Posts.ParentId = PostTags.PostId
           JOIN Tags ON PostTags.TagId = Tags.Id
           WHERE Posts.PostTypeId = 2
           AND Tags.Id = 1386 -- Android
           AND Posts.OwnerUserId = Users.Id
       ) AS AndroidCount,

       (
           SELECT COUNT(*)
           FROM Posts
           JOIN PostTags ON Posts.ParentId = PostTags.PostId
           JOIN Tags ON PostTags.TagId = Tags.Id
           WHERE Posts.PostTypeId = 2
           AND Tags.Id IN (58338, 81106, 92809, 7003) -- IOS
           AND Posts.OwnerUserId = Users.Id
       ) AS IosCount

FROM Users

WHERE Users.Reputation > @RepLimit
AND Users.Location = @Location

В приведенном выше запросе происходит несколько вещей:

  1. На data.stackexchange.com они генерируют поле формы для ввода данных, которые у меня есть DECLAREd вверху (в данном случае Location и RepLimit).
  2. Теги, которые я ищу (Javascript, iOS, Android, PHP и т. Д.), Жестко закодированы, каждый из которых использует вложенный выбор, который не так эффективен, как мне кажется.

Я хочу изменить две вещи, но я не достаточно знаком с SQL Server, чтобы знать, насколько они выполнимы (а также не знаю, что именно запрашивать, чтобы найти нужные мне результаты). Два изменения:

  1. Я хотел бы оптимизировать запрос. Прямо сейчас я чувствую, что создание шести почти одинаковых подвыборов - не идеальный способ достижения конечного результата.
  2. Я хотел бы, чтобы список тегов был представлен в виде списка через запятую в новом элементе формы (будучи DECLAREd в верхней части, например Location и RepLimit), или в виде 5 отдельных полей формы (ограничение запроса до 5 разных тегов)

Кто-нибудь имел опыт работы с подобным запросом (или с подобной проблемой, когда фактические столбцы, возвращаемые запросом, были динамическими?). Любая помощь приветствуется.

1 ответ

Что касается вашего первого вопроса, вот способ оптимизировать запрос:

DECLARE @Location varchar(128) = ##Location:string##
DECLARE @RepLimit int = ##RepLimit:int##

SELECT Users.DisplayName,
       Users.Id,
       Users.WebsiteUrl,
       Users.Reputation,
       sum(case when Tags.Id = 3 then 1 else 0 end) as JavascriptCount,
       sum(case when Tags.Id = 5 then 1 else 0 end) as PhpCount,
       sum(case when Tags.Id = 820 then 1 else 0 end) as jQueryCount,
       sum(case when Tags.Id = 21 then 1 else 0 end) as MySqlCount,
       sum(case when Tags.Id = 1386 then 1 else 0 end) as AndroidCount,
       sum(case when Tags.Id IN (58338, 81106, 92809, 7003) then 1 else 0 end) as IosCount

FROM Users
    JOIN Posts ON Posts.OwnerUserId = Users.Id
        JOIN PostTags ON Posts.ParentId = PostTags.PostId
           JOIN Tags ON PostTags.TagId = Tags.Id

WHERE Users.Reputation > @RepLimit AND Posts.PostTypeId = 2
AND Users.Location = @Location

GROUP BY
       Users.DisplayName,
       Users.Id,
       Users.WebsiteUrl,
       Users.Reputation

Что касается вашего второго вопроса, я - как ничего не зная об этих формах стека обмена - боюсь, я не могу помочь. Но так как вы упоминаете список через запятую, вы можете использовать функцию SQL CHARINDEX() для поиска в параметре Tags.Id. Sth, как это:

case when CHARINDEX(','+convert(varchar,Tags.Id)+',',','+@List1+',') <> 0 then 1 else 0 end 
Другие вопросы по тегам