数据库查询视图较慢

时间:2009-09-17 22:07:31

标签: sql sql-server sql-server-2005 tsql

我有点感觉为什么视图较慢:where子句可能不会同时应用。但结果看起来似乎是一样的。我不知道我能做些什么,没有使用视图...这是不理想的,因为我添加了视图以避免代码重复,我不想删除它,如果没有必要。

有关更改我这样做的方法的任何建议,以便我可以使用命令1中的视图,但仍然可以在命令2中执行我的查询时快速执行吗?

declare @foo varchar(50)
set @foo = 'be%'

ALTER VIEW [dbo].[wpvw_v]
AS
select distinct [name]
from kvgs kvg left join cdes cde
on kvg.kvgi = cde.kgi
group by [name], cde.kgi, kvg.mU
having count(cde.kgi) >= 2 or kvg.mU = 1 or 
   exists (select [name] from FP x where x.name = kvg.name) 

--Command 1: Takes 7 seconds
select [name] from wpvw_v where name like @foo

--Command 2: Takes 1 second
SELECT DISTINCT kvg.name
FROM         dbo.kvgs AS kvg LEFT JOIN
                      dbo.cdes AS cde ON kvg.kvgi = cde.kgi
where name like @foo
GROUP BY kvg.name, cde.kgi, kvg.mU
HAVING      (COUNT(cde.kgi) >= 2) OR
                      (kvg.mU = 1) OR
                      EXISTS
                          (SELECT     Name
                            FROM          dbo.FP AS x
                            WHERE      (Name = kvg.name))

7 个答案:

答案 0 :(得分:1)

您在查看中的查询是这样的:

SELECT name FROM (SELECT DISTINCT name FROM ...) WHERE name = @name;

而第二个是:

SELECT DISTINCT name FROM ... WHERE name = @name;

这两个查询是非常不同的,即使它们产生相同的结果,只有扫描整个表以产生不同的名称才可以回答furst,而第二个只能扫描你感兴趣的名称in。

问题的要点是DISTINCT的存在会产生一个障碍,它不允许过滤谓词沿着查询树向下移动到有效的地方。

<强>更新

即使DISTINCT不是障碍,第二眼看第二眼也有更强大的障碍:GROUP BY / HAVING子句。一个查询在应用GROUP和HAVING条件后过滤,另一个在之前。 HAVING条件具有再次引用name的子查询。我怀疑QO可以在聚合之前证明过滤的等价性并在聚合之后过滤。

答案 1 :(得分:1)

我认为HAVING子句不能容纳您发布的内容,但我相信您应该编写您的视图以使用UNION。这是我的看法:

ALTER VIEW [dbo].[wpvw_v] AS
WITH names AS(
  SELECT k.name
    FROM KVGS k 
   WHERE EXISTS(SELECT NULL
                  FROM CDES c
                 WHERE c.kgi = k.kvgi
              GROUP BY c.kgi
                HAVING COUNT(c.kgi) > 1)
  UNION ALL
  SELECT k.name
    FROM KVGS k 
   WHERE k.mu = 1
GROUP BY k.name
  UNION ALL
  SELECT k.name
    FROM KVGS k 
    JOIN FP x ON x.name = k.name
GROUP BY k.name)
SELECT n.name
  FROM names n

如果要过滤掉3个SQL语句之间的重复项,请将UNION ALL更改为UNION。然后你可以使用:

SELECT n.name
  FROM wpvw_v n
 WHERE CHARINDEX(@name, n.name) > 0

答案 2 :(得分:0)

据我所知,视图的完整结果集被收集,然后被使用它的SELECT语句进一步淹没。这与您的第二个SELECT语句非常不同,后者不会收集任何超出需要的语句。

答案 3 :(得分:0)

您可以尝试使用内联表函数(http://www.sqlhacks.com/index.php/Retrieve/Parameterized-View),但是我认为这有点像黑客。

老实说,我可能会去重复代码。我没有像看到其他代码一样看到SQL - 我一直看到其他逻辑上等价的语句之间的性能存在巨大差异。

答案 4 :(得分:0)

如果没有看到更多(例如,每个表的CREATE TABLE,INDEX和CONSTRAINT语句),并且最好将查询计划视为代表连接基数的一些示例数据,那么很难说。

可能在查询之间存在语义差异,这与查询LIKE表达式的排序规则有关,并且可能无法同步计划。

但是,这里可能有足够的空间进行查询调优。您似乎不太可能需要完全聚合所有COUNT()。您有三个相当不同的条件,您希望在结果中看到“名称”。使用UNION,您可以使其中一个或多个更容易计算,如果并发不是问题,您甚至可以将其写为多步骤用户定义的表值函数,该函数在单独的步骤中累积名称

答案 5 :(得分:0)

我相信以下内容可以重现您的问题:

create table tbl (idx int identity(1,1), name varchar(50), val float)

declare @cnt int
set @cnt=0
while @cnt < 10000
begin
insert tbl select char(CAST(rand()*256 AS INT)), rand()
set @cnt = @cnt + 1
end
go
create view tbl_view as select distinct name from tbl group by name having sum(val) > 1

然后,如果您运行以下查询:

SET STATISTICS IO ON
declare @n varchar(50)
set @n='w%'
select * from tbl_view where name like @n
SET STATISTICS IO OFF
GO
SET STATISTICS IO ON
declare @n varchar(50)
set @n='w%'
select distinct name from tbl where name like @n group by name having sum(val) > 1
SET STATISTICS IO OFF

您将获得以下内容:

(1 row(s) affected)
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'tbl'. Scan count 1, logical reads 338, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)
Table 'tbl'. Scan count 1, logical reads 338, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

视图强制它首先处理子表,然后才应用过滤器。现在,如果您修改视图并删除 DISTINCT ,则不会更改。但是,如果您修改视图以通过以下方式删除组:

create view tbl_view as select name from tbl where val > 0.8 group by name 
go
SET STATISTICS IO ON
declare @n varchar(50)
set @n='w%'
select * from tbl_view where name like @n
SET STATISTICS IO OFF
GO
SET STATISTICS IO ON
declare @n varchar(50)
set @n='w%'
select name from tbl where val > 0.8 and name like @n group by name
SET STATISTICS IO OFF

然后,您会得到两个查询相同的结果:

(1 row(s) affected)
Table 'tbl'. Scan count 1, logical reads 34, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

(1 row(s) affected)
Table 'tbl'. Scan count 1, logical reads 34, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

所以看起来 HAVING 似乎是障碍。

答案 6 :(得分:0)

表值函数往往比视图更快,假设您的WHERE条件已知且可以在参数中提供。

表值函数的一个优点是可以有多个语句,因此可以在后续语句中将OUTER JOIN转换为更快的INNER JOINS。所以不要这样:

INSERT INTO @resultTable
    table1_id,
    table1_column,
    table2_column,
    table3_column
SELECT
    table1.id,
    table1.column,
    table2.column,
    table3.column
FROM
    table1
    INNER JOIN table2 ON table2.table1_id = table1.id
    LEFT OUTER JOIN table3 ON table3.table1_id = table1.id

return @resultTable

...你可以做到这一点,我发现它总是更快:

INSERT INTO @resultTable
    table1_id,
    table1_column,
    table2_column,
SELECT
    table1.id,
    table1.column,
    table2.column,
FROM
    table1
    INNER JOIN table2 ON table2.table1_id = table1.id

UPDATE @resultTable SET
    table3_column = table3.column
FROM @resultTable AS result
    INNER JOIN table3 ON table3.table1_id = result.table1_id

return @resultTable