2011-10-18 24 views
5

我有一個sql視圖,我正在使用它來檢索數據。我們可以說它有一大串產品,它們與購買它們的顧客有關。每個產品的視圖應只返回一行,而不管它鏈接到多少客戶。我正在使用row_number函數來實現這一點。 (這個例子被簡化,一般情況會查詢那裏應該只爲這是返回行的一些列X的每個唯一值一個返回行並不重要)重構使用row_number()返回具有唯一列值的行的tsql視圖

CREATE VIEW productView AS 
SELECT * FROM 
    (SELECT 
     Row_number() OVER(PARTITION BY products.Id ORDER BY products.Id) AS product_numbering, 
     customer.Id 
     //various other columns 
    FROM products 
    LEFT OUTER JOIN customer ON customer.productId = prodcut.Id 
    //various other joins 
    ) as temp 
WHERE temp.prodcut_numbering = 1 

現在讓我們說,該視圖中的總行數爲〜100萬,並且從productView運行select *需要10秒。在產品ID = 10的productView中執行查詢(如select *)需要相同的時間。我相信這是因爲查詢得到這個評估

SELECT * FROM 
    (SELECT 
     Row_number() OVER(PARTITION BY products.Id ORDER BY products.Id) AS product_numbering, 
     customer.Id 
     //various other columns 
    FROM products 
    LEFT OUTER JOIN customer ON customer.productId = prodcut.Id 
    //various other joins 
    ) as temp 
WHERE prodcut_numbering = 1 and prodcut.Id = 10 

我認爲這是導致內部子查詢每次完整評估。理想情況下,我想沿着以下方向使用一些東西

SELECT 
    Row_number() OVER(PARTITION BY products.productID ORDER BY products.productID) AS product_numbering, 
    customer.id 
    //various other columns 
FROM products 
    LEFT OUTER JOIN customer ON customer.productId = prodcut.Id 
    //various other joins 
WHERE prodcut_numbering = 1 

但是這似乎不被允許。有什麼辦法可以做類似的事嗎?

編輯 -

多次試驗之後,我相信我有實際的問題是如何強制加入到只返回1行。我嘗試使用outer apply,如下所示。一些示例代碼。

CREATE TABLE Products (id int not null PRIMARY KEY) 
CREATE TABLE Customers (
     id int not null PRIMARY KEY, 
     productId int not null, 
     value varchar(20) NOT NULL) 

declare @count int = 1 
while @count <= 150000 
begin 
     insert into Customers (id, productID, value) 
     values (@count,@count/2, 'Value ' + cast(@count/2 as varchar))  
     insert into Products (id) 
     values (@count) 
     SET @count = @count + 1 
end 

CREATE NONCLUSTERED INDEX productId ON Customers (productID ASC) 

通過上述樣本集,下面

select * from Products 
outer apply (select top 1 * 
      from Customers 
      where Products.id = Customers.productID) Customers 

了 '讓一切' 查詢時間1000毫秒〜運行。添加明確的條件:

select * from Products 
outer apply (select top 1 * 
      from Customers 
      where Products.id = Customers.productID) Customers 
where Customers.value = 'Value 45872' 

需要一定的時間。對於相當簡單的查詢來說,這1000毫秒已經太多了,並且在添加其他類似的連接時會以錯誤的方式擴展(向上)。

+0

你需要實際客戶的詳細信息或者只是存在或只是一個客戶ID?子查詢是評估的,因爲「10」事先不知道。你正在問第十排。因此,我的第一個問題關於期望的輸出 – gbn

+1

真的很好的觀察 - SQL無法將視圖過濾器應用到子查詢中。你真的需要視圖的靈活性嗎?如果您使用SPROC或帶有「已定義」過濾器的表值函數(在您的示例中爲ProductID),則可以將過濾器構建到子查詢中。而在PARTITION BY和FILTER相同的情況下(ProductId),根本不需要PARTITION--所以SELECT TOP 1應該足夠了。 – StuartLC

+0

我確實需要實際的客戶詳細信息(如果不存在,則爲空值),而不僅僅是一個的存在。我也必須使用視圖,重構檢索數據的應用程序是不可能的。 – John

回答

2

如果你不喜歡的東西:

SELECT ... 
FROM products 
OUTER APPLY (SELECT TOP 1 * from customer where customerid = products.buyerid) as customer 
... 

然後在productId使用過濾器應該有所幫助。不過,這可能會更糟。

3

使用公共表格表達式(CTE)嘗試以下方法。使用您提供的測試數據,它會在不到一秒的時間內返回特定的ProductIds。

create view ProductTest as 

with cte as (
select 
    row_number() over (partition by p.id order by p.id) as RN, 
    c.* 
from 
    Products p 
    inner join Customers c 
     on p.id = c.productid 
) 

select * 
from cte 
where RN = 1 
go 

select * from ProductTest where ProductId = 25 
+0

這似乎確實比其他方法運行得更快,但它仍然會導致對整個子查詢進行評估。單獨從ProductTest執行'select * *需要大致相同的時間,並且具有與where子句相同的執行計劃。 – John

+0

我認爲這是由於觀點本身的本質而最好的。另一種選擇是創建一個存儲過程,或者一個表值函數,它可以傳入productid中,並且可以直接對您希望查詢的部分進行過濾。 –

1

問題是您的數據模型有缺陷。你應該有三個表:

  • 客戶(客戶ID,...)
  • 產品(productId參數,...)
  • ProductSales(客戶ID,productId參數)

此外,銷售表應該可以分成一對多(Sales和SalesDetails)。除非你修復你的數據模型,否則你只會在你的尾巴上追逐紅鯡魚問題。如果系統不是您的設計,請修復它。如果老闆不讓你修復它,然後修復它。如果你不能修復它,然後修復它。對於您提出的錯誤數據模型來說,並不容易。

0

這將可能是速度不夠快,如果你真的不在乎你帶來哪些客戶回

select p1.*, c1.* 
FROM products p1 
Left Join (
     select p2.id, max(c2.id) max_customer_id 
     From product p2 
     Join customer c2 on 
     c2.productID = p2.id 
     group by 1 
) product_max_customer 
Left join customer c1 on 
c1.id = product_max_customer.max_customer_id 
;