SQL 服务器枢轴查询替代或优化
SQL Server pivot query alternative or optimize
所以我有这些 tables:
-- 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
我想要一个查询,它将来自两个 table 的数据结合起来,如下所示:
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
我知道我可以用像这样的枢轴来做到这一点:
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
到目前为止,这是 returns 结果(我认为),但我有大约一百万行,而且运行速度非常慢。有什么替代方法可以更快地实现这一目标?我正在使用 SQL Server 2012。
先优化枢轴,再优化join
。我认为 SQL 服务器对枢轴做了合理的工作,所以从以下开始:
select obs_id, gender, type, description
from tbl_obsdata
pivot (max(value) for [name] in (gender, type, description)
) as pvt;
然后,在 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;
编辑:
我也想知道这会怎么样:
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;
这也需要 tbl_obsdata(obs_id, name, value)
上的索引。
另一种选择是
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
Returns
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
所以我有这些 tables:
-- 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
我想要一个查询,它将来自两个 table 的数据结合起来,如下所示:
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
我知道我可以用像这样的枢轴来做到这一点:
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
到目前为止,这是 returns 结果(我认为),但我有大约一百万行,而且运行速度非常慢。有什么替代方法可以更快地实现这一目标?我正在使用 SQL Server 2012。
先优化枢轴,再优化join
。我认为 SQL 服务器对枢轴做了合理的工作,所以从以下开始:
select obs_id, gender, type, description
from tbl_obsdata
pivot (max(value) for [name] in (gender, type, description)
) as pvt;
然后,在 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;
编辑:
我也想知道这会怎么样:
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;
这也需要 tbl_obsdata(obs_id, name, value)
上的索引。
另一种选择是
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
Returns
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