Hash Match bên trong tham gia trong truy vấn đơn giản với trong câu lệnh


7

Tôi đang chạy kế hoạch thực hiện cho truy vấn sau:

select m_uid from EmpTaxAudit 
where clientid = 91682 
and  empuid = 42100176452603
and newvalue in('Deleted','DB-Deleted','Added')

Đây là kế hoạch thực hiện:

Kế hoạch thực hiện

Tôi có một chỉ mục không được nhóm trên Bảng EmpTaxAudit trên các cột ClientId và NewValue hiển thị ở trên 14,9% của việc thực hiện:

CREATE NONCLUSTERED INDEX [idx_EmpTaxAudit_clientid_newvalue] ON [dbo].

[EmpTaxAudit]
(
    [ClientID] ASC,
    [NewValue] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

Tôi cũng có một PK chỉ mục duy nhất không phân cụm như sau:

ALTER TABLE [dbo].[EmpTaxAudit] ADD  CONSTRAINT [PK_EmpTaxAudit] PRIMARY KEY NONCLUSTERED 
(
    [ClientID] ASC,
    [EmpUID] ASC,
    [m_uid] ASC,
    [m_eff_start_date] ASC,
    [ReplacedOn] ASC,
    [ColumnName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

Mã kích hoạt trong bảng nguồn EmpTax:

CREATE                trigger [dbo].[trins_EmpTax]
on [dbo].[emptax]
for insert
as
begin
  declare 
    @intRowCount  int,
      @user          varchar(30)

  select @intRowCount = @@RowCount
  IF @intRowCount > 0 
  begin
     select @user = suser_sname()

     insert EmpTaxAudit (Clientid, empuid,m_uid,m_eff_start_date, ColumnName, ReplacedOn, ReplacedBy, OldValue,dblogin,newvalue)
     select Clientid, empuid,m_uid,m_eff_start_date,'taxcode', getdate(),IsNull(userid,@user), '', Left(@user,15),'Added'
        from inserted i
         where m_uid not in (select m_uid from EmpTaxAudit 
                    where clientid = i.clientid  and (newvalue = 'Deleted'
            or newvalue = 'DB-Deleted'
                        or newvalue = 'Added') and  empuid = i.empuid)
       and i.m_eff_end_date is null 

     insert EmpTaxAudit (Clientid, empuid,m_uid,m_eff_start_date, ColumnName, ReplacedOn, ReplacedBy, OldValue,dblogin,newvalue)
     select Clientid, empuid,m_uid,m_eff_start_date,'taxcode', getdate(),IsNull(userid,@user), '', Left(@user,15),'Deleted'
        from inserted i
         where m_uid not in (select m_uid from EmpTaxAudit 
                    where clientid = i.clientid  and (newvalue = 'Deleted'
            or newvalue = 'DB-Deleted'
                        or newvalue = 'Added') and  empuid = i.empuid)
       and i.m_eff_end_date is not null 


  end
end

Tôi có thể làm gì để tránh chi phí cao của Hash Match (Tham gia nội bộ)?

Cảm ơn!


1
Nhưng truy vấn có thực sự chậm? Những con số này (74%, 11%, 14,9%) không hữu ích lắm.
ypercubeᵀᴹ

Nó không chậm đối với một bản ghi nhưng điều này được gọi từ trình kích hoạt chèn nhiều hàng rất chậm
Adolfo Perez

Chỉ cần thêm kích hoạt. Bạn có cần bảng tạo cho EmpTaxAudit không?
Adolfo Perez

Có một số lý do cụ thể mà bạn không có chỉ số phân cụm được xác định (hoặc có lẽ bạn không đưa nó vào ví dụ của mình)? Không có chỉ số phân cụm tạo ra một đống và họ có thể thường xuyên thực hiện tối ưu phụ. Bạn có thể 'thử' phân cụm 'khóa chính của mình để xem điều đó có khác biệt gì không. Đăng xml cho kế hoạch thực hiện có thể cho chúng ta nhiều hơn để tiếp tục.
Scott Hodgin

Câu trả lời:


3

Đối với truy vấn thứ nhất, một chỉ mục sử dụng cả ba cột từ WHEREmệnh đề và bao gồm cột từ SELECTdanh sách sẽ hữu ích hơn nhiều:

-- index suggestion A
(clientid, empuid, newvalue) INCLUDE (m_uid)

hoặc một chỉ mục được nhắm mục tiêu cụ thể cho truy vấn này:

-- index suggestion B
(clientid, empuid, m_uid)
WHERE newvalue in ('Deleted', 'DB-Deleted', 'Added')

Về kích hoạt, một số ý kiến:

  • Truy vấn đầu tiên bạn hiển thị không xuất hiện trong trình kích hoạt. Những gì xuất hiện là một phép nối từ bảng đó đến các insertedhàng sang bảng khác (có trình kích hoạt).
  • Gợi ý của tôi ở trên có vẻ phù hợp hơn để được kích hoạt sử dụng.
  • Kích hoạt có 2 inserttuyên bố gần như giống hệt nhau . Tại sao? Tôi nghĩ rằng chúng có thể được kết hợp trong một - và đơn giản hơn - chèn và sử dụng NOT EXISTSthay vì NOT IN:

    insert EmpTaxAudit 
      ( Clientid, empuid, m_uid, m_eff_start_date, ColumnName, 
        ReplacedOn, ReplacedBy, OldValue, dblogin, 
        newvalue
      )
    select 
        Clientid, empuid, m_uid, m_eff_start_date, 'taxcode',
        getdate(), IsNull(userid,@user), '', Left(@user,15),
        case when m_eff_end_date is null 
            then 'Added' else 'Deleted'
        end
    from inserted i
    where not exists 
          ( select 1 
            from EmpTaxAudit 
            where m_uid = i.m_uid 
              and clientid = i.clientid  
              and empuid = i.empuid
              and newvalue in ('Deleted', 'DB-Deleted', 'Added')
          ) ;

Lời giải thích hay với gợi ý A & B. Dành cho +1
Md Haidar Ali Khan

1

Lý do cho Hash Match (tham gia bên trong):

"Tham gia Hash Match xảy ra khi SQL Server tham gia hai bảng bằng cách băm các hàng từ nhỏ hơn trong hai bảng được nối và sau đó chèn chúng vào bảng băm, sau đó xử lý một bảng lớn hơn một lần so với bảng băm nhỏ hơn bảng, tìm kiếm các kết quả khớp trong đó các hàng cần được nối. Vì các bảng nhỏ hơn cung cấp các giá trị trong bảng băm, kích thước bảng được giữ ở mức tối thiểu và vì các giá trị băm thay vì giá trị thực được sử dụng, nên có thể so sánh Rất nhanh, miễn là bảng được băm tương đối nhỏ, đây có thể là một quá trình nhanh chóng. Mặt khác, nếu cả hai bảng đều rất lớn, phép nối Hash Match có thể rất kém hiệu quả so với các loại phép nối khác. "

Vì vậy, rõ ràng bảng nhỏ hơn của bạn trả về 16 hàng và lớn hơn trả lại 1,41.000 hàng. Bảng nhỏ hơn này là hàm băm khá tốt, bây giờ nó sẽ tham gia tới 1.41.000 hàng nên chi phí phải nhiều hơn và thời gian thực hiện phải chậm.

bạn phải giảm thiểu số lượng hàng trả về trong bảng lớn. Bạn đang tìm kiếm chỉ mục trong cả hai trường hợp để chỉ mục được sử dụng.

Tôi nghĩ bạn chỉ cần viết lại truy vấn kích hoạt.

CREATE  trigger [dbo].[trins_EmpTax]
on [dbo].[emptax]
for insert
as
begin

if @@rowcount = 0 
        return
    set nocount on

  declare 
    @intRowCount  int,
      @user          varchar(30)

  --select @intRowCount = @@RowCount
  if exists(select * from inserted)
  begin
     select @user = suser_sname()

     insert EmpTaxAudit (Clientid, empuid,m_uid,m_eff_start_date, ColumnName, ReplacedOn, ReplacedBy, OldValue,dblogin,newvalue)
     select Clientid, empuid,m_uid,m_eff_start_date,'taxcode', getdate(),IsNull(userid,@user), '', Left(@user,15)
     ,case when i.m_eff_end_date is null then 'Added' else 'Deleted' END
        from inserted i
         where not exists (select m_uid from EmpTaxAudit ETA
                    where i.m_uid=ETA.m_uid and  empuid = i.empuid and clientid = i.clientid  
                    and newvalue in('Deleted','DB-Deleted' , 'Added') 
                    )


     --insert EmpTaxAudit (Clientid, empuid,m_uid,m_eff_start_date, ColumnName, ReplacedOn, ReplacedBy, OldValue,dblogin,newvalue)
     --select Clientid, empuid,m_uid,m_eff_start_date,'taxcode', getdate(),IsNull(userid,@user), '', Left(@user,15),'Deleted'
     --   from inserted i
     --    where m_uid not in (select m_uid from EmpTaxAudit 
     --               where clientid = i.clientid  and (newvalue = 'Deleted'
     --       or newvalue = 'DB-Deleted'
     --                   or newvalue = 'Added') and  empuid = i.empuid)
     --  and i.m_eff_end_date is not null 


  end
end

Lưu ý rằng bạn có thể sửa điều kiện bên trong NOT EXISTSmệnh đề nhỏ, phần còn lại sẽ hoạt động hoàn hảo và hoạt động tốt hơn.

Cũng quan tâm để giải thích rằng các chỉ số được nhóm trên bảng này là gì và tại sao rất nhiều khóa chính tổng hợp.?


-1

Phê bình đầu tiên: Tôi không nghĩ PRIMARY KEYnên bao gồm rất nhiều lĩnh vực. Khóa chính chỉ có nghĩa là một định danh đơn giản về tính duy nhất. Có thể cho rằng bạn có thể có hai trường trong PK của mình cho các bảng giao nhau nhiều-nhiều, mặc dù cá nhân tôi luôn có một khóa chính một trường, ngay cả đối với các trường này. Vì vậy, tôi muốn nói rằng PK của bạn PK_EmpTaxAuditthực sự nên được xác định lại là một chỉ mục duy nhất.

Sau đó, tôi sẽ đề nghị thêm newvaluevào các INCLUDEtrường của chỉ mục đó:

create unique index IX_EmpTaxAudit(
  [ClientID] ASC,
  [EmpUID] ASC,
  [m_uid] ASC,
  [m_eff_start_date] ASC,
  [ReplacedOn] ASC,
  [ColumnName] ASC
)
include (newvalue)

Tôi nghi ngờ rằng sẽ cung cấp cho bạn một kế hoạch thực hiện gọn gàng hơn.


-1

Trước hết, bạn nên có một chỉ mục được nhóm (với FILLFACTOR <= 95) trên các bảng để tránh IO cao. Bởi vì HEAP gây ra sự phân mảnh nhiều hơn. Vì vậy, bạn có thể thay thế PK Non Clustered Index bằng Clustered.

Ngoài ra, để tránh HASH_MATCH, bạn nên có tất cả các cột được sử dụng trong câu lệnh SELECT và mệnh đề WHERE phải nằm trong một chỉ mục. Vì vậy, hoặc bạn thay thế chỉ mục không phân cụm idx_EmpTaxAudit_clientid_newvalue bằng một quặng dưới đây tạo ra chỉ mục không phân cụm mới.

    CREATE NONCLUSTERED INDEX [idx_EmpTaxAudit_clientid_newvalue] ON [dbo].

    [EmpTaxAudit]
    (
        [ClientID] ASC,
        [NewValue] ASC,
        [newvalue]
    )
    INCLUDE (m_uid)
    WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)

Lý do của HASH_MATCH

Trình tối ưu hóa máy chủ SQL thích sử dụng chỉ mục thay vì HEAP. Vì vậy, trong trường hợp của bạn, dữ liệu và cột được yêu cầu trong đó điều kiện nằm trên hai chỉ mục khác nhau. Đó là lý do tại sao SQL Server Tối ưu hóa sử dụng cả hai chỉ mục và thực hiện HASH_MATCH.

Cảm ơn

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.