2009-09-17 61 views
0

我有種感覺,爲什麼視圖更慢: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)) 
+0

對不起,我不響應的答案。我最近一直沒有在電腦上工作,直到我在週一的實際代碼前面才能真正評估任何事情。 – Brian 2009-09-19 16:48:08

回答

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 ALLUNION。然後你可以使用:

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

我結束了使用或而不是工會。 – Brian 2009-09-21 15:02:15

0

據我所知,整個結果集,視圖被收集,然後再由SELECT語句widdled下來那使用它。這與你的第二個SELECT語句完全不同,它沒有收集到超出需要的東西。

0

你可以嘗試內聯函數提交(http://www.sqlhacks.com/index.php/Retrieve/Parameterized-View),但TBH我認爲這是一個黑客攻擊的一位。

老實說,我可能會去代碼重複。我並沒有像我看到其他代碼那樣真正地看到SQL - 我在不同的邏輯等價語句之間看到了性能上的巨大差異。

1

從查看您的查詢是這樣的:

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

,而第二個是:

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

這兩個查詢是非常不同的,儘管它們產生相同的結果,fiurst一個如果整個表進行掃描,以產生不同的名稱只能回答,而第二個可以只掃描您感興趣的名字。

問題的要點是, DISTINCT的存在放置了一個屏障,不允許篩選謂詞沿查詢樹向下移動到有效的位置。

更新

即使DISTINCT不是障礙,在第二看二看有一個更強大的障礙有:在GROUP BY/HAVING子句。一個查詢在GROUP和HAVING條件應用之後過濾,另一個在之前。 HAVING條件的子查詢再次參考name。我懷疑QO可以在聚合之後的聚集和過濾之前證明過濾的等價性。

+0

這不可能是一個問題。如果在選擇匹配名稱之前或之後應用DISTINCT,SQL Server的查詢處理器能夠識別出結果沒有區別。 這裏沒有障礙。 SQL語句對評估順序沒有指示。查詢處理器可以自由地從任何執行計劃中返回正確的結果。我測試SQL Server 2008的這種重寫,但我非常懷疑這不是也在2005年,2000年,也許是7.0。) – 2009-09-17 22:32:51

+0

@Steve:你是對的。不是獨一無二的,就是HAVING那樣的障礙。 – 2009-09-17 22:36:27

+0

@Remus:GROUP BY/HAVING也不是障礙。我剛剛測試了它,並且SQL Server 2008在GROUP BY之前推送LIKE謂詞,即使它在包含GROUP BY和HAVING規範的視圖中的SELECT後面的WHERE子句中指定。 這裏的查詢更復雜,但沒有理由我明白爲什麼謂詞不能推高。無論是否被推高,我都不知道沒有看到查詢計劃。 – 2009-09-17 23:59:50

0

沒有看到更多(例如,每個表的CREATE TABLE,INDEX和CONSTRAINT語句),並且最好將查詢計劃看作代表連接基數的一些示例數據,這很難說。

可能地,存在具有與在其下LIKE表達式求值的排序規則做查詢之間的語義差異,這可能是不可能的哄同樣的計劃。

但是,這裏可能有很多空間用於查詢調整。您似乎不太可能需要完全彙總所有COUNT()。你有三個相當不同的條件,你想在結果中看到一個「名字」。使用UNION,您可以使其中的一個或多個計算更簡單,並且如果併發不是問題,那麼您甚至可以將其作爲多步驟用戶定義的表值函數來編寫,這些函數可以在單獨的步驟中累積名稱。

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是障礙。

0

假設您的WHERE條件已知並且可以通過參數提供,那麼表值函數往往比視圖更快。

表值函數的一個優點是可以有多個語句,因此您可以在後續語句中將OUTER JOIN轉換爲更快的INNER JOIN。因此,而不是這樣的:

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