简体   繁体   中英

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

I retrieve data by joining multiple tables as indicated on the image below. On the other hand, as there is no data in the FK column (EmployeeID) of Event table, I have to use CardNo (nvarchar) fields in order to join the two tables. On the other hand, the digit numbers of CardNo fields in the Event and Employee tables are different, I also have to use RIGHT function of SQL Server and this makes the query to be executed approximately 10 times longer. So, in this scene what should I do? Can I use CardNo field without changing its data type to int, etc (because there are other problem might be seen after changing it and it sill be better to find a solution without changing the data type of it). Here is also execution plan of the query below.

Query:

; 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

连接架构

执行计划

You can add a computed column to your table like this:

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)

You don't need to persist the column since it already matches the criteria for a computed column to be indexed, but adding the PERSISTED keyword shouldn't hurt and might help the performance of other queries. It will cause a minor performance hit on updates and inserts, but that's probably fine in your case unless you're importing a lot of data (millions of rows) at a time.

The better solution though is to make sure that your columns that are supposed to match actually match. If the right 8 characters of the card number are something meaningful, then they shouldn't be part of the card number, they should be another column. If this is an issue where one table uses leading zeroes and the other doesn't then you should fix that data to be consistent instead of putting together work arounds like this.

This line is what is costing you 86% of the query time:

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

This is happening because it has to run RIGHT() on those fields for every row and then match them with the other table. This is obviously going to be inefficient.

The most straightforward solution is probably to either remove the RIGHT() entirely or else to re-implement it as a built-in column on the table so it doesn't have to be calculated on the fly while the query is running.

While inserting the record, you would have to also insert the eight, right digits of the card number and store it in this field. My original thought was to use a computed column but I don't think those can be indexed so you'd have to use a regular column.

; 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

This will help you see how to add a calculated column to your database.

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

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

select * from #temp

The other alternative is to fix the data and the data entry so that both columns are the same data type and contain the same leading zeros (or remove them)

Many thanks all of your help. With the help of your answers, I managed to reduce the query execution time from 2 minutes to 1 at the first step after using computed columns. After that, when creating an index for these columns, I managed to reduce the execution time to 3 seconds. Wow, it is really perfect :)

Here are the steps posted for those who suffers from a similar problem:

Step I: Adding computed columns to the tables (As CardNo fields are nvarchar data type, I specify data type of computed columns as 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) 


Step II: Create index for the computed columns in order to execute the query faster:

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


Step 3: Update the query by using the computed columns in it:

; 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM