การล่ม CLR บน SQL Server 2014 (windows 2012R2)


12

ฉันมี CLR ขนาดเล็กนี้ที่ทำหน้าที่ RegEX ในสตริงในคอลัมน์

เมื่อทำงานบน SQL Server 2014 (12.0.2000) บน Windows Server 2012R2 กระบวนการขัดข้อง

ข่าวสารเกี่ยวกับ 0, ระดับ 11, สถานะ 0, บรรทัด 0 มีข้อผิดพลาดร้ายแรงเกิดขึ้นกับคำสั่งปัจจุบัน ควรยกเลิกผลลัพธ์หากมี

และให้กองการถ่ายโอนข้อมูลถ้าฉันทำ

select count (*) from table where (CLRREGEX,'Regex')

แต่เมื่อฉันทำ

select * from table where (CLRREGEX,'Regex') 

มันจะส่งคืนแถว

ทำงานได้อย่างสมบูรณ์บน SQL Server รุ่นเดียวกับที่ทำงานบน Windows 8.1

ความคิดใด ๆ

- แก้ไขมันง่ายอย่างที่ควรเป็น

using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlTypes;           //SqlString, SqlInt32, SqlBoolean
using System.Text.RegularExpressions; //Match, Regex
using Microsoft.SqlServer.Server;     //SqlFunctionAttribute
public partial class UserDefinedFunctions
{
    public static readonly RegexOptions Options = RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline;
    [SqlFunction]
    [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true)]
    public static SqlBoolean RegExMatch(SqlString input, SqlString pattern)
    {
        if (input.IsNull || pattern.IsNull) //nulls dont qualify for a match
            return SqlBoolean.False;
    return Regex.IsMatch(input.Value, pattern.Value, RegexOptions.IgnoreCase);
    }
}

ดังนั้นโดยการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ งานนี้ตอนนี้: บทเรียนหลักใน C # ดูเหมือนว่าจะเหมือนกับใน TSQL ระวังการแปลงข้อมูลโดยนัย

using System;
using System.Text;
using System.Data.SqlTypes;           //SqlString, SqlInt32, SqlBoolean
using System.Text.RegularExpressions; //Match, Regex
using Microsoft.SqlServer.Server;     //SqlFunctionAttribute
public partial class UserDefinedFunctions
{
public static readonly RegexOptions Options = RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;

    [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true, DataAccess = DataAccessKind.Read)]
    public static SqlBoolean RegExMatch(SqlString input, SqlString pattern)
{
    if (input.IsNull || pattern.IsNull) //nulls dont qualify for a match
        return SqlBoolean.False;
    string sqldata = input.ToString();
    string regex = pattern.ToString();
    return Regex.IsMatch(sqldata, regex);
 }

สิ่งนี้เกิดขึ้นกับทุกรูปแบบหรือแค่อันนี้หรือไม่? มันอาจเป็นรูปแบบที่ไม่มีประสิทธิภาพ (เช่นการย้อนรอยมากเกินไปหรือการจับภาพที่ไม่จำเป็น) คุณควรตรวจสอบการตั้งค่าคุณสมบัติMatchTimeout (ใหม่ใน. NET Framework 4.5) คุณเขียนโค้ดฟังก์ชัน RegEx ด้วยตัวเองหรือไม่ ถ้าเป็นเช่นนั้นคุณกำลังใช้วิธีการแบบคงที่หรืออินสแตนซ์ RegEx? เป็นSqlFunctionวิธีการทำเครื่องหมายว่าIsDeterministic=true? ชุดประกอบทำเครื่องหมายว่าเป็นSAFE?
โซโลมอน Rutzky

2
ตารางเหล่านี้ใหญ่แค่ไหน นอกจากนี้คุณสามารถตรวจสอบได้ว่าแผนโดยประมาณสำหรับคำแถลงปัญหามีตัวดำเนินการขนานหรือไม่ ถ้าใช่คุณสามารถตรวจสอบว่าปัญหาเกิดขึ้นโดยไม่มีการขนานกันหรือไม่เช่นมีคำใบ้ MAXDOP = 1
Amit Banerjee

2
รหัสดูดียกเว้น[SqlFunction]แอตทริบิวต์ซ้ำกัน นั่นคือรหัสที่แน่นอน? ฉันไม่คิดว่าจะรวบรวม Framework รุ่น 2.0 / 3.0 / 3.5 มีความแตกต่างไม่ใช่ประเด็นตามที่คุณใช้ 4.0 / 4.5 / 4.5.x / etc หรือสิ่งที่อยู่บนเซิร์ฟเวอร์นั้นเนื่องจากคุณอยู่ใน SQL Server 2014 ซึ่งถูกผูกไว้กับรุ่น CLR 4 เซิร์ฟเวอร์แสดงปัญหา 32 บิตหรือไม่ มันมีหน่วยความจำเท่าไหร่เมื่อเทียบกับเซิร์ฟเวอร์อื่น ๆ ? และคุณได้ตรวจสอบบันทึก SQL Server หลังจากได้รับข้อผิดพลาดนั้นหรือไม่?
โซโลมอน Rutzky

2
. NET รุ่นที่แน่นอนไม่เกี่ยวข้องกับปัญหาแม้ว่ามันจะเป็นการดีที่จะรู้ว่าเซิร์ฟเวอร์ทั้งหมดอยู่ในอย่างน้อย 4.5 เพราะนั่นหมายความว่าคุณสามารถใช้MatchTimeoutคุณสมบัติใหม่ได้ แต่ฉันไม่คิดว่าเป็นปัญหาจริง ๆ ถ้าคุณผ่านเพียง 5 ตัวอักษรสูงสุด มันเป็นไปได้ว่าเครื่องนี้คนมีความเสียหายติดตั้งของ .NET Framework และที่สามารถได้รับการซ่อมแซมครั้งหนึ่งกิจกรรมการประมงปลาเทราท์ได้หยุด ;-) นอกจากนี้ยัง[0-9].*ง่าย แต่ไม่มีประสิทธิภาพเนื่องจากตรงกับตัวอักษรทั้งหมดหากมีหลังจากตัวเลขตัวแรก ใช้เพียง[0-9]เพื่อIsMatchจะดีกว่า
โซโลมอน Rutzky

1
ทำไมคุณDataAccessKindถึงเปลี่ยนไปRead? นั่นทำให้ช้าลงและคุณไม่ได้เข้าถึงข้อมูลใด ๆ นอกจากนี้ฉันรู้ว่าตอนนี้ดูเหมือนว่าจะทำงานได้ แต่ฉันจะต้องระมัดระวังในการใช้ToString()วิธีการที่ตรงข้ามกับValueสถานที่ให้บริการที่ฉันไม่คิดว่า ToString จัดการการเข้ารหัสอย่างถูกต้องหรือสิ่งที่ชอบ การเปรียบเทียบฐานข้อมูลของคุณตั้งไว้เป็นเท่าไหร่? แน่นอนฉันเพิ่งอ่านความคิดเห็นของคุณข้างบนอีกครั้งและดูว่าคอลัมน์นั้นเป็น VARCHAR แทนที่จะเป็น NVARCHAR ฟิลด์นั้นมีการเปรียบเทียบที่แตกต่างจากฐานข้อมูลหรือไม่
โซโลมอน Rutzky

คำตอบ:


4

ปัญหาคือความขัดแย้งของโลแคลระหว่าง Windows OS และ SQL Server (โดยเฉพาะฐานข้อมูลที่โหลดแอสเซมบลี) คุณสามารถเรียกใช้แบบสอบถามต่อไปนี้เพื่อดูว่าทั้งคู่ตั้งค่าเป็น:

SELECT os_language_version,
       DATABASEPROPERTYEX(N'{name of DB where Assembly exists}', 'LCID') AS 'DatabaseLCID'
FROM   sys.dm_os_windows_info;

หากพวกเขาแตกต่างกันคุณจะได้รับพฤติกรรม "แปลก" อย่างแน่นอนเช่นสิ่งที่คุณเห็น ปัญหาคือ:

  • SqlStringรวมมากกว่าตัวหนังสือเอง: รวมการเรียงหน้าเริ่มต้นของฐานข้อมูลที่มีแอสเซมบลีอยู่ การเรียงประกอบด้วยข้อมูลสองส่วน: ข้อมูลโลแคล (เช่น LCID) และตัวเลือกการเปรียบเทียบ (เช่น SqlCompareOptions) ซึ่งให้รายละเอียดความไวต่อตัวพิมพ์เล็ก, สำเนียง, kana, ความกว้างหรือทุกอย่าง (ไบนารีและไบนารี 2)
  • การดำเนินงานสตริงใน. NET เว้นแต่ว่าจะได้รับสถานที่อย่างชัดเจนให้ใช้ข้อมูลสถานที่ของหัวข้อปัจจุบันซึ่งตั้งอยู่ใน Windows (เช่นระบบปฏิบัติการ / ระบบปฏิบัติการ)

ความขัดแย้งที่มักจะเกิดขึ้นเมื่ออ้างอิงพารามิเตอร์ SqlString โดยไม่ต้องใช้.Valueหรือดังกล่าวว่ามันไม่แปลงนัยไป.ToString() SqlStringในกรณีนั้นมันจะทำให้เกิดข้อยกเว้นที่บอกว่า LCID ไม่ตรงกัน

เห็นได้ชัดว่ามีสถานการณ์อื่น ๆ เช่นการเปรียบเทียบสตริง (บางส่วน / ทั้งหมด) รวมถึงเมื่อใช้ Regex เนื่องจากกรณีนี้แสดงให้เห็น (แม้ว่าจนถึงตอนนี้ฉันยังไม่สามารถทำซ้ำได้)

แนวคิดบางประการสำหรับการแก้ไข:

อุดมคติ (ความคาดหวังจะได้พบเสมอเกี่ยวกับการเปรียบเทียบ)

  • เปลี่ยน Windows หรือ SQL Server LCID (ภาษาเริ่มต้น) เพื่อให้ตรงกันทั้งคู่

น้อยกว่าอุดมคติ (พฤติกรรมของโลแคล Windows อาจไม่เหมือนกันกฎสำหรับความเท่าเทียมกันและการเรียงลำดับและอาจมีผลลัพธ์ที่ไม่คาดคิด):

  • ใช้.ToStringวิธีการหรือ.Valueคุณสมบัติซึ่งทั้งสองคืนสตริงโดยไม่มี SQL Server LCID ดังนั้นการดำเนินการทั้งหมดจะใช้ OS LCID

อาจช่วย:

  • อาจใช้SqlCharsแทนSqlStringเนื่องจากไม่ได้นำข้อมูล LCID และการเปรียบเทียบจาก SQL Server
  • ระบุว่าวัฒนธรรมไม่สำคัญผ่านStringComparison.InvariantCulture:
    • String.Compare(string, string, StringComparison.InvariantCulture) หรือ String.Compare(string, string, StringComparison.InvariantCultureIgnoreCase)
    • สำหรับ Regex ให้ระบุ RegexOptions.CultureInvariant

1

Updated ..

การโลคัลไลซ์เซชันแตกต่างระหว่าง SQL Engine และหน้าต่างเซิร์ฟเวอร์ตามที่ @srutzky ชี้ให้เห็น:

os_language_version SqlServerLCID
1033 1039

การเปลี่ยนแปลงรหัสต่อไปนี้ - การตั้งค่าตัวเลือกRegexOptions.CultureInvariantทำให้เกิดข้อผิดพลาด รหัสที่ไม่เปลี่ยนแปลงจะไม่เกิดความผิดพลาด SQL Server 2012 บน Windows Server 2012R2 ด้วยการตั้งค่าภาษาเดียวกัน แต่จะทำเช่นนั้นใน SQL Server 2014

using System;
using System.Text;
using System.Data.SqlTypes;           //SqlString, SqlInt32, SqlBoolean
using System.Text.RegularExpressions; //Match, Regex
using Microsoft.SqlServer.Server;     //SqlFunctionAttribute
public partial class UserDefinedFunctions
{
public static readonly RegexOptions Options = RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;

    [Microsoft.SqlServer.Server.SqlFunction(IsDeterministic = true, IsPrecise = true)]
    public static SqlBoolean RegExMatch(SqlString input, SqlString pattern)
{
    if (input.IsNull || pattern.IsNull) //nulls dont qualify for a match
        return SqlBoolean.False;
    string sqldata = input.ToString();
    string regex = pattern.ToString();
    return Regex.IsMatch(sqldata, regex);
 }

คุณช่วยกรุณาเรียกใช้สิ่งต่อไปนี้บนเซิร์ฟเวอร์ที่ขัดข้อง: SELECT os_language_version, SERVERPROPERTY('LCID') AS 'SqlServerLCID' FROM sys.dm_os_windows_info;. อาจเป็นไปได้ว่าปัญหานี้เกิดจากความขัดแย้งในการตั้งค่าภาษา วิธีการแก้ปัญหาของคุณยังคงอาจจะเป็นวิธีที่ดีที่สุดที่จะไป แต่โดยทั่วไปไม่ควรมีความต้องการที่จะใช้ToString()แทนของValueทรัพย์สินในSqlStrings ดังนั้นจึงเป็นการดีที่จะยืนยันสถานการณ์
โซโลมอน Rutzky

ผมโพสต์คำตอบที่จะชี้แจง แต่ปัญหาไม่ควรได้รับการแก้ไขโดยการตั้งค่าRegexOptions.CultureInvariantตั้งแต่คุณไม่ผ่านตัวแปรเข้าOptions Regex.IsMatch(sqldata, regex)สิ่งที่เปลี่ยนแปลงระหว่างรหัสเดิมของคุณและใหม่รหัสการทำงานเป็นคุณไปจากการใช้เพื่อSqlString.Value ฉันสงสัยว่าคุณจะได้เห็นพฤติกรรมคงเดียวกันถ้าคุณเปลี่ยนไปใช้SqlString.ToString() SqlCharsแต่ฉันจะทำแบบทดสอบ วิธีที่ดีที่สุดคือเปลี่ยน LCID ของ Windows หรือ SQL Server ให้ตรงกัน คุณยังสามารถลบตัวแปรสแตติกตัวเลือกได้
โซโลมอน Rutzky

สวัสดี. ขอบคุณที่ยอมรับคำตอบของฉัน :) เพิ่งพูดถึงฉันได้ทำการวิจัยเพิ่มเติมและถ้าเข้าใจสิ่งที่ฉันเห็นในขณะที่ฉันถูกต้องเกี่ยวกับสาเหตุที่ทำให้เกิด LCID ที่แตกต่างกันระหว่างระบบปฏิบัติการและ SQL Server มันไม่ได้หรือไม่ควรเกี่ยวข้องกับ.Valueทรัพย์สิน ของ a SqlStringที่เห็นได้ชัดว่าส่งกลับค่าภายในเช่นเดียวกับ.ToString()วิธีการ ฉันยังคงตรวจสอบและจะอัปเดตคำตอบของฉันกับสิ่งที่ฉันพบ :)
โซโลมอน Rutzky

ฉันปรับคำตอบในแง่ของข้อมูลใหม่ ฉันไม่สามารถจำลองสถานการณ์นี้ได้ รหัสในคำถามเป็นสิ่งที่คุณใช้จริงหรือ? ข้อแตกต่างที่แท้จริงระหว่างพวกเขาคือข้อผิดพลาดที่ใช้RegexOptions.IgnoreCaseในขณะที่ข้อผิดพลาดไม่ได้ทำ ฉันได้ตั้งค่าสภาพแวดล้อมที่คล้ายกัน: Windows (8.0) โดยใช้ LCID ที่ 1033, SQL Server DB มี LCID ที่ 1039 โดยใช้ RegEx เดียวกันกับที่คุณโพสต์ทำCOUNT(*)บนVARCHARฟิลด์ที่เต็มไปด้วย GUID โดยใช้รูปแบบของ'[0-3â].*'บนโต๊ะ กับ 10 ล้านแถว มันคือ SQL Server 2012 ไม่ใช่ปี 2014 แต่ฉันไม่คิดว่ามันจะสำคัญ
โซโลมอน Rutzky

1
ขอบคุณสำหรับคำตอบทั้งหมด รหัสในคำถามคือสิ่งที่ฉันใช้ ฉันมี regex ที่ซับซ้อนมาก ๆ แต่ก็จัดการมันล้มเหลวด้วยการใช้ง่ายๆ การเปลี่ยนการตั้งค่า RegexOptions.CultureInvariant หยุดการทำงาน
Spörri
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.