Cảnh báo về chuyển đổi ngầm


7

Tôi có bảng có 2 cột tên:

CREATE TABLE Test
(
  TestID int identity primary key clustered
  , Name_Eng nvarchar(50)
  , Name_Nat nvarchar(50)
)

Bây giờ tôi cần một truy vấn để Namephân tách cột này ,, như thế này:

    DECLARE @NameColumns NVARCHAR(1024)

            SET @NameColumns = STUFF(
      (SELECT   ',' + 'Test.' + name AS [text()]
       FROM     ( SELECT    c.name
                  FROM      sys.columns c 
                            INNER JOIN sys.tables t ON t.object_id = c.object_id
                  WHERE     t.name = 'Test'
                            AND c.name LIKE 'Name_%'
                ) AS D
                FOR                  XML PATH('') ,
                                         TYPE).value('.[1]', 'VARCHAR(MAX)'), 1, 1,
                                     N'')

select  @NameColumns

Nhưng truy vấn này có một cảnh báo trong kế hoạch thực hiện:

cảnh báo chuyển đổi ngầm

Có cách nào để loại bỏ cảnh báo này?

Câu trả lời:


9

Cảnh báo là có vì chức năng XML value(). Tham số thứ hai value()là những gì bạn muốn giá trị được lưu trữ trong XML được chuyển đổi thành. Bạn có thể lập luận rằng trên thực tế đây không phải là một chuyển đổi ngầm mà là một chuyển đổi rất rõ ràng vì bạn đang yêu cầu nó xảy ra. Có lẽ một cái gì đó cho một mục kết nối để đề xuất với Microsoft.

Cách đơn giản nhất để tái tạo những gì bạn nhìn thấy.

declare @X xml;
select @X.value('text()[1]', 'int');

Đưa ra hai cảnh báo.

<Warnings>
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[lvalue],0)" />
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[value],0)" />
</Warnings>

Như bạn có thể thấy bạn cũng nhận được nó với int và bạn nhận được hai trong số chúng cho mỗi lần gọi value().

declare @X xml;
select @X.value('text()[1]', 'int'),
       @X.value('text()[1]', 'bit');
<Warnings>
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[lvalue],0)" />
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(int,XML Reader with XPath filter.[value],0)" />
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(bit,XML Reader with XPath filter.[lvalue],0)" />
    <PlanAffectingConvert ConvertIssue="Cardinality Estimate" Expression="CONVERT_IMPLICIT(bit,XML Reader with XPath filter.[value],0)" />
</Warnings>

Việc chuyển đổi được thực hiện trong toán tử Stream Aggregate tính toán giá trị như thế này.

MIN(CASE WHEN [@X] IS NULL 
      THEN NULL 
      ELSE 
      CASE WHEN datalength(XML Reader with XPath filter.[value])>=(128) 
        THEN CONVERT_IMPLICIT(int,XML Reader with XPath filter.[lvalue],0) 
        ELSE CONVERT_IMPLICIT(int,XML Reader with XPath filter.[value],0) 
      END 
    END)

Kết quả từ hàm Giá trị bảng được trả về trong cột lvaluehoặc value. Biểu thức kiểm tra bằng cách sử dụng datalengthđể tìm ra nó nên được tìm nạp từ đâu và sau đó chuyển đổi nó thành kiểu dữ liệu mong muốn của bạn.

Có cách nào để loại bỏ cảnh báo này?

Có, có. Bạn loại bỏ lệnh TYPEnày khỏi FOR XML PATHcâu lệnh và bạn loại bỏ cuộc gọi đến value()hàm. Một tác dụng phụ của việc đó là các giá trị mà bạn ghép có chứa các ký tự cần được mã hóa trong XML giống như &<>sẽ được mã hóa trong kết quả của bạn.


9

Mặc dù tôi đồng ý với @Kin về các loại dữ liệu, tôi không nghĩ cảnh báo này rắc rối như bạn nghĩ. Bạn đang thực hiện ghép nối theo nhóm, sẽ là đơn đặt hàng lớn hơn bất kỳ chuyển đổi nào (và như Daniel nói, trừ khi số lượt xem danh mục của bạn rất lớn - như lớn hơn bộ nhớ vật lý - không có khả năng ảnh hưởng đến bất kỳ điều gì về ước tính hoặc).

Tôi sẽ viết truy vấn theo cách này, chú ý không bao giờ sử dụng các chuỗi varchar (điều này cũng có nghĩa là ngừng bỏ Ntiền tố trên chuỗi ký tự chuỗi) và cũng đảm bảo bạn sử dụng các dấu kết thúc câu lệnh :

DECLARE @NameColumns nvarchar(max); -- why 1024 when you use max below?

SET @NameColumns = STUFF(
  (SELECT   N',Test.' + name AS [text()] FROM    
    (
      SELECT c.name FROM sys.columns AS c 
        INNER JOIN sys.tables AS t 
          ON t.object_id = c.object_id
        WHERE t.name = N'Test'
        AND   c.name LIKE N'Name_%'
    ) AS D FOR XML PATH(N''),
    TYPE).value(N'.[1]', N'nvarchar(max)'), 1, 1, N'');

SELECT @NameColumns;

Thậm chí, tôi không tin có bất kỳ cách nào để tránh chuyển đổi ngầm mà không thay đổi đầu ra bằng cách tránh TYPE/ value()như Mikael đề xuất . Tôi sẽ nói lo lắng về điều này khi bạn thực sự có thể chứng minh rằng điều này có một số tác động quan trọng đến hiệu suất của truy vấn. Trong các thử nghiệm của tôi, hai hình thức khác nhau thực hiện giống nhau (chúng tôi nói chuyện phụ 10ms mỗi lần), nhưng tất nhiên nếu tôi có một bảng được đặt tên Sales & Stuffthì nó sẽ trở thành Sales &amp; Stuffkhông có chuyển đổi ngầm định.


3

Cảnh báo trong kế hoạch truy vấn có nghĩa là vì bạn có chuyển đổi kiểu dữ liệu ngầm định, SQL Server sẽ không thể đoán chính xác số lượng hàng được trả về chính xác, do đó có thể dẫn đến một kế hoạch kém tối ưu.

Điều này rất quan trọng trong các truy vấn phải thực hiện tốt, thông thường vì chúng hoạt động với nhiều dữ liệu, nhưng trong tình huống của bạn, điều này dường như không xảy ra, vì bạn chỉ đang truy vấn sys.tablessys.columns.

Câu trả lời ngắn cho câu hỏi của bạn là: không thành vấn đề trừ khi bạn có hàng triệu triệu bảng và cột trong cơ sở dữ liệu của mình.

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.