Aracılığıyla paylaş


SQL-CLR Tür Uyumsuzlukları

LINQ to SQL, nesne modeli ile SQL Server arasındaki çevirinin büyük bölümünü otomatikleştirir. Bununla birlikte, bazı durumlar tam çeviriyi engeller. Ortak dil çalışma zamanı (CLR) türleri ile SQL Server veritabanı türleri arasındaki bu anahtar uyuşmazlıkları aşağıdaki bölümlerde özetlenmiştir. Sql-CLR Tür Eşlemesi ve Veri Türleri ve İşlevleri'nde belirli tür eşlemeleri ve işlev çevirisi hakkında daha fazla ayrıntı bulabilirsiniz.

Veri Türleri

CLR ile SQL Server arasındaki çeviri, veritabanına bir sorgu gönderildiğinde ve sonuçlar nesne modelinize geri gönderildiğinde gerçekleşir. Örneğin, aşağıdaki Transact-SQL sorgusu iki değer dönüştürmesi gerektirir:

Select DateOfBirth From Customer Where CustomerId = @id

Sorgunun SQL Server'da yürütülebilmesi için transact-SQL parametresinin değeri belirtilmelidir. Bu örnekte, veritabanının id değerin ne olduğunu anlayabilmesi için parametre değerinin önce CLR System.Int32 türünden SQL Server INT türüne çevrilmesi gerekir. Ardından sonuçları almak için SQL Server DateOfBirth sütununun nesne modelinde kullanılmak üzere SQL Server DATETIME türünden CLR System.DateTime türüne çevrilmesi gerekir. Bu örnekte, CLR nesne modelindeki ve SQL Server veritabanındaki türlerin doğal eşlemeleri vardır. Ama her zaman böyle değildir.

Eksik Karşılıklar

Aşağıdaki türlerin makul karşılıkları yoktur.

  • CLR System ad alanında uyuşmazlıklar:

    • İşaretsiz tamsayılar. Bu türler genellikle taşmasını önlemek için daha büyük boyutlu imzalı karşılıklarıyla eşlenir. Değişmez değerler, değere göre aynı veya daha küçük boyutta imzalı bir sayısala dönüştürülebilir.

    • Boole dili. Bu türler bir bit veya daha büyük bir sayısal veya dizeyle eşlenebilir. Değişmez değer, aynı değere (örneğin, 1=1 CLS için SQL'de) değerlendirilen bir ifadeye True eşlenebilir.

    • Zaman Aralığı. Bu tür iki DateTime değer arasındaki farkı temsil eder ve SQL Server'a timestamp karşılık gelmez. CLR System.TimeSpan , bazı durumlarda SQL Server TIME türüyle de eşlenebilir. SQL Server TIME türü yalnızca 24 saatten kısa pozitif değerleri temsil etmek için tasarlanmıştır. CLR'nin TimeSpan çok daha geniş bir aralığı vardır.

    Not

    içindeki SQL Server'a özgü .NET Framework türleri System.Data.SqlTypes bu karşılaştırmaya dahil değildir.

  • SQL Server'da uyuşmazlıklar:

    • Sabit uzunluklu karakter türleri. Transact-SQL, Unicode ve Unicode olmayan kategorileri birbirinden ayırır ve her kategoride üç ayrı türe sahiptir: sabit uzunluk nchar/char, değişken uzunluğuvarchar/nvarchar ve daha büyük boyutlu.ntext/text Sabit uzunluklu karakter türleri, karakterleri almak için CLR System.Char türüne eşlenebilir, ancak dönüştürmelerde ve davranışlarda gerçekten aynı türe karşılık değildir.

    • Bit' i seçin. Etki alanı ile aynı sayıda değere Nullable<Boolean>sahip olsa bit da, ikisi farklı türlerdedir. Bit, yerine/truefalse ve değerlerini 01 alır ve Boole ifadelerine eşdeğer olarak kullanılamaz.

    • Zaman damgası. CLR System.TimeSpan türünden farklı olarak, SQL Server TIMESTAMP türü veritabanı tarafından oluşturulan ve her güncelleştirme için benzersiz olan ve değerler arasındaki DateTime farka bağlı olmayan 8 baytlık bir sayıyı temsil eder.

    • Para ve SmallMoney. Bu türler ile eşlenebilir Decimal , ancak temelde farklı türlerdir ve sunucu tabanlı işlevler ve dönüştürmeler tarafından bu şekilde ele alınmaktadır.

Birden Çok Eşleme

Bir veya daha fazla CLR veri türüyle eşleyebileceğiniz birçok SQL Server veri türü vardır. Bir veya daha fazla SQL Server türüne eşleyebileceğiniz birçok CLR türü de vardır. Eşleme LINQ ile SQL arasında desteklense de, CLR ile SQL Server arasında eşlenen iki türün duyarlık, aralık ve semantik açısından mükemmel bir eşleşme olduğu anlamına gelmez. Bazı eşlemeler bu boyutların herhangi birinde veya tamamında farklılıklar içerebilir. Çeşitli eşleme olasılıkları için bu olası farklılıklar hakkındaki ayrıntıları SQL-CLR Tür Eşlemesi'nde bulabilirsiniz.

Kullanıcı Tanımlı Türler

Kullanıcı tanımlı CLR türleri, tür sistemi boşluğunun kapatılmasına yardımcı olmak için tasarlanmıştır. Bununla birlikte, tür sürümü oluşturma ile ilgili ilginç sorunları ortaya çıkarıyorlar. İstemcideki sürümdeki bir değişiklik, veritabanı sunucusunda depolanan türdeki bir değişiklikle eşleşmeyebilir. Bu tür bir değişiklik, tür semantiğinin eşleşmeyebileceği ve sürüm boşluğunun görünür olma olasılığının yüksek olduğu başka bir tür uyuşmazlığına neden olur. Devralma hiyerarşileri ardışık sürümlerde yeniden düzenlendikçe daha fazla komplikasyon oluşur.

İfade Semantiği

CLR ve veritabanı türleri arasındaki çift tabanlı uyuşmazlıklara ek olarak, ifadeler uyuşmazlık için karmaşıklık ekler. İşleç semantiği, işlev semantiği, örtük tür dönüştürme ve öncelik kurallarındaki uyuşmazlıklar dikkate alınmalıdır.

Aşağıdaki alt bölümler, görünüşte benzer ifadeler arasındaki uyuşmazlığı gösterir. Belirli bir CLR ifadesine benzer sql ifadeleri oluşturmak mümkün olabilir. Ancak, benzer ifadeler arasındaki semantik farklılıkların bir CLR kullanıcısı için belirgin olup olmadığı ve bu nedenle anlamsal denklik için gereken değişikliklerin amaçlanıp amaçlanmadığı net değildir. Bir ifade bir değer kümesi için değerlendirildiğinde bu özellikle kritik bir sorundur. Farkın görünürlüğü verilere bağlı olabilir ve kodlama ve hata ayıklama sırasında tanımlanması zor olabilir.

Null Semantikler

SQL ifadeleri Boole ifadeleri için üç değerli mantık sağlar. Sonuç true, false veya null olabilir. Buna karşılık CLR, null değerler içeren karşılaştırmalar için iki değerli Boole sonucunu belirtir. Aşağıdaki kodu inceleyin:

Nullable<int> i = null;
Nullable<int> j = null;
if (i == j)
{
    // This branch is executed.
}
Dim i? As Integer = Nothing
Dim j? As Integer = Nothing
If i = j Then
    '  This branch is executed.
End If
-- Assume col1 and col2 are integer columns with null values.
-- Assume that ANSI null behavior has not been explicitly
--  turned off.
Select …
From …
Where col1 = col2
-- Evaluates to null, not true and the corresponding row is not
--   selected.
-- To obtain matching behavior (i -> col1, j -> col2) change
--   the query to the following:
Select …
From …
Where
    col1 = col2
or (col1 is null and col2 is null)
-- (Visual Basic 'Nothing'.)

İki değerli sonuçlarla ilgili varsayımda benzer bir sorun oluşur.

if ((i == j) || (i != j)) // Redundant condition.
{
    // ...
}
If (i = j) Or (i <> j) Then ' Redundant condition.
    ' ...
End If
-- Assume col1 and col2 are nullable columns.
-- Assume that ANSI null behavior has not been explicitly
--   turned off.
Select …
From …
Where
    col1 = col2
or col1 != col2
-- Visual Basic: col1 <> col2.

-- Excludes the case where the boolean expression evaluates
--   to null. Therefore the where clause does not always
--   evaluate to true.

Önceki örnekte SQL oluştururken eşdeğer davranışlar elde edebilirsiniz, ancak çeviri amacınızı doğru yansıtmayabilir.

LINQ to SQL, SQL üzerinde C# null veya Visual Basic nothing karşılaştırma semantiği uygulamaz. Karşılaştırma işleçleri, sql eşdeğerlerine benzer şekilde çevrilir. Semantik, sunucu veya bağlantı ayarları tarafından tanımlanan SQL semantiğini yansıtır. varsayılan SQL Server ayarları altında iki null değer eşit değil olarak kabul edilir (ancak semantiği değiştirmek için ayarları değiştirebilirsiniz). Ne olursa olsun, LINQ to SQL sorgu çevirisindeki sunucu ayarlarını dikkate almaz.

Değişmez null değer (nothing) ile karşılaştırma, uygun SQL sürümüne (is null veya is not null) çevrilir.

Harmanlamadaki (nothing) değeri null SQL Server tarafından tanımlanır; LINQ to SQL harmanlamayı değiştirmez.

Tür Dönüştürme ve Yükseltme

SQL, ifadelerde zengin bir örtük dönüştürme kümesini destekler. C# dilindeki benzer ifadeler açık bir atama gerektirir. Örneğin:

  • Nvarchar ve DateTime türleri SQL'de herhangi bir açık atama olmadan karşılaştırılabilir; C# için açık dönüştürme gerekir.

  • Decimal sql'de örtük olarak öğesine DateTime dönüştürülür. C# örtük dönüştürmeye izin vermez.

Benzer şekilde, Transact-SQL'deki tür önceliği, temel alınan tür kümesi farklı olduğundan C# dilinde tür önceliği farklıdır. Aslında, öncelik listeleri arasında net bir alt küme/üst küme ilişkisi yoktur. Örneğin, ile nvarcharvarchar karşılaştırmak ifadenin örtük olarak olarak dönüştürülmesine varcharnvarcharneden olur. CLR eşdeğer yükseltme sağlamaz.

Basit durumlarda bu farklılıklar, atamaları olan CLR ifadelerinin ilgili SQL ifadesi için yedekli olmasına neden olur. Daha da önemlisi, BIR SQL ifadesinin ara sonuçları örtük olarak C# dilinde doğru karşılığı olmayan bir türe yükseltilebilir ve tam tersi de geçerlidir. Genel olarak, bu tür ifadelerin test edilmesi, hata ayıklaması ve doğrulanması kullanıcıya önemli bir yük getirir.

Harmanlama

Transact-SQL, karakter dizesi türlerine ek açıklama olarak açık harmanlamaları destekler. Bu harmanlamalar belirli karşılaştırmaların geçerliliğini belirler. Örneğin, iki sütunu farklı açık harmanlamalarla karşılaştırmak bir hatadır. Çok basitleştirilmiş CTS dize türünün kullanılması bu tür hatalara neden olmaz. Aşağıdaki örneği inceleyin:

create table T2 (
    Col1 nvarchar(10),
    Col2      nvarchar(10) collate Latin_general_ci_as
)
class C
{
string s1;       // Map to T2.Col1.
string s2;       // Map to T2.Col2.

    void Compare()
    {
        if (s1 == s2) // This is correct.
        {
            // ...
        }
    }
}
Class C
    Dim s1 As String    ' Map to T2.Col1.
    Dim s2 As String    ' Map to T2.Col2.
    Sub Compare()
        If s1 = s2 Then ' This is correct.
            ' ...
        End If
    End Sub
End Class
Select …
From …
Where Col1 = Col2
-- Error, collation conflict.

Sonuç olarak, harmanlama alt türü, altstitutable olmayan kısıtlanmış bir tür oluşturur.

Benzer şekilde, sıralama düzeni tür sistemleri arasında önemli ölçüde farklı olabilir. Bu fark, sonuçların sıralamasını etkiler. Guid 16 baytın tümüne sözlük düzenine (IComparable() ) göre sıralanırken, T-SQL GUID'leri şu sırayla karşılaştırır: node(10-15), clock-seq(8-9), time-high(6-7), time-mid(4-5), time-low(0-3). Bu sıralama, NT tarafından oluşturulan GUID'lerin böyle bir sekizli sırası olduğunda SQL 7.0'da yapıldı. Yaklaşım, aynı düğüm kümesinde oluşturulan GUID'lerin zaman damgasına göre sıralı sırada bir araya geldiğinden emin oldu. Yaklaşım dizinler oluşturmak için de yararlı oldu (eklemeler rastgele GÇ'ler yerine eklemelere dönüşür). Gizlilik endişeleri nedeniyle sipariş daha sonra Windows'ta karışıktı, ancak SQL'in uyumluluğu koruması gerekiyor. Geçici bir çözüm yerine kullanmaktır SqlGuidGuid.

İşleç ve İşlev Farklılıkları

Temelde karşılaştırılabilir işleçler ve işlevler birbirinden çok farklı semantiklere sahiptir. Örneğin:

  • C# ve ||mantıksal işleçler && için işlenenlerin sözcük sırasına göre kısa devre semantiğini belirtir. Sql ise set tabanlı sorgular için hedeflenmiştir ve bu nedenle iyileştiricinin yürütme sırasına karar vermesi için daha fazla özgürlük sağlar. Etkilerinden bazıları şunlardır:

    • Eşanlamlı olarak eşdeğer çeviri " .CASE .. WHEN ... THEN" işlenen yürütmenin yeniden sıralanmasından kaçınmak için SQL'de yapısı.

    • C# ifadesi, ikinci işlenenin ilk işlenenin değerlendirmesinin sonucuna göre değerlendirilmeye dayanırsa, işleçlere yönelik gevşek bir çeviri AND/OR beklenmeyen hatalara neden olabilir.

  • Round() işlevi, .NET Framework ve T-SQL'de farklı semantiklere sahiptir.

  • Dizeler için başlangıç dizini CLR'de 0, SQL'de 1'dir. Bu nedenle, dizine sahip herhangi bir işlev için dizin çevirisi gerekir.

  • CLR kayan nokta sayıları için modül ('%') işlecini destekler ancak SQL desteklemez.

  • işleci Like örtük dönüştürmelere göre otomatik aşırı yüklemeleri etkili bir şekilde alır. İşleç Like , karakter dizesi türleri üzerinde çalışacak şekilde tanımlanmış olsa da, sayısal türlerden veya DateTime türlerden örtük dönüştürme, bu dize olmayan türlerin de birlikte Like kullanılmasına olanak tanır. CTS'de, benzer örtük dönüştürmeler yoktur. Bu nedenle, ek aşırı yüklemeler gereklidir.

    Not

    Bu Like işleç davranışı yalnızca C# için geçerlidir; Visual Basic Like anahtar sözcüğü değişmemiştir.

  • Taşma her zaman SQL'de denetlenür, ancak kaydırmayı önlemek için C# dilinde (Visual Basic'te değil) açıkça belirtilmesi gerekir. C1+C2 C3'te depolanıyorsa verilen tamsayı sütunları C1, C2 ve C3 (Güncelleştirme T Kümesi C3 = C1 + C2).

    create table T3 (
        Col1      integer,
        Col2      integer
    )
    insert into T3 (col1, col2) values (2147483647, 5)
    -- Valid values: max integer value and 5.
    select * from T3 where col1 + col2 < 0
    -- Produces arithmetic overflow error.
    
// C# overflow in absence of explicit checks.
int i = Int32.MaxValue;
int j = 5;
if (i+j < 0) Console.WriteLine("Overflow!");
// This code prints the overflow message.
' Does not apply.
' Visual Basic overflow in absence of implicit check
' (turn off overflow checks in compiler options)
Dim I As Integer = Int32.MaxValue
Dim j As Integer = 5
If I + j < 0 Then
    ' This code prints the overflow message.
    Console.WriteLine("Overflow!")
End If
  • .NET Framework bankacı yuvarlama kullanırken SQL simetrik aritmetik yuvarlama gerçekleştirir. Ek ayrıntılar için bilgi bankası makalesi 196652 bakın.

  • Varsayılan olarak, yaygın yerel ayarlar için karakter dizesi karşılaştırmaları SQL'de büyük/küçük harfe duyarlı değildir. Visual Basic'te ve C# dilinde büyük/küçük harfe duyarlıdır. Örneğin, s == "Food" (s = "Food" Visual Basic'te) ve s == "Food" ise sfoodfarklı sonuçlar verebilir.

    -- Assume default US-English locale (case insensitive).
    create table T4 (
        Col1      nvarchar (256)
    )
    insert into T4 values ('Food')
    insert into T4 values ('FOOD')
    select * from T4 where Col1 = 'food'
    -- Both the rows are returned because of case-insensitive matching.
    
// C# equivalent on collections of Strings in place of nvarchars.
String[] strings = { "food", "FOOD" };
foreach (String s in strings)
{
    if (s == "food")
    {
        Console.WriteLine(s);
    }
}
// Only "food" is returned.
' Visual Basic equivalent on collections of Strings in place of
' nvarchars.
Dim strings() As String = {"food", "FOOD"}
For Each s As String In strings
    If s = "food" Then
        Console.WriteLine(s)
    End If
Next
' Only "food" is returned.
  • SQL'de sabit uzunlukta karakter türü bağımsız değişkenlerine uygulanan işleçler/işlevler, CLR'ye System.Stringuygulanan işleçlerden/işlevlerden önemli ölçüde farklı semantiklere sahiptir. Bu, türler hakkında bölümünde ele alınan eksik karşılık gelen sorunun uzantısı olarak da görüntülenebilir.

    create table T4 (
        Col1      nchar(4)
    )
    Insert into T5(Col1) values ('21');
    Insert into T5(Col1) values ('1021');
    Select * from T5 where Col1 like '%1'
    -- Only the second row with Col1 = '1021' is returned.
    -- Not the first row!
    
    // Assume Like(String, String) method.
    string s = ""; // map to T4.Col1
    if (System.Data.Linq.SqlClient.SqlMethods.Like(s, "%1"))
    {
        Console.WriteLine(s);
    }
    // Expected to return true for both "21" and "1021"
    
    ' Assume Like(String, String) method.
    Dim s As String    ' Map to T4.Col1.
    If s Like (System.Data.Linq.SqlClient.SqlMethods.Like(s, "%1")) Then
        Console.WriteLine(s)
    End If
    ' Expected to return true for both "21" and "1021".
    

    Dize birleştirmede de benzer bir sorun oluşur.

    create table T6 (
        Col1      nchar(4)
        Col2       nchar(4)
    )
    Insert into T6 values ('a', 'b');
    Select Col1+Col2 from T6
    -- Returns concatenation of padded strings "a   b   " and not "ab".
    

Özetle, CLR ifadeleri için birleştirilmiş çeviri gerekebilir ve SQL işlevselliğini ortaya çıkarmak için ek işleçler/işlevler gerekebilir.

Tür Atama

C# ve SQL'de kullanıcılar, açık tür atamalarını (Cast ve Convert) kullanarak ifadelerin varsayılan semantiğini geçersiz kılabilir. Ancak, bu özelliğin tür sistemi sınırında açığa çıkması ikilem oluşturur. İstenen semantiği sağlayan bir SQL ataması, karşılık gelen bir C# atamasına kolayca çevrilemez. Öte yandan, tür uyuşmazlıkları, eksik eşdeğerleri ve farklı tür öncelik hiyerarşileri nedeniyle C# ataması doğrudan eşdeğer bir SQL atamasına çevrilemez. Tür sistemi uyuşmazlığı ortaya çıkarmak ve ifadenin önemli bir gücünü kaybetmek arasında bir denge vardır.

Diğer durumlarda, bir ifadenin doğrulanması için her iki etki alanında da tür ataması gerekli olmayabilir, ancak ifadeye varsayılan olmayan bir eşlemenin doğru uygulandığından emin olmak için gerekli olabilir.

-- Example from "Non-default Mapping" section extended
create table T5 (
    Col1      nvarchar(10),
    Col2      nvarchar(10)
)
Insert into T5(col1, col2) values ('3', '2');
class C
{
    int x;        // Map to T5.Col1.
    int y;        // Map to T5.Col2.

    void Casting()
    {
        // Intended predicate.
        if (x + y > 4)
        {
            // valid for the data above
        }
    }
}
Class C
    Dim x As Integer        ' Map to T5.Col1.
    Dim y As Integer        ' Map to T5.Col2.

    Sub Casting()
        ' Intended predicate.
        If (x + y) > 4 Then
            ' Valid for the data above.
        End If
    End Sub
End Class
Select *
From T5
Where Col1 + Col2 > 4
-- "Col1 + Col2" expr evaluates to '32'

Performans Sorunları

Bazı SQL Server-CLR tür farklarının hesaplandığında, CLR ve SQL Server tür sistemleri arasında geçiş yaparken performansta düşüş olabilir. Performansı etkileyen senaryolara örnek olarak şunlar verilebilir:

  • Mantıksal ve/veya işleçler için zorunlu değerlendirme sırası

  • Koşul değerlendirmesi sırasını zorlamak için SQL oluşturmak, SQL iyileştiricisinin yeteneğini kısıtlar.

  • İster CLR derleyicisi ister Nesne-İlişkisel sorgu uygulaması tarafından sunulan tür dönüştürmeleri dizin kullanımını kısıtlar.

    Örneğin,

    -- Table DDL
    create table T5 (
        Col1      varchar(100)
    )
    
    class C5
    {
        string s;        // Map to T5.Col1.
    }
    
    Class C5
        Dim s As String ' Map to T5.Col1.
    End Class
    

    ifadesinin (s = SOME_STRING_CONSTANT)çevirisini düşünün.

    -- Corresponding part of SQL where clause
    Where …
    Col1 = SOME_STRING_CONSTANT
    -- This expression is of the form <varchar> = <nvarchar>.
    -- Hence SQL introduces a conversion from varchar to nvarchar,
    --   resulting in
    Where …
    Convert(nvarchar(100), Col1) = SOME_STRING_CONSTANT
    -- Cannot use the index for column Col1 for some implementations.
    

Anlam farklılıklarına ek olarak, SQL Server ile CLR tür sistemleri arasında geçiş yaparken performans üzerindeki etkileri göz önünde bulundurmak önemlidir. Büyük veri kümeleri için bu tür performans sorunları bir uygulamanın dağıtılabilir olup olmadığını belirleyebilir.

Ayrıca bkz.