[英]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.