2012-03-23 28 views
5

Trước khi có bất cứ điều gì, tôi không tìm kiếm viết lại. Điều này đã được trình bày cho tôi, và tôi dường như không thể tìm ra nếu đây là một lỗi nói chung hoặc một số loại craziness cú pháp xảy ra do sự khác biệt của kịch bản. Tạm bằng lòng với những gì đã nói trên với các thiết lập:Tại sao truy vấn phụ này dường như không hoạt động?

  • Microsoft SQL Server Standard Edition (64-bit)

  • Version 10.50.2500.0

Trên bàn nằm trong một chung cơ sở dữ liệu, được định nghĩa là:

CREATE TABLE [dbo].[Regions](
    [RegionID] [int] NOT NULL, 
    [RegionGroupID] [int] NOT NULL, 
    [IsDefault] [bit] NOT NULL, 
CONSTRAINT [PK_Regions] PRIMARY KEY CLUSTERED 
(
    [RegionID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

chèn một số giá trị:

INSERT INTO [dbo].[Regions] 
([RegionID],[RegionGroupID],[IsDefault]) 
VALUES 
(0,1,0), 
(1,1,0), 
(2,1,0), 
(3,2,0), 
(4,2,0), 
(5,2,0), 
(6,3,0), 
(7,3,0), 
(8,3,0) 

Bây giờ chạy các truy vấn (để chọn một duy nhất từ ​​mỗi nhóm, hãy nhớ không có lời đề nghị viết lại!):

SELECT RXXID FROM (
    SELECT 
     RXX.RegionID as RXXID, 
     ROW_NUMBER() OVER (PARTITION BY RXX.RegionGroupID ORDER BY RXX.RegionGroupID) AS RXXNUM 
    FROM Regions as RXX 
) AS tmp 
WHERE tmp.RXXNUM = 1 

Bạn sẽ nhận được:

RXXID 
----------- 
0 
3 
6 

Bây giờ hãy dính vào e một báo cáo cập nhật (với bộ tuỳ chọn để 0 và chọn tất cả sau khi):

UPDATE Regions SET IsDefault = 0 

UPDATE Regions 
SET IsDefault = 1 
WHERE RegionID IN (
    SELECT RXXID FROM (
     SELECT 
      RXX.RegionID as RXXID, 
      ROW_NUMBER() OVER (PARTITION BY RXX.RegionGroupID ORDER BY RXX.RegionGroupID) AS RXXNUM 
     FROM Regions as RXX 
    ) AS tmp 
    WHERE tmp.RXXNUM = 1 
) 


SELECT * FROM Regions 
ORDER BY RegionGroupID 

và nhận được kết quả này:

RegionID RegionGroupID IsDefault 
----------- ------------- --------- 
0   1    1 
1   1    1 
2   1    1 
3   2    1 
4   2    1 
5   2    1 
6   3    1 
7   3    1 
8   3    1 

ZOMG wtf lamaz?

Mặc dù tôi không tuyên bố là một guru SQL, điều này có vẻ không đúng và cũng không chính xác. Và để làm những việc điên rồ hơn, nếu bạn thả phím chính nó có vẻ làm việc:

Drop khóa chính:

IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[Regions]') AND name = N'PK_Regions') 
ALTER TABLE [dbo].[Regions] DROP CONSTRAINT [PK_Regions] 

và chạy lại bản cập nhật tuyên bố thiết lập, kết quả:

RegionID RegionGroupID IsDefault 
----------- ------------- --------- 
0   1    1 
1   1    0 
2   1    0 
3   2    1 
4   2    0 
5   2    0 
6   3    1 
7   3    0 
8   3    0 

Đó không phải là ab?

Có ai có bất kỳ đầu mối nào đang xảy ra ở đây không? Đoán của tôi là một số loại bộ nhớ đệm truy vấn phụ và đây có phải là lỗi không? Nó chắc chắn không giống như những gì SQL nên được làm?

+0

Đây là btw một câu hỏi rất thú vị! – cairnz

Trả lời

9

Chỉ cần cập nhật như một CTE trực tiếp:

WITH tmp AS (
SELECT 
     RegionID as RXXID, 
     RegionGroupID, 
     IsDefault, 
     ROW_NUMBER() OVER (PARTITION BY RegionGroupID ORDER BY RegionID) AS RXXNUM 
    FROM Regions 

) 
UPDATE tmp SET IsDefault = 1 WHERE RXXNUM = 1 
select * from Regions 

thêm nhiều cột để minh họa.Bạn có thể thấy điều này trên http://sqlfiddle.com/#!3/03913/9

Không chắc chắn 100% điều gì đang xảy ra trong ví dụ của bạn, nhưng vì bạn phân vùng và đặt hàng theo cùng một cột, bạn không thực sự chắc chắn để có cùng thứ tự trở lại, vì tất cả đều gắn liền. Không nên bạn đặt hàng theo RegionID hoặc một số cột khác, như tôi đã làm trên sqlfiddle?


Về câu hỏi của bạn:

Nếu bạn thay đổi cập nhật của bạn (với chỉ số clustered) vào một SELECT, bạn sẽ nhận được tất cả 9 hàng trở lại. Nếu bạn thả PK và thực hiện lệnh SELECT, bạn chỉ nhận được 3 hàng. Quay lại câu lệnh cập nhật của bạn. Kiểm tra việc thực hiện kế hoạch cho thấy rằng họ khác nhau đôi chút:

First (PK) Execution plan Second (No PK) Execution plan

Những gì bạn có thể thấy ở đây là trong (với PK) truy vấn đầu tiên, bạn sẽ quét các nhóm chỉ số cho tài liệu tham khảo bên ngoài, lưu ý rằng nó không có bí danh RXX. Sau đó, đối với mỗi hàng ở trên cùng, hãy tìm kiếm RXX. Và có, vì thứ tự số hàng của bạn, mỗi RegionID có thể là row_number() 1 cho mỗi RegionGroupID. SQL Server sẽ biết điều này dựa trên PK của bạn, tôi đoán, và có thể nói rằng Đối với mỗi RegionID, RegionID này có thể là số hàng 1. Vì vậy, tuyên bố là khá hợp lệ.

Trong truy vấn thứ hai, không có chỉ mục và bạn nhận được bảng quét trên Vùng, sau đó nó tạo bảng thăm dò bằng RXX và tham gia khác nhau (một lần, ROW_NUMBER() chỉ có thể là 1 cho một hàng cho mỗi regiongroupid bây giờ). Bằng cách này trong quá trình quét, mọi RegionID chỉ có một ROW_NUMBER(), mặc dù bạn không thể chắc chắn 100% nó sẽ giống nhau mỗi lần.

Điều này có nghĩa là: Sử dụng truy vấn phụ không có thứ tự xác định cho mỗi lần thực hiện, bạn nên tránh sử dụng loại kết nối nhiều lần (NESTED LOOP), nhưng một lần tham gia (MERGE OR HASH).

Để sửa lỗi này mà không thay đổi cấu trúc của câu hỏi của bạn, thêm OPTION (HASH JOIN) hoặc OPTION (MERGE JOIN) đến CẬP NHẬT đầu tiên:

Vì vậy, bạn sẽ cần phải báo cáo cập nhật sau (khi bạn có PK):

UPDATE Regions SET IsDefault = 0 

UPDATE Regions 
SET IsDefault = 1 
WHERE RegionID IN (
    SELECT RXXID FROM (
     SELECT 
      RXX.RegionID as RXXID, 
      ROW_NUMBER() OVER (PARTITION BY RXX.RegionGroupID ORDER BY RXX.RegionGroupID) AS RXXNUM 
     FROM Regions as RXX 
    ) AS tmp 
    WHERE tmp.RXXNUM = 1 
) 
OPTION (HASH JOIN) 

SELECT * FROM Regions 
ORDER BY RegionGroupID 

Sau đây là các kế hoạch thực hiện sử dụng hai tham gia loại (lưu ý con số thực tế của hàng: 3 trong các thuộc tính):

Using MERGE JOIN Using HASH JOIN

+1

Tôi nhận thấy điều này không trả lời được câu hỏi của bạn (và tôi đang xem nó trên môi trường, nhưng nó sẽ giúp bạn giải quyết vấn đề này, và tránh bỏ chọn không cần thiết) – cairnz

+1

chắc chắn đủ nếu bạn chuyển yêu cầu của mình sang RegionID, dự kiến ​​(b) kết quả, và tôi đã nhận được kết quả tương tự trong phiên bản 10,0 như ông ckozl là với các truy vấn ông trình bày. –

+0

Bạn nhận được một cuộc bỏ phiếu lên để được vào mũi. 'ROW_NUMBER() OVER (PARTITION BY RXX.RegionGroupID ORDER BY RXX.RegionID) AS RXXNUM' sửa chữa nó một cách hoàn hảo. nhưng 50% của câu hỏi là tại sao hành vi này là cách nó là ... vì nó dường như không có ý nghĩa nếu ai đó không nhận được nó mặc dù bạn sẽ nhận được cookie cho những nỗ lực của bạn. lam tôt lăm! – ckozl

3

Truy vấn của bạn bằng ngôn ngữ đơn giản là:
Đối với mỗi hàng trong Regions, hãy kiểm tra xem có RegionID tồn tại trong một số truy vấn phụ hay không. Có nghĩa là truy vấn phụ được thực hiện cho mỗi hàng trong Regions. (Tôi biết đó không phải là trường hợp nhưng nó là ngữ nghĩa của truy vấn).

Vì bạn đang sử dụng RegionGroupID làm đơn đặt hàng và phân vùng, bạn thực sự không có ý tưởng gì RegionID sẽ được trả lại để nó có thể là một ID mới cho mỗi lần truy vấn phụ được kiểm tra.

Cập nhật:

Làm bản cập nhật với một tham gia so với đồng bảng xuất phát thay vì thay vì sử dụng trong thay đổi ngữ nghĩa của các truy vấn và nó đã thay đổi kết quả là tốt.

này làm việc như mong đợi:

UPDATE R 
SET IsDefault = 1 
FROM Regions as R 
    inner join 
     (
     SELECT RXXID FROM (
      SELECT 
       RXX.RegionID as RXXID, 
       ROW_NUMBER() OVER (PARTITION BY RXX.RegionGroupID ORDER BY RXX.RegionGroupID) AS RXXNUM 
      FROM Regions as RXX 
     ) AS tmp 
     WHERE tmp.RXXNUM = 1 
    ) as C 
    on R.RegionID = C.RXXID 
+0

Điều này làm việc bằng cách sử dụng một LESTP NESTED vì C được xây dựng đầu tiên (và chỉ Một lần). – cairnz

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