SQL Server透视查询替代或优化

Ale*_*lex 2 sql t-sql sql-server pivot sql-server-2012

所以我有这些表:

-- tbl_obs
id  lat  lon   created
-------------------------
1   1.2  -2.1  2002-08-03
2   1.9  -5.5  2002-08-03
3   1.5  -4.1  2002-08-03

-- tbl_obsdata
id  name         value     obs_id
---------------------------------
1   gender       Male       1
2   type         Type I     1
3   description  Some desc  1
4   gender       Female     2
5   type         Type II    2
6   description  Some desc  2
7   gender       Female     3
8   type         Type II    3
9   description  Some desc  3
Run Code Online (Sandbox Code Playgroud)

我想要一个查询,它将合并来自两个表的数据,如下所示:

lat  lon   created     gender  type  description
------------------------------------------------
1.2  -2.1  2002-08-03  Male   Type I  Some desc
1.9  -5.5  2002-08-03  Female Type I  Some desc
1.5  -4.1  2002-08-03  Male   Type II Some desc
Run Code Online (Sandbox Code Playgroud)

我知道我可以像这样的枢轴来做到这一点:

with cte as (
 select obsdata.name, obsdata.value, obs.lat, obs.lon, obs.created
 from obsdata
 left join obs on obs.id = obsdata.obs_id
)
select lat, lon, created, gender, type, description
from cte
pivot(
 max(value)
 for [name] in (gender, type, description)
) as pvt
Run Code Online (Sandbox Code Playgroud)

到目前为止,这返回了结果(我认为),但是我大约有一百万行,而且运行速度非常慢。还有其他方法可以更快地实现这一目标吗?我正在使用SQL Server 2012。

Gor*_*off 5

首先优化枢轴,然后优化join. 我认为 SQL Server 对数据透视做了合理的工作,所以从以下开始:

select obs_id,  gender, type, description
from tbl_obsdata
pivot (max(value) for [name] in (gender, type, description)
      ) as pvt;
Run Code Online (Sandbox Code Playgroud)

然后,在 上创建索引tbl_obsdata(obs_id, name, value)。这应该相当快。

如果是这样,那么加入其余的:

with cte as (
      select obs_id,  gender, type, description
      from tbl_obsdata
      pivot (max(value) for [name] in (gender, type, description)
            ) as pvt
    )
select obs.lat, obs.lon, obs.created,
       cte.gender, cte.type, cte.description
from cte join
     obs
     on obs.id = cte.obs_id;
Run Code Online (Sandbox Code Playgroud)

编辑:

我也想知道这会如何:

select obs.lat, obs.lon, obs.created, od.gender, od.type, od.description
from obs cross apply
     (select max(case when name = 'gender' then value end) as gender,
             max(case when name = 'type' then value end) as type,
             max(case when name = 'description' then value end) as description

      from tbl_obsdata od
      where od.obs_id = obs.id
     ) od;
Run Code Online (Sandbox Code Playgroud)

这也需要一个索引tbl_obsdata(obs_id, name, value)

  • Pivots 存在性能问题,并且通常比 John Cappalletti 在这里给出的解决方案慢。Unpivot 很好,但由于我在大容量大数据环境中工作,我从不使用 Pivot。 (2认同)
  • @btberry 。. . 有趣的。我基本上从不使用枢轴,更喜欢条件聚合。我认为这是一个旧习惯,pivot 语法具有几乎相同的性能。你有枢轴性能的参考吗? (2认同)

Joh*_*tti 5

另一种选择是

Select A.lat
      ,A.lon
      ,A.created
      ,gender      = max(IIF(B.name='gender',B.value,null))
      ,type        = max(IIF(B.name='type',B.value,null))
      ,description = max(IIF(B.name='description',B.value,null))
 From  tbl_obs A
 Join  tbl_obsdata B on (A.id=B.obs_id)
 Group By A.lat
      ,A.lon
      ,A.created
Run Code Online (Sandbox Code Playgroud)

退货

lat lon     created     gender  type    description
1.2 -2.1    2002-08-03  Male    Type I  Some desc
1.5 -4.1    2002-08-03  Female  Type II Some desc
1.9 -5.5    2002-08-03  Female  Type II Some desc
Run Code Online (Sandbox Code Playgroud)