2011-10-18 12 views
5

Tôi có chế độ xem sql mà tôi đang sử dụng để truy xuất dữ liệu. Cho phép nói một danh sách lớn các sản phẩm, được liên kết với khách hàng đã mua chúng. Chế độ xem sẽ chỉ trả lại một hàng cho mỗi sản phẩm, bất kể số lượng khách hàng được liên kết đến. Tôi đang sử dụng hàm row_number để đạt được điều này. (Ví dụ này được đơn giản hóa, tình huống chung sẽ là một truy vấn mà chỉ nên có một hàng được trả lại cho mỗi giá trị duy nhất của một số cột X. Hàng nào được trả về không quan trọng)Tái cấu trúc chế độ xem tsql sử dụng row_number() để trả về các hàng có giá trị cột duy nhất

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 

Bây giờ cho phép nói rằng tổng số hàng trong chế độ xem này là ~ 1 triệu và chạy * từ productView mất 10 giây. Thực hiện truy vấn như chọn * từ productView trong đó productID = 10 mất cùng một lượng thời gian. Tôi tin rằng điều này là do truy vấn được đánh giá cho điều này

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 

Tôi nghĩ điều này khiến cho truy vấn con bên trong được đánh giá đầy đủ mỗi lần. Lý tưởng nhất là tôi muốn sử dụng thứ gì đó dọc theo các dòng sau

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 

Nhưng điều này dường như không được phép. Có cách nào để làm một cái gì đó tương tự?

EDIT -

Sau nhiều thử nghiệm, vấn đề thực tế Tôi tin rằng tôi đang gặp là làm thế nào để buộc một tham gia trở lại chính xác 1 hàng. Tôi đã cố gắng sử dụng bên ngoài áp dụng, như đề nghị dưới đây. Một số mã mẫu.

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) 

Với các thiết lập ví dụ trên, các truy vấn 'có được mọi thứ' bên dưới

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

mất ~ 1000ms để chạy. Thêm điều kiện rõ ràng:

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

Mất một khoảng thời gian giống nhau. 1000ms này cho một truy vấn khá đơn giản đã quá nhiều, và quy mô sai cách (trở lên) khi thêm các phép nối tương tự bổ sung.

+0

Bạn có cần chi tiết khách hàng thực tế hay chỉ là sự tồn tại của hay chỉ là một customerid? Truy vấn phụ là evaluatde vì "10" không được biết trước. Và bạn đang yêu cầu hàng thứ 10 chính xác. Do đó câu hỏi đầu tiên của tôi về đầu ra mong muốn – gbn

+1

Quan sát thực sự tốt - SQL không thể áp dụng bộ lọc chế độ xem vào truy vấn phụ. Bạn có thực sự cần sự linh hoạt của chế độ xem không? Nếu bạn đã sử dụng hàm SPROC hoặc bảng có giá trị với bộ lọc 'đã xác định' (ProductID trong ví dụ của bạn), bạn có thể xây dựng trong bộ lọc vào truy vấn phụ. Và trong trường hợp PARTITION BY của bạn và FILTER là giống nhau (ProductId), bạn sẽ không cần PARTITION chút nào - vì vậy SELECT TOP 1 phải là đủ. – StuartLC

+0

Tôi cần chi tiết thực tế của khách hàng (hoặc giá trị null nếu không tồn tại), không chỉ sự tồn tại của một. Tôi cũng phải sử dụng chế độ xem, việc tái cấu trúc ứng dụng để truy xuất dữ liệu là không thể. – John

Trả lời

2

gì nếu bạn đã làm điều gì đó như:

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

Sau đó, bộ lọc trên productId nên giúp đỡ. Nó có thể tồi tệ hơn mà không lọc.

3

Thử cách tiếp cận sau, sử dụng Biểu thức bảng chung (CTE). Với dữ liệu thử nghiệm bạn cung cấp, nó trả về ProductIds cụ thể trong chưa đầy một giây.

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

Điều này dường như hoạt động nhanh hơn nhiều so với các phương pháp khác, nhưng nó vẫn khiến cho toàn bộ truy vấn con được đánh giá. Việc thực hiện 'select * từ ProductTest' chỉ mất khoảng một thời gian, và có cùng một kế hoạch thực hiện, giống như với mệnh đề where. – John

+0

Tôi nghĩ rằng đây là điều tốt nhất bạn sẽ có thể nhận được do bản chất của lượt xem. Tùy chọn khác là tạo một thủ tục được lưu trữ, hoặc có thể là một hàm có giá trị bảng, đi qua productid và có thể lọc trực tiếp trên phần truy vấn mà bạn mong muốn. –

1

Vấn đề là mô hình dữ liệu của bạn bị thiếu sót. Bạn nên có ba bảng:

  • Khách hàng (ID khách hàng, ...)
  • Sản phẩm (productId, ...)
  • ProductSales (ID khách hàng, productId)

Hơn nữa, việc bán bảng có lẽ nên được chia thành 1 thành nhiều (Sales and SalesDetails).Trừ khi bạn sửa mô hình dữ liệu của mình, bạn sẽ chỉ chạy vòng tròn quanh đuôi của bạn theo đuổi các vấn đề về cá trích đỏ. Nếu hệ thống không phải là thiết kế của bạn, hãy sửa nó. Nếu sếp không cho phép bạn sửa nó, hãy sửa nó. Nếu bạn không thể sửa chữa nó, sau đó sửa chữa nó. Không có cách nào dễ dàng cho mô hình dữ liệu xấu mà bạn đang đề xuất.

0

này có lẽ sẽ đủ nhanh nếu bạn thực sự không quan tâm mà khách hàng bạn mang lại

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 
; 
Các vấn đề liên quan