簡體   English   中英

如果要索引的列是SQL Server中的nvarchar數據類型,該怎么辦?

[英]What if the column to be indexed is nvarchar data type in SQL Server?

我通過連接多個表來檢索數據,如下圖所示。 另一方面,由於事件表的FK列(EmployeeID)中沒有數據,因此我必須使用CardNo(nvarchar)字段才能連接兩個表。 另一方面,Event和Employee表中CardNo字段的位數不同,我還必須使用SQL Server的RIGHT函數,這會使查詢的執行時間延長大約10倍。 那么,在這個場景中我該怎么辦? 我可以使用CardNo字段而不將其數據類型更改為int等嗎(因為將其更改后可能還會出現其他問題,並且最好在不更改其數據類型的情況下找到解決方案)。 這也是下面查詢的執行計划。

查詢:

; WITH a AS (SELECT emp.EmployeeName, emp.Status, dep.DeptName, job.JobName, emp.CardNo 
    FROM TEmployee emp 
    LEFT JOIN TDeptA AS dep ON emp.DeptAID = dep.DeptID 
    LEFT JOIN TJob AS job ON emp.JobID = job.JobID),                           

b AS (SELECT eve.EventID, eve.EventTime, eve.CardNo, evt.EventCH, dor.DoorName 
    FROM TEvent eve LEFT JOIN TEventType AS evt ON eve.EventType = evt.EventID
    LEFT JOIN TDoor AS dor ON eve.DoorID = dor.DoorID) 
    SELECT * FROM b LEFT JOIN a ON RIGHT(a.CardNo, 8) = RIGHT(b.CardNo, 8)

ORDER BY b.EventID ASC

連接架構

執行計划

您可以像這樣向表中添加一個計算列:

ALTER TABLE TEmployee -- Don't start your table names with prefixes, you already know they're tables
ADD CardNoRight8 AS RIGHT(CardNo, 8) PERSISTED

ALTER TABLE TEvent
ADD CardNoRight8 AS RIGHT(CardNo, 8) PERSISTED

CREATE INDEX TEmployee_CardNoRight8_IDX ON TEmployee (CardNoRight8)
CREATE INDEX TEvent_CardNoRight8_IDX ON TEvent (CardNoRight8)

您不需要保留該列,因為它已經與要索引的計算列的條件匹配,但是添加PERSISTED關鍵字應該不會有問題,並且可能有助於其他查詢的性能。 這將對更新和插入造成較小的性能影響,但是對於您而言,這可能很好,除非您一次導入大量數據(數百萬行)。

不過,更好的解決方案是確保應該匹配的列實際匹配。 如果卡號的右邊8個字符有意義,則它們不應成為卡號的一部分,而應在另一列中。 如果這是一個表使用前導零而另一個表不使用前導零的問題,則應修復該數據以保持一致,而不是像這樣將變通方法放在一起。

這行代碼使您花費了86%的查詢時間:

LEFT JOIN a ON RIGHT(a.CardNo, 8) = RIGHT(b.CardNo, 8)

發生這種情況是因為它必須在每行的那些字段上運行RIGHT() ,然后將它們與另一個表匹配。 這顯然是低效的。

最直接的解決方案可能是完全刪除RIGHT()或將其重新實現為表中的內置列,因此在查詢運行時不必立即進行計算。

在插入記錄時,您還必須插入卡號的八個右數字並將其存儲在此字段中。 我最初的想法是使用計算列,但是我不認為這些索引可以被索引,因此您必須使用常規列。

; WITH a AS (
    SELECT emp.EmployeeName, emp.Status, dep.DeptName, job.JobName, emp.CardNoRightEight 
    FROM TEmployee emp 
    LEFT JOIN TDeptA AS dep ON emp.DeptAID = dep.DeptID 
    LEFT JOIN TJob AS job ON emp.JobID = job.JobID
),                           
b AS (
    SELECT eve.EventID, eve.EventTime, eve.CardNoRightEight, evt.EventCH, dor.DoorName 
    FROM TEvent eve LEFT JOIN TEventType AS evt ON eve.EventType = evt.EventID
    LEFT JOIN TDoor AS dor ON eve.DoorID = dor.DoorID
) 
SELECT *
FROM b
LEFT JOIN a ON a.CardNoRightEight = b.CardNoRightEight
ORDER BY b.EventID ASC

這將幫助您了解如何向數據庫中添加計算列。

create table #temp (test varchar(30))
insert into #temp
values('000456')

alter table #temp
add test2 as right(test, 3) persisted

select * from #temp

另一種選擇是修復數據和數據條目,以使兩列都是相同的數據類型並包含相同的前導零(或刪除它們)

非常感謝您的幫助。 借助您的答案,在使用計算列之后的第一步中,我設法將查詢執行時間從2分鍾減少到1分鍾。 之后,在為這些列創建索引時,我設法將執行時間減少到3秒。 哇,真的很完美:)

這是為遭受類似問題的人發布的步驟:

步驟I:將計算列添加到表中(由於CardNo字段是nvarchar數據類型,因此我將計算列的數據類型指定為int):

ALTER TABLE TEvent ADD CardNoRightEight AS RIGHT(CAST(CardNo AS int), 8)  
ALTER TABLE TEmployee ADD CardNoRightEight AS RIGHT(CAST(CardNo AS int), 8) 


步驟II:為計算列創建索引,以便更快地執行查詢:

CREATE INDEX TEmployee_CardNoRightEight_IDX ON TEmployee (CardNoRightEight)
CREATE INDEX TEvent_CardNoRightEight_IDX ON TEvent (CardNoRightEight)


步驟3:使用查詢中的計算列更新查詢:

; WITH a AS (
    SELECT emp.EmployeeName, emp.Status, dep.DeptName, job.JobName, emp.CardNoRightEight --emp.CardNo 
    FROM TEmployee emp 
    LEFT JOIN TDeptA AS dep ON emp.DeptAID = dep.DeptID 
    LEFT JOIN TJob AS job ON emp.JobID = job.JobID
    ),                         
b AS (
    SELECT eve.EventID, eve.EventTime, evt.EventCH, dor.DoorName, eve.CardNoRightEight --eve.CardNo
    FROM TEvent eve 
    LEFT JOIN TEventType AS evt ON eve.EventType = evt.EventID 
    LEFT JOIN TDoor AS dor ON eve.DoorID = dor.DoorID) 

SELECT * FROM b LEFT JOIN a ON a.CardNoRightEight = b.CardNoRightEight --ON RIGHT(a.CardNo, 8) = RIGHT(b.CardNo, 8)
ORDER BY b.EventID ASC

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM