การเปรียบเทียบสตริงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ใน LINQ-to-SQL


137

ฉันอ่านแล้วว่ามันไม่ฉลาดที่จะใช้ ToUpper และ ToLower เพื่อทำการเปรียบเทียบสตริงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ แต่ฉันไม่เห็นทางเลือกอื่นเมื่อพูดถึง LINQ-to-SQL อาร์กิวเมนต์ IgnaseCase และ CompareOptions ของ String.Compare จะถูกละเว้นโดย LINQ-to-SQL (ถ้าคุณใช้ฐานข้อมูลที่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่คุณจะได้รับการเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ ToLower หรือ ToUpper เป็นตัวเลือกที่ดีที่สุดหรือไม่ ดีกว่าอีกไหม? ฉันคิดว่าฉันอ่านที่ไหนสักแห่งว่า ToUpper ดีกว่า แต่ฉันไม่รู้ว่ามันใช้กับที่นี่หรือไม่ (ฉันกำลังตรวจสอบโค้ดจำนวนมากและทุกคนใช้ ToLower)

Dim s = From row In context.Table Where String.Compare(row.Name, "test", StringComparison.InvariantCultureIgnoreCase) = 0

สิ่งนี้แปลเป็นเคียวรี SQL ที่เปรียบเทียบ row.Name กับ "test" และจะไม่ส่งคืน "Test" และ "TEST" บนฐานข้อมูล


1
ขอบคุณ! สิ่งนี้ช่วยชีวิตฉันไว้ได้แล้ววันนี้ หมายเหตุ: จะทำงานร่วมกับส่วนขยาย LINQ อื่น ๆ ด้วยเช่นและLINQQuery.Contains("VaLuE", StringComparer.CurrentCultureIgnoreCase) LINQQuery.Except(new string[]{"A VaLUE","AnOTher VaLUE"}, StringComparer.CurrentCultureIgnoreCase)ฮู!
Greg Bray

ขำ ๆ ฉันแค่อ่านว่า ToUpper ดีกว่าในการเปรียบเทียบกับแหล่งข้อมูลนี้: msdn.microsoft.com/en-us/library/dd465121
malckier

คำตอบ:


110

ดังที่คุณกล่าวว่ามีความแตกต่างที่สำคัญระหว่าง ToUpper และ ToLower และมีเพียงสิ่งเดียวที่ถูกต้องเชื่อถือได้เมื่อคุณพยายามทำการตรวจสอบความเท่าเทียมกันแบบไม่รู้สึกตัว

วิธีที่ดีที่สุดในการตรวจสอบความเท่าเทียมกันแบบคำนึงถึงขนาดตัวพิมพ์คือ :

String.Equals(row.Name, "test", StringComparison.OrdinalIgnoreCase)

โปรดทราบว่านี่ไม่สามารถใช้งานได้ในกรณีนี้! ดังนั้นเราจึงจะติดอยู่กับหรือToUpperToLower

หมายเหตุOrdinal IgnoreCase เพื่อให้ปลอดภัย แต่ประเภทของการตรวจสอบที่ละเอียดอ่อนที่คุณใช้นั้นขึ้นอยู่กับวัตถุประสงค์ของคุณ แต่ในการใช้งานทั่วไปเท่ากับสำหรับการตรวจสอบความเท่าเทียมกันและเปรียบเทียบเมื่อคุณเรียงลำดับแล้วเลือก StringComparison ที่เหมาะสมสำหรับงาน

Michael Kaplan (ผู้มีอำนาจที่ได้รับการยอมรับในด้านวัฒนธรรมและการจัดการตัวละครเช่นนี้) มีการโพสต์ที่เกี่ยวข้องใน ToUpper vs. ToLower:

เขาบอกว่า "String.ToUpper - ใช้ ToUpper แทน ToLower และระบุ InvariantCulture เพื่อรับกฎการทำ OS "


1
ดูเหมือนว่าสิ่งนี้จะไม่นำไปใช้กับ SQL Server: print upper ('GroßeStraße') ส่งคืนGROßESTRAßE
BlueMonkMN

1
นอกจากนี้โค้ดตัวอย่างที่คุณระบุมีปัญหาเช่นเดียวกับรหัสที่ฉันระบุเท่าที่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่เมื่อรันผ่าน LINQ-to-SQL บนฐานข้อมูล MS SQL 2005
BlueMonkMN

2
ฉันเห็นด้วย. ขอโทษฉันไม่ชัดเจน รหัสตัวอย่างที่ฉันให้ไว้ไม่สามารถใช้งานกับ Linq2Sql ได้เนื่องจากคุณชี้ให้เห็นในคำถามเดิมของคุณ ฉันแค่บอกว่าวิธีที่คุณเริ่มเป็นวิธีที่ดีที่จะไป - ถ้ามันใช้ได้เฉพาะในสถานการณ์นี้ และใช่ Soapbox ของ Mike Kaplan อีกอันก็คือการจัดการตัวละครของ SQL Server นั้นครอบคลุมทุกที่ หากคุณต้องการตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ไม่สามารถรับได้ด้วยวิธีอื่นฉันแนะนำ (ไม่ชัดเจน) ว่าคุณจัดเก็บข้อมูลเป็นตัวพิมพ์ใหญ่แล้วค้นหาเป็นตัวพิมพ์ใหญ่
Andrew Arnott

3
ถ้าคุณมีฐานข้อมูลที่มีตัวพิมพ์เล็กและตัวพิมพ์ใหญ่และเก็บไว้ในตัวพิมพ์เล็กและค้นหาในตัวพิมพ์ใหญ่คุณจะไม่ได้รับการจับคู่ หากคุณเพิ่มทั้งข้อมูลและคิวรีในการค้นหาของคุณคุณจะแปลงข้อความทั้งหมดที่คุณค้นหาในทุกการค้นหาซึ่งไม่ได้มีประสิทธิภาพ
Andrew Arnott

1
@BlueMonkMN คุณแน่ใจหรือไม่ว่าคุณวางข้อมูลโค้ดที่ถูกต้อง เป็นการยากที่จะเชื่อว่าเซิร์ฟเวอร์ MSSQL ชอบสีแดงมากกว่าสีดำ
greenoldman

75

ฉันใช้ System.Data.Linq.SqlClient.SqlMethods.Like(row.Name, "test") ในการค้นหาของฉัน

สิ่งนี้ทำการเปรียบเทียบแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่


3
ฮ่า! ใช้ linq 2 sql มาหลายปีแล้ว แต่ไม่เคยเห็น SqlMethods มาก่อนจนถึงตอนนี้ขอบคุณ!
Carl Hörberg

3
ยอดเยี่ยม! สามารถใช้รายละเอียดเพิ่มเติมได้ นี่เป็นหนึ่งในการใช้งานที่คาดหวังของ Like หรือไม่ มีปัจจัยที่เป็นไปได้ที่จะทำให้เกิดผลบวกปลอมหรือไม่? หรือผลลบที่เป็นเท็จ? เอกสารเกี่ยวกับวิธีการนี้ขาดเอกสารที่จะอธิบายการดำเนินการของวิธีการชอบอยู่ที่ไหน
ภารกิจ

2
ฉันคิดว่ามันขึ้นอยู่กับวิธีที่ SQL Server เปรียบเทียบสตริงซึ่งอาจกำหนดค่าได้ที่ใดที่หนึ่ง
Andrew Davey

11
System.Data.Linq.SqlClient.SqlMethods.Like (row.Name, "test") จะเหมือนกับ row.Name.Contains ("test") อย่างที่แอนดรูว์พูดขึ้นอยู่กับการเปรียบเทียบของเซิร์ฟเวอร์ sql ดังนั้นไลค์ (หรือมี) จึงไม่ได้ทำการเปรียบเทียบแบบตัวพิมพ์ใหญ่และตัวพิมพ์เล็ก
doekman

3
SqlClientจะตระหนักถึงนี้ทำให้โค้ดคู่เกินไปที่จะ
Jaider

5

ฉันลองสิ่งนี้โดยใช้แลมบ์ดานิพจน์และใช้งานได้

List<MyList>.Any (x => (String.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase)) && (x.Type == qbType) );


18
นั่นเป็นเพราะคุณใช้List<>ซึ่งหมายความว่าการเปรียบเทียบที่จะเกิดขึ้นในหน่วยความจำ (รหัส C #) มากกว่าIQueryable(หรือObjectQuery) ซึ่งจะดำเนินการเปรียบเทียบในฐานข้อมูล
drzaus

1
สิ่งที่ @drzaus พูด คำตอบนี้ผิดปกติโดยพิจารณาว่าบริบทเป็น linq2sql ไม่ใช่ linq ปกติ
rsenna

0

หากคุณส่งสตริงที่ตรงตามตัวพิมพ์เล็กและใหญ่ลงใน LINQ-to-SQL มันจะถูกส่งผ่านไปยัง SQL ที่ไม่เปลี่ยนแปลงและการเปรียบเทียบจะเกิดขึ้นในฐานข้อมูล หากคุณต้องการทำการเปรียบเทียบสตริงที่ไม่ต้องคำนึงถึงตัวพิมพ์ใหญ่ - เล็กในฐานข้อมูลทั้งหมดที่คุณต้องทำคือสร้างนิพจน์แลมบ์ดาที่ทำการเปรียบเทียบและผู้ให้บริการ LINQ-to-SQL จะแปลนิพจน์นั้นเป็นแบบสอบถาม SQL ด้วยสตริงของคุณ

ตัวอย่างการสืบค้น LINQ นี้:

from user in Users
where user.Email == "foo@bar.com"
select user

รับการแปลเป็น SQL ต่อไปนี้โดยผู้ให้บริการ LINQ-to-SQL:

SELECT [t0].[Email]
FROM [User] AS [t0]
WHERE [t0].[Email] = @p0
-- note that "@p0" is defined as nvarchar(11)
-- and is passed my value of "foo@bar.com"

อย่างที่คุณเห็นพารามิเตอร์สตริงจะถูกเปรียบเทียบใน SQL ซึ่งหมายความว่าสิ่งต่าง ๆ ควรทำงานในแบบที่คุณคาดหวังไว้


ฉันไม่เข้าใจสิ่งที่คุณพูด 1) สตริงตัวเองต้องไม่ตรงตามตัวพิมพ์เล็กหรือใหญ่ - เล็กใน. NET ดังนั้นฉันจึงไม่สามารถผ่าน "สตริงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่" 2) การสืบค้น LINQ โดยทั่วไปคือการแสดงออกแลมบ์ดาและนั่นคือวิธีที่ฉันส่งผ่านสองสายของฉันดังนั้นนี่จึงไม่สมเหตุสมผลเลยสำหรับฉัน
BlueMonkMN

3
ฉันต้องการทำการเปรียบเทียบ CASE-INSENSITIVE บนฐานข้อมูล CASE-SENSITIVE
BlueMonkMN

คุณใช้ฐานข้อมูลแบบ CASE-SENSITIVE อะไร
Andrew Hare

นอกจากนี้แบบสอบถาม LINQ ไม่ใช่การแสดงออกแลมบ์ดา การสืบค้น LINQ ประกอบด้วยหลายส่วน (โดยเฉพาะอย่างยิ่งผู้ประกอบการสอบถามและการแสดงออกแลมบ์ดา)
Andrew Hare

คำตอบนี้ไม่สมเหตุสมผลตามความเห็นของ BlueMonkMN
Alf

0

ในการดำเนินการตามตัวพิมพ์เล็กและใหญ่ของ Linq ไปยัง Sql เคียวรีจะประกาศเขตข้อมูล 'string' ให้ตรงตามตัวพิมพ์ใหญ่และเล็กโดยการระบุชนิดข้อมูลเซิร์ฟเวอร์โดยใช้วิธีใดวิธีหนึ่งต่อไปนี้

varchar(4000) COLLATE SQL_Latin1_General_CP1_CS_AS 

หรือ

nvarchar(Max) COLLATE SQL_Latin1_General_CP1_CS_AS

หมายเหตุ: 'CS' ในประเภทการเปรียบเทียบข้างต้นหมายถึง 'Case Sensitive'

สามารถป้อนข้อมูลนี้ในฟิลด์“ ชนิดข้อมูลเซิร์ฟเวอร์” เมื่อดูคุณสมบัติโดยใช้ Visual Studio DBML Designer

สำหรับรายละเอียดเพิ่มเติมดูที่http://yourdotnetdesignteam.blogspot.com/2010/06/case-sensitive-linq-to-sql-queries.html


นั่นคือปัญหา โดยทั่วไปสนามที่ฉันใช้จะต้องตรงตามตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ (สูตรทางเคมี CO [คาร์บอนมอนอกไซด์] จะแตกต่างจากโคบอลต์] อย่างไรก็ตามในสถานการณ์ที่เฉพาะเจาะจง (ค้นหา) ฉันต้องการให้ co ตรงกันทั้ง Co และ CO การกำหนดคุณสมบัติเพิ่มเติมด้วย "ชนิดข้อมูลเซิร์ฟเวอร์" ที่แตกต่างกันนั้นไม่ถูกกฎหมาย (linq to sql อนุญาตเพียงหนึ่งคุณสมบัติต่อคอลัมน์ sql) ยังคงไม่ไป
doekman

นอกจากนี้หากทำการทดสอบหน่วยวิธีการนี้จะไม่เข้ากันได้กับการจำลองข้อมูล ดีที่สุดที่จะใช้วิธี linq / lambda ในคำตอบที่ได้รับการยอมรับ
ปั้นจั่น

0
where row.name.StartsWith(q, true, System.Globalization.CultureInfo.CurrentCulture)

1
อะไรคือข้อความ SQL ที่สิ่งนี้ได้รับการแปลและสิ่งที่ทำให้มันไม่ตรงตามตัวพิมพ์เล็กและใหญ่ในสภาพแวดล้อม SQL ที่จะถือว่าเป็นแบบตรงตามตัวพิมพ์ใหญ่ - เล็ก?
BlueMonkMN

0

วิธีสองขั้นตอนต่อไปนี้ใช้งานได้สำหรับฉัน (VS2010, ASP.NET MVC3, SQL Server 2008, Linq ถึง SQL):

result = entRepos.FindAllEntities()
    .Where(e => e.EntitySearchText.Contains(item));

if (caseSensitive)
{
    result = result
        .Where(e => e.EntitySearchText.IndexOf(item, System.StringComparison.CurrentCulture) >= 0);
}

1
รหัสนี้มีข้อผิดพลาดถ้าข้อความเริ่มต้นด้วยข้อความค้นหา (ควร> = 0)
Flatliner DOA

@ FlatlinerDOA จริง ๆ แล้วควรเป็น!= -1เพราะIndexOf "ส่งคืน -1 หากไม่พบอักขระหรือสตริง"
drzaus

0

บางครั้งค่าที่เก็บไว้ในฐานข้อมูลอาจมีช่องว่างดังนั้นการเรียกใช้อาจล้มเหลว

String.Equals(row.Name, "test", StringComparison.OrdinalIgnoreCase)

ทางออกสำหรับปัญหานี้คือการลบที่ว่างแล้วแปลงเคสแล้วเลือกดังนี้

 return db.UsersTBs.Where(x => x.title.ToString().ToLower().Replace(" ",string.Empty).Equals(customname.ToLower())).FirstOrDefault();

หมายเหตุในกรณีนี้

customnameคือค่าเพื่อให้ตรงกับค่าฐานข้อมูล

UsersTBsเป็นคลาส

titleคือคอลัมน์ฐานข้อมูล


-1

โปรดจำไว้ว่ามีความแตกต่างระหว่างว่าแบบสอบถามทำงานและทำงานอย่างมีประสิทธิภาพหรือไม่ ! คำสั่ง LINQ จะถูกแปลงเป็น T-SQL เมื่อเป้าหมายของคำสั่งคือ SQL Server ดังนั้นคุณต้องคิดถึง T-SQL ที่จะสร้างขึ้น

การใช้ String.Equals จะเป็นไปได้มากที่สุด (ฉันเดา) นำแถวทั้งหมดกลับมาจาก SQL Server แล้วทำการเปรียบเทียบใน. NET เพราะเป็นนิพจน์. NET ที่ไม่สามารถแปลเป็น T-SQL ได้

กล่าวอีกนัยหนึ่งการใช้นิพจน์จะเพิ่มการเข้าถึงข้อมูลของคุณและลบความสามารถในการใช้ดัชนี มันจะทำงานบนโต๊ะเล็ก ๆ และคุณจะไม่สังเกตเห็นความแตกต่าง บนโต๊ะขนาดใหญ่มันสามารถทำงานได้แย่มาก

นั่นเป็นหนึ่งในปัญหาที่เกิดขึ้นกับ LINQ; ผู้คนไม่คิดว่างบที่เขียนจะเป็นจริงได้อีกต่อไป

ในกรณีนี้ไม่มีวิธีการทำสิ่งที่คุณต้องการโดยไม่ต้องใช้นิพจน์ - แม้แต่ใน T-SQL ดังนั้นคุณอาจไม่สามารถทำสิ่งนี้ได้อย่างมีประสิทธิภาพมากขึ้น แม้แต่คำตอบของ T-SQL ที่ให้ไว้ข้างต้น (การใช้ตัวแปรที่มีการเรียงหน้า) จะส่งผลให้ดัชนีถูกละเว้น แต่ถ้าเป็นตารางขนาดใหญ่มันก็คุ้มค่าที่จะใช้คำสั่งและดูที่แผนปฏิบัติการเพื่อดูว่าดัชนีถูกใช้หรือไม่ .


2
ไม่เป็นความจริง (ไม่ทำให้แถวถูกส่งกลับไปยังไคลเอ็นต์) ฉันใช้ String.Equals และสาเหตุที่ใช้ไม่ได้เพราะได้รับการแปลงเป็นการเปรียบเทียบสตริง TSQL ซึ่งพฤติกรรมนั้นขึ้นอยู่กับการเปรียบเทียบของฐานข้อมูลหรือเซิร์ฟเวอร์ ฉันควรพิจารณาว่าทุก LINQ เป็นนิพจน์ SQL ที่ฉันเขียนจะถูกแปลงเป็น TSQL อย่างไร วิธีการในสิ่งที่ฉันต้องการคือการใช้ ToUpper เพื่อบังคับให้ TSQL ที่สร้างขึ้นเพื่อใช้บน จากนั้นการแปลงและการเปรียบเทียบทั้งหมดจะทำใน TSQL ดังนั้นคุณจะไม่เสียประสิทธิภาพมากนัก
BlueMonkMN
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.