SQL Server:从查询返回的动态列

Posted

技术标签:

【中文标题】SQL Server:从查询返回的动态列【英文标题】:SQL Server: Dynamic columns returned from query 【发布时间】:2014-01-09 17:32:54 【问题描述】:

我对 data.stackexchange.com 运行了一个相当复杂的 SQL 查询。该查询可以在this link 找到(并粘贴在下面)。

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

在上面的查询中,发生了一些事情:

    在 data.stackexchange.com 上,他们为我生成了一个表单字段,让我输入顶部有 DECLAREd 的数据(在本例中为 Location 和 RepLimit)。 我正在搜索的标签(Javascript、iOS、Android、PHP 等)是硬编码的,每个标签都使用一个子选择,效率不如我想象的那么高。

我想更改两件事,但对 SQL Server 不够熟悉,无法知道它们是否都可行(而且也不知道要查询什么才能找到我需要的结果)。这两个变化是:

    我想优化查询。现在,我觉得制作六个几乎相同的子选择并不是完成最终结果的理想方式。 我希望将标签列表提供为新表单元素中的逗号分隔列表(顶部为 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

至于你的第二个问题——我对那些 stackexchange 表格一无所知——恐怕我无能为力。但是由于您提到了逗号分隔的列表,您可以利用 SQL 的 CHARINDEX() 函数来查找参数中的 Tags.Id。像这样的:

case when CHARINDEX(','+convert(varchar,Tags.Id)+',',','+@List1+',') <> 0 then 1 else 0 end 

【讨论】:

在 StackExchange 上,当您使用 DECLARE 语句时才生成表单,并且当您运行查询时,会将表单值插入到变量中进行查询。 StackExchange 只运行 SQL Server。 那么CHARINDEX 应该是一个明智的解决方案(尽管这忽略了建立在 Tags.Id 上的索引 - 因此效率大大降低)。您可能应该坚持“将查询限制为最多 5 个不同的标签”。

以上是关于SQL Server:从查询返回的动态列的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 中具有动态列的数据透视表

SQL Server:从链接到另一个表的 ID 列动态创建列

jquery easyui datagrid 动态 加载列

SQL Server - 在按特定列分组时构建动态范围的数字

SQL Server中动态列转行

SQL Server 动态行转列(参数化表名分组列行转列字段字段值)