Tại sao con trỏ này tạo ra kết quả theo thứ tự không chính xác?


7

Tôi đang viết một số SQL động để xác định và, có lẽ nếu tôi cảm thấy đủ điên rồ, sẽ tự động chuyển đổi NONCLUSTEREDcác chỉ mục của tôi thành CLUSTEREDcác chỉ mục.

Dòng ORDER BY 1,2,3 DESC;trong SQL bên dưới được thiết kế để xuất các DROP INDEX...câu lệnh trước các ALTER TABLE...câu lệnh để DROP chỉ mục NONCLUSTERED trước và sau đó thêm chỉ mục CLUSTERED. Tôi đã phải thêm DESCcột sau 3 để có được DROP đầu tiên theo sau là ALTER. Điều này là ngược, trừ khi tôi mất nó!

DECLARE @Server nvarchar(max);
DECLARE @Database nvarchar(max);
DECLARE @cmd nvarchar(max);
DECLARE @IndexType int;


SET @IndexType = 2; /*  1 is CLUSTERED, 2 is NONCLUSTERED */
SET @Server = 'MyServer';
SET @Database = 'MyDatabase';

SET @cmd = '
    DECLARE @cmd nvarchar(max);
    SET @cmd = ''
    SET NOCOUNT ON;
    DECLARE @IndexInfo TABLE (TableName nvarchar(255), IndexName nvarchar(255), IndexColumnName nvarchar(255));
    INSERT INTO @IndexInfo (TableName, IndexName, IndexColumnName)
    SELECT t.name AS TableName, i.name AS IndexName, c.name AS IndexColumnName /*, t.create_date, ic.*, c.* */
    FROM sys.tables t
        LEFT JOIN sys.indexes i ON t.object_id = i.object_id
        LEFT JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
        LEFT JOIN sys.columns c ON i.object_id = c.object_id and ic.column_id = c.column_id
    WHERE i.is_primary_key = 1  
        AND i.type = ' + CAST(@IndexType as nvarchar(max)) + '
    ORDER BY t.create_date desc;
    DECLARE @t1 nvarchar(max);
    DECLARE @t2 nvarchar(max);
    DECLARE @t3 nvarchar(max);
    DECLARE @cmd nvarchar(max);
    DECLARE cur CURSOR FOR
    SELECT TableName, IndexName, 1 AS ExecOrder, ''''DROP INDEX '''' + IndexName + '''' ON '''' + TableName + '''';'''' FROM @IndexInfo I
    UNION ALL 
    SELECT TableName, IndexName, 2 AS ExecOrder, ''''ALTER TABLE '''' + TableName + '''' ADD CONSTRAINT PK_'''' + TableName + ''''_'''' + IndexColumnName + '''' PRIMARY KEY CLUSTERED ('''' + IndexColumnName + '''')'''' + '''';'''' FROM @IndexInfo I
    ORDER BY 1,2,3 DESC;
    OPEN cur;
    FETCH NEXT FROM cur INTO @t1, @t2, @t3, @cmd;
    WHILE @@FETCH_STATUS = 0
    BEGIN
        PRINT @cmd;
        FETCH NEXT FROM cur INTO @t1, @t2, @t3, @cmd;
    END
    CLOSE cur;
    DEALLOCATE cur;
    '';
    EXEC ' + @Server + '.' + @Database + '.sys.sp_executesql @cmd;
';
PRINT @cmd;

Ồ, tôi quên đề cập đến SQL động này được thiết lập để chạy trên máy chủ từ xa. Tôi có thể đăng một phiên bản mà không cần thêm sự phức tạp nếu cần thiết.
Max Vernon

Câu trả lời:


11

Câu PRINTlệnh bên trong WHILEvòng lặp thực hiện theo thứ tự bạn mong đợi, nhưng đầu ra được đệm trước khi sys.sp_executesqltrả về. Chi tiết thực hiện có nghĩa là đầu ra đệm được đảo ngược.

Sử dụng RAISERROR (@cmd, 0, 1) WITH NOWAIT;thay vì PRINTbuộc bộ đệm phải xả sau mỗi cuộc gọi, cho bạn kết quả theo thứ tự bạn mong đợi. NOWAITThủ thuật IIRC chỉ hoạt động trên mỗi hàng trong 500 hàng đầu tiên. Trong mọi trường hợp, tất cả điều này là những thứ không có giấy tờ có thể thay đổi bất cứ lúc nào, vì vậy xin đừng dựa vào nó - tôi chỉ đề cập đến nó để giải thích những gì bạn nhìn thấy.

Việc đảo ngược không xảy ra nếu bạn thay thế sp_executesqlcuộc gọi bằng EXEC (@cmd) AT ' + @Server + 'mặc dù điều đó yêu cầu một USE databaselệnh có tiền tố @cmdvà máy chủ được liên kết cần phải được bật cho RPC. Đây cũng không phải là một khuyến nghị, chỉ hiển thị đảo ngược đầu ra là một điều không hay sp_executesql.


wow - câu trả lời sâu sắc; cảm ơn! Tôi thường sử dụng RAISERROR nhưng đang trở nên lười biếng và PRINT thì ít gõ hơn.
Max Vernon

5

Với các thông tin mới được đưa ra trong các bình luận, có ít nhất ba giải pháp cho vấn đề này không yêu cầu sự lộn xộn của SQL động và thủ công, sau phản ứng thực tế:

dùng thước kẻ

Nghiêm túc mà nói, anh ấy là một DBA có trách nhiệm công việc tại công ty của bạn. Chắc chắn bạn có thể thực hiện một chính sách mà các bảng có các chỉ mục được nhóm trừ các trường hợp nhất định (và trong trường hợp đó, chúng phải được ký tắt). Khi được nêu là một chính sách, điều này trở thành một thước đo có thể định lượng tiềm năng cho hiệu suất công việc.

thực hiện kích hoạt DDL

Rất dễ dàng để nắm bắt CREATE_TABLEcác sự kiện trong trình kích hoạt DDL, sau đó kiểm tra các khung nhìn danh mục sys.indexes hoặc sys.partitions để biết sự hiện diện của một chỉ mục được nhóm. Không có chỉ số cụm? Phục hồi. Điều này sẽ loại bỏ khả năng SELECT INTO, vì bạn không thể xác định trước một chỉ mục được nhóm, nhưng có lẽ điều đó ổn.

làm cho anh ta phát triển trong Azure

Bạn không thể tạo bảng trong đám mây mà không có chỉ mục được nhóm. Nếu bạn làm cho anh ta phát triển ở đó, không cần người cai trị hoặc kích hoạt DDL.


Tại sao bạn sử dụng chức vụ ? Thay vì:

ORDER BY 1,2,3 DESC;

Hãy thử tham khảo các bí danh trực tiếp:

ORDER BY TableName, IndexName, ExecOrder;

Ngoài ra, bạn sử dụng một con trỏ để xây dựng @cmdvà tôi mong đợi ở đâu đó để bạn thêm nó. Khi nó đứng, có vẻ như bạn đang lặp qua con trỏ nhưng chỉ thực hiện @cmdtừ lần lặp cuối cùng trong vòng lặp. Vậy mục đích của con trỏ là gì? Và tại sao bạn không sử dụng các tùy chọn con trỏ tốt hơn nếu con trỏ thực sự cần thiết? (Nếu không, bạn có thể thay đổi nó thành một truy vấn TOP duy nhất).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.