2009-01-18 33 views
5

Tôi cần phải chọn một số lượng lớn các hàng từ một bảng lớn hơn, được khóa trên cột nhận dạng tự động. Tôi có các giá trị khóa chính cho mỗi hàng mà tôi đang cố chọn, nhưng nó có thể rất lớn.Chọn một số lượng lớn các hàng được cung cấp cho khóa chính

Thông thường, nhưng không phải lúc nào, các hàng được chọn liền nhau, vì vậy tôi đã triển khai cơ chế chuyển đổi lựa chọn thành một tập hợp các mệnh đề phạm vi cho tất cả các mục được gộp lại với nhau ([PrimaryKey] GIỮA 151 VÀ 217), và phương thức dự phòng chọn tất cả các mục bị cô lập với mệnh đề IN.

Cuối cùng tôi nhận được một cái gì đó như thế này

SELECT * FROM Table WHERE ([PrimaryKey] BETWEEN 151 AND 217) OR ([PrimaryKey] BETWEEN 314 AND 378) OR ... 
OR [PrimaryKey] IN (1,3,7,14,147...) 

này hoạt động rất tốt cho các trường hợp nơi mà tôi có phạm vi chủ yếu là lớn, nhưng bị phá vỡ như truy vấn được lớn hơn. Tôi chỉ chạy qua một trường hợp thoái hóa nơi tôi có một số lượng lớn các cặp mục tạo ra các câu lệnh BETWEEN cho 2 mục tại một thời điểm mất hơn 15 phút để mô tả kế hoạch thực hiện trước khi tôi từ bỏ nó. Điều đầu tiên bạn cần lưu ý là tôi có thể thay đổi ngưỡng khi tôi bắt đầu tạo các dải ô như trái ngược với các giá trị riêng lẻ cho một cái gì đó lớn hơn 2 (10 có lẽ?), Nhưng tôi đã tự hỏi liệu có một giải pháp tốt hơn hay không ngoài đó.

Trả lời

7

Tạo bảng tạm thời với các giá trị bạn muốn chọn và thực hiện kết nối từ bảng chính của bạn với bảng tạm thời. Bằng cách này bạn có thực tế không có giới hạn.

+0

cũng có thể sử dụng biến bảng ở đây –

1

Khi người dùng ocdecio đề xuất chính xác, bạn có thể tham gia vào bảng tạm thời chứa tất cả ID. Bạn cũng có thể cố gắng để chia tay các ORS vào khác nhau UNION phần:

SELECT * FROM Table WHERE [PrimaryKey] BETWEEN 151 AND 217 
UNION 
SELECT * FROM Table WHERE [PrimaryKey] BETWEEN 314 AND 378 
UNION 
... 
UNION 
SELECT * FROM Table WHERE [PrimaryKey] IN (1,3,7,14,147...) 
2

tôi có thể hiểu được ý định của cơ chế của bạn. Tuy nhiên, trong thực tế, sự hiện diện của một chỉ số và niềm tin vào người tối ưu hóa thường tốt hơn.

Nếu bạn tạo một tá điều kiện trong mệnh đề WHERE, công cụ truy vấn cần phải kiểm tra từng điều kiện cho đến khi nó tìm thấy kết quả phù hợp.

Tương tự, việc tạo nhiều truy vấn và kết hợp chúng sẽ có nghĩa là quét chỉ mục hoặc tra cứu chỉ mục trên bảng nhiều lần.

Tuy nhiên, điều đúng là một mệnh đề IN có thể rất chậm khi bạn có danh sách lớn. Trong trường hợp đó, việc sử dụng phép nối thường nhanh hơn. Từ kinh nghiệm, đây luôn là lựa chọn ưa thích của tôi.

Tuy nhiên, đúng là việc sử dụng BETWEEN là hiệu quả đặc biệt cho các phạm vi lớn hơn. Với ý nghĩ đó, có thể có ích khi sử dụng cơ chế UNION WHERE bộ lặp đầu tiên sử dụng JOIN và phần còn lại sử dụng GIỮA, với điều kiện GIỮA GIỮA là một phạm vi đáng kể.

Cân nhắc quan trọng là thời gian để chuẩn bị truy vấn như vậy. Nếu SQL Server phải tạo ra một truy vấn động, sử dụng T-SQL, sẽ có hai đầu trên; Thời gian để tạo truy vấn và thời gian phân tích cú pháp, sau đó tạo kế hoạch thực hiện. Việc đầu tiên sẽ thống trị cho các danh sách lớn và có thể tốn nhiều thời gian hơn so với việc lưu bằng cách sử dụng GIỮA.

Nếu khách hàng tạo truy vấn động, bạn có thể lập luận rằng bạn đang chuyển tải một số tải từ máy chủ cho máy khách. Mặc dù tôi vẫn hoài nghi rằng những lợi ích sẽ là đáng kể.

Giả sử như vậy, trừ khi tôi thấy hiệu suất rất đáng chú ý tăng lên, tôi sẽ dính vào tham gia. Lý do chính là nguyên nhân của kỹ thuật;
- Thời gian để phát triển
- Độ tin cậy của mã (simple luôn luôn đáng tin cậy hơn các thủ thuật thông minh là)
- Bảo trì mã (? Sẽ bảo trì sau này hiểu các trick)

Nếu bạn làm thử nghiệm kết hợp khác nhau của JOIN và GIỮA, có hoặc không có UNION, vv, tôi rất muốn xem kết quả hoạt động của bạn.

0

Đây là vấn đề tương tự với những gì tôi gặp phải gần đây. Tôi cần phải chọn từ một bảng những hàng đó giao nhau với một danh sách lớn các khóa chính. Câu hỏi đặt ra là làm thế nào để gửi danh sách khóa lớn tới máy chủ SQL một cách hiệu quả dưới dạng nhanh và hiệu quả để sử dụng.

Điều gì hoạt động thực sự tốt cho loại tình huống này là loại dữ liệu XML. Nếu bạn tạo một thủ tục lưu sẵn có một tham số kiểu XML, bạn có thể định dạng trước đầu vào thành một đoạn XML. Ví dụ: giả sử đoạn XML sẽ trông như sau:

<a> 
    <b>1</b> 
    <b>3</b> 
    <b>7</b> 
    <b>14</b> 
    <b>147</b> 
</a> 

Tôi đã đặt tên các phần tử ("a" và "b") vì tên dài hơn có nghĩa là nhiều byte truyền từ khách hàng hơn máy chủ SQL. Đây là cách bạn sẽ chọn tất cả các nội dung của các yếu tố "b" như là một tập kỷ lục:

declare @x xml 
set @x = '<a><b>1</b><b>3</b><b>7</b><b>14</b><b>147</b></a>' 
select t.item.value('.', 'int') from @x.nodes('//a/b') as t(item) 

Mặc dù cú pháp là khó hiểu, kiểu XML có thể được truy vấn giống như một bảng. Bây giờ bạn sẽ thấy nơi này đang diễn ra. Nếu chúng ta có thể truy vấn kiểu XML như một bảng, chúng ta có thể giao nhau rằng với bảng khác:

select * from MyTable where ID in 
    (select t.item.value('.', 'int') from @x.nodes('//a/b') as t(item)) 

hoặc sử dụng một tham gia

;with cte as 
    (select ID = t.item.value('.', 'int') from @x.nodes('//a/b') as t(item)) 
select * from MyTable inner join cte on MyTable.ID = cte.ID 

Bạn cần phải chạy cả hai phiên bản để xem sẽ nhanh hơn cho dữ liệu của bạn. Tôi thấy JOIN hoạt động nhanh hơn với dữ liệu của tôi. Đây là một thủ tục lưu trữ mà có kiểu XML như là đầu vào và spits lại hàng chọn chúng tôi:

create procedure MyProc @x xml as 
begin 
    set nocount on 
    ;with cte as 
    (select ID = t.item.value('.', 'int') from @x.nodes('//a/b') as t(item)) 
    select * from MyTable inner join cte on Table.ID = cte.ID 
end 

Một cuộc gọi mẫu của các thủ tục lưu trữ mới:

exec MyProc '<a><b>1</b><b>3</b><b>7</b><b>14</b><b>147</b></a>' 

Tôi cũng thấy rằng việc thêm một lược đồ XML cho đoạn đầu vào giúp tăng tốc độ lưu trữ một chút. Tôi sẽ không đi sâu vào các chi tiết của các lược đồ XML ở đây, nhưng ý tưởng là nói cho SQL trước những gì mà đoạn XML sẽ trông như thế nào. Dưới đây là cách chúng tôi sẽ đầu vào giản đồ của chúng tôi:

create xml schema collection MyInputSchema as 
    '<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <xsd:element name="a"> 
     <xsd:complexType> 
     <xsd:complexContent> 
      <xsd:restriction base="xsd:anyType"> 
      <xsd:sequence> 
       <xsd:element name="b" type="xsd:integer" maxOccurs="unbounded" /> 
      </xsd:sequence> 
      </xsd:restriction> 
     </xsd:complexContent> 
     </xsd:complexType> 
    </xsd:element> 
    </xsd:schema>' 

Bây giờ chúng ta có thể kết hợp sơ đồ này với các đầu vào cho thủ tục lưu trữ của chúng tôi như thế này:

create procedure MyProc @x xml(MyInputSchema) as 
begin 
    set nocount on 
    ;with cte as 
    (select ID = t.item.value('.', 'int') from @x.nodes('//a/b') as t(item)) 
    select * from MyTable inner join cte on Table.ID = cte.ID 
end 

Với tất cả điều này tại chỗ, tôi đã có thể gửi một Đoạn XML của 43,016 ký tự từ máy khách của tôi đến máy chủ SQL và lấy lại một tập kết quả rất nhanh. Tôi đã thử nghiệm 1.000 yêu cầu trên 10 luồng cho tổng số 10.000 yêu cầu. Kết quả là 72 yêu cầu được xử lý mỗi giây. Tất nhiên, tỷ lệ phần trăm của bạn sẽ khác nhau tùy thuộc vào phần cứng và phần mềm của bạn.

LƯU Ý: Mã này hoạt động trên SQL 2005 và cũng nên hoạt động vào năm 2008.

0

Làm cách nào để bạn xác định danh sách các giá trị khóa chính để chọn? Tôi tự hỏi nếu chúng ta nên nhìn vào điều này 'thượng nguồn' hơn nữa - là có một số truy vấn hoặc truy vấn trước đó bạn chạy để đến danh sách các khóa? Nếu vậy, có lẽ bạn có thể làm việc tham gia từ đó và bỏ qua việc tra cứu khóa hoàn toàn.

0

Suy nghĩ đầu tiên của tôi là xem cách bạn đang kéo danh sách khóa. Là nó từ một truy vấn ở nơi khác? Nếu vậy, bạn đã thử

SELECT * FROM Table WHERE [PrimaryKey] in 
(
    SELECT PrimaryKey from SomeOtherTable where Condition = 'met' 
) 

hoặc nếu có điều kiện bạn đang ở trong cùng một bảng, nó thậm chí còn đơn giản hơn

SELECT * FROM Table WHERE condition = 'met' 

Tất nhiên, nếu phím của bạn đều xuất phát từ một nguồn khác (ứng dụng của bạn, có lẽ), sau đó cá nhân tôi sẽ gắn bó với một trong các phương pháp được đề xuất trước đó, hoặc có thể cắn viên đạn và sử dụng một mệnh đề IN lớn.

BTW - xin lỗi nếu tôi dạy bất cứ ai để hút trứng, nó chỉ là kinh nghiệm của tôi đã được rằng đôi khi các giải pháp đơn giản nhất bị bỏ qua.

Các vấn đề liên quan