[英]How to order by date when a column is of type nvarchar in Microsoft SQL Server
[英]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.