CLR kullanmadan veritabanı düzeyinde sabitler (numaralandırmalar) oluşturulsun mu?


9

Istenilen bir duruma dayalı alternatif eylemler almak gereken birkaç SQL nesneleri var. Saklı yordamlara, tablo değerli işlevlere geçirilebilen ve sorgularda (CLR kullanılmadan) kullanılabilen veritabanı düzeyinde sabitler (numaralandırmalar) oluşturmanın bir yolu var mı?

CREATE PROCEDURE dbo.DoSomeWork(@param1 INTEGER, ..., @EnumValue myEnumType)  AS ...;

ve sonra kullanın:

EXEC doSomeWork 85, ..., (myEnumType.EnumValue1 + myEnumType.EnumValue2);

Burada myEnumTypebirkaç numaralandırma değeri bulunur.

Prosedürde gerekli işi yapmak için @EnumValuedeğerleri kullanarak test edebilirim myEnumType. myEnumTypeDüşündüğüm dava için bir bit maskının değerlerini yapardım .

Basit bir örnek olarak, çok büyük bir veri kümesi alan ve daha küçük ama yine de çok büyük bir veri kümesine indirgeyen pahalı bir işlemi düşünün. Bu süreçte, o sürecin ortasında sonucu etkileyecek bazı ayarlamalar yapmanız gerekir. Bunun, azaltma içindeki bir ara hesaplamanın bazı durumuna dayalı olarak bazı kayıt türlerine filtre uygulandığını (veya bunlara karşı olduğunu) varsayalım. @EnumValueÇeşidi myEnumTypeBunun için teste kullanılabilecek

SELECT   ...
FROM     ...
WHERE       (@EnumValue & myEnumType.EnumValue1 = myEnumType.EnumValue1 AND ...)
        OR  (@EnumValue & myEnumType.EnumValue2 = myEnumType.EnumValue2 AND ...)
        OR  ...

Bu tür veritabanı düzeyinde sabitler CLR kullanılmadan SQL Server'da mümkün müdür?

Ben bir veritabanı düzeyinde numaralandırma veya saklı yordamlar, işlevler, vb parametreler olarak geçirilebilir sabitler kümesi arıyorum .

Yanıtlar:


9

XML Şeması kullanarak SQL Server'da bir numaralandırma türü oluşturabilirsiniz.

Örneğin Renkler.

create xml schema collection ColorsEnum as '
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="Color">
        <xs:simpleType>
            <xs:restriction base="xs:string"> 
                <xs:enumeration value="Red"/>
                <xs:enumeration value="Green"/>
                <xs:enumeration value="Blue"/>
                <xs:enumeration value="Yellow"/>
            </xs:restriction> 
        </xs:simpleType>
    </xs:element>
</xs:schema>';

Bu, türdeki bir değişkeni veya parametreyi kullanmanıza olanak tanır xml(dbo.ColorsEnum).

declare @Colors xml(dbo.ColorsEnum);
set @Colors = '<Color>Red</Color><Color>Green</Color>'

Renk olmayan bir şey eklemeye çalışırsanız

set @Colors = '<Color>Red</Color><Color>Ferrari</Color>';

bir hata alıyorsunuz.

Msg 6926, Level 16, State 1, Line 43
XML Validation: Invalid simple type value: 'Ferrari'. Location: /*:Color[2]

XML'i bu şekilde oluşturmak biraz sıkıcı olabilir, böylece izin verilen değerleri de tutan bir yardımcı görünüm oluşturabilirsiniz.

create view dbo.ColorsConst as
select cast('<Color>Red</Color>' as varchar(100)) as Red,
       cast('<Color>Green</Color>' as varchar(100)) as Green,
       cast('<Color>Blue</Color>' as varchar(100)) as Blue,
       cast('<Color>Yellow</Color>' as varchar(100)) as Yellow;

Numaralandırmayı oluşturmak için böyle kullanın.

set @Colors = (select Red+Blue+Green from dbo.ColorsConst);

Görünümü XML Şeması'ndan dinamik olarak oluşturmak isterseniz, bu sorgu ile renkleri çıkarabilirsiniz.

select C.Name
from (select xml_schema_namespace('dbo','ColorsEnum')) as T(X)
  cross apply T.X.nodes('//*:enumeration') as E(X)
  cross apply (select E.X.value('@value', 'varchar(100)')) as C(Name);

Numaralandırma elbette fonksiyonlara ve prosedürlere parametre olarak da kullanılabilir.

create function dbo.ColorsToString(@Colors xml(ColorsEnum))
returns varchar(100)
as
begin
  declare @T table(Color varchar(100));

  insert into @T(Color)
  select C.X.value('.', 'varchar(100)')
  from @Colors.nodes('Color') as C(X);

  return stuff((select ','+T.Color
                from @T as T
                for xml path('')), 1, 1, '');
end
create procedure dbo.GetColors
  @Colors xml(ColorsEnum)
as
select C.X.value('.', 'varchar(100)') as Color
from @Colors.nodes('Color') as C(X);
declare @Colors xml(ColorsEnum) = '
<Color>Red</Color>
<Color>Blue</Color>
';

select dbo.ColorsToString(@Colors);

set @Colors = (select Red+Blue+Green from dbo.ColorsConst);
exec dbo.GetColors @Colors;

6

Görünüşe göre SQL Server 2016 kullandığınız için, başka bir ' olası ' seçenek atmak istiyorum - SESSION_CONTEXT.

Leonard Lobel'in SQL Server 2016'daki Paylaşım Durumu ileSESSION_CONTEXT ilgili makalesi, SQL Server 2016'daki bu yeni işlevsellik hakkında çok iyi bilgilere sahiptir.

Bazı önemli noktaları özetlemek gerekirse:

Bir veritabanı bağlantısının ömrü boyunca oturum durumunu tüm saklı yordamlar ve gruplar arasında paylaşmak istiyorsanız, çok seveceksiniz SESSION_CONTEXT. SQL Server 2016'ya bağlandığınızda, durum bilgisi olan bir sözlük veya genellikle durum çantası olarak adlandırılan bir sözcük, dizeler ve sayılar gibi değerleri depolayabileceğiniz ve atadığınız bir anahtarla alabileceğiniz bir yer alırsınız. Durumunda, SESSION_CONTEXTanahtar herhangi bir dize ve değer bir sql_variant, yani çeşitli türleri barındırabilir.

Bir şeyleri SESSION_CONTEXT , bağlantı kapanana kadar orada kalır. Veritabanındaki herhangi bir tabloda depolanmaz, bağlantı canlı kaldığı sürece bellekte kalır. Ve saklı yordamlar, tetikleyiciler, işlevler veya herhangi bir şey içinde çalışan herhangi bir T-SQL kodu içine ittiğini paylaşabilirsiniz SESSION_CONTEXT.

Şimdiye kadar sahip olduğumuz en yakın şey CONTEXT_INFO, 128 bayta kadar tek bir ikili değeri depolamanıza ve paylaşmanıza olanak tanıyan, aldığınız sözlüğe göre çok daha az esnek olan ve SESSION_CONTEXTfarklı verilerin birden çok değerini destekleyen türleri.

SESSION_CONTEXTkullanımı kolaydır, değeri istenen bir anahtarla kaydetmek için sadece sp_set_session_context'i arayın. Bunu yaptığınızda, anahtar ve değeri elbette sağlarsınız, ancak read_only parametresini true olarak da ayarlayabilirsiniz. Bu, oturum bağlamındaki değeri kilitler, böylece bağlantının geri kalanı boyunca değiştirilemez. Örneğin, bir istemci uygulamasının, veritabanı bağlantısını kurduktan hemen sonra bazı oturum bağlamı değerlerini ayarlamak için bu saklı yordamı çağırması kolaydır. Uygulama bunu yaparken read_only parametresini ayarlarsa, daha sonra sunucuda yürütülen saklı yordamlar ve diğer T-SQL kodu yalnızca değeri okuyabilir, istemcide çalışan uygulama tarafından ayarlananları değiştiremez.

Bir test olarak, bazı CONTEXT_SESSIONbilgileri ayarlayan bir sunucu oturum açma tetikleyicisi oluşturdum - bunlardan biri SESSION_CONTEXTolarak ayarlandı @read_only.

DROP TRIGGER IF EXISTS [InitializeSessionContext] ON ALL SERVER
GO
CREATE TRIGGER InitializeSessionContext ON ALL SERVER
FOR LOGON AS

BEGIN

    --Initialize context information that can be altered in the session
    EXEC sp_set_session_context @key = N'UsRegion'
        ,@value = N'Southeast'

    --Initialize context information that cannot be altered in the session
    EXEC sp_set_session_context @key = N'CannotChange'
        ,@value = N'CannotChangeThisValue'
        ,@read_only = 1

END;

Tamamen yeni bir kullanıcı olarak giriş yaptım ve SESSION_CONTEXTbilgileri elde edebildim :

DECLARE @UsRegion varchar(20)
SET @UsRegion = CONVERT(varchar(20), SESSION_CONTEXT(N'UsRegion'))
SELECT DoThat = @UsRegion

DECLARE @CannotChange varchar(20)
SET @CannotChange = CONVERT(varchar(20), SESSION_CONTEXT(N'CannotChange'))
SELECT DoThat = @CannotChange

Hatta 'salt okunur' bağlam bilgilerini değiştirmeye çalıştım:

EXEC sp_set_session_context @key = N'CannotChange'
    ,@value = N'CannotChangeThisValue'

ve bir hata aldı:

Msg 15664, Seviye 16, Durum 1, Yordam sp_set_session_context, Satır 1 [Toplu Başlangıç ​​Satırı 8] Oturum bağlamında 'CannotChange' anahtarı ayarlanamıyor. Anahtar, bu oturum için salt okunur olarak ayarlandı.

Giriş tetikleyicileri hakkında önemli bir not ( bu gönderiden )!

Oturum açma tetikleyicisi, sysadmin sabit sunucu rolünün üyeleri de dahil olmak üzere tüm kullanıcılar için Veritabanı Altyapısı'na başarılı bağlantıları etkili bir şekilde önleyebilir. Oturum açma tetikleyicisi bağlantıları engelliyorsa, sysadmin sabit sunucu rolünün üyeleri özel yönetici bağlantısını kullanarak veya Veritabanı Altyapısı'nı en az yapılandırma modunda (-f) başlatarak bağlanabilir.


Potansiyel bir dezavantaj, bunun oturum bağlamı örneğini (veritabanı başına değil) doldurmasıdır. Bu noktada, düşünebileceğim tek seçenek:

  1. Session_ContextBaşka bir veritabanında aynı tür adında bir çarpışmaya neden olmamak için ad-değer çiftlerinizi veritabanı adıyla önek olarak adlandırın. Bu, Session_Contexttüm kullanıcılar için TÜM ad değerlerini önceden tanımlama sorununu çözmez .
  2. Oturum açma tetikleyicisi tetiklendiğinde EventData, oturum açma kullanıcısını ayıklamak için kullanabileceğiniz (xml) erişimine sahip olursunuz ve buna dayalı olarak, belirli Session_Contextad-değer çiftleri oluşturabilirsiniz.

4

SQL Server'da, hayır (yine de 1998'de Oracle paketlerinde sabitler oluşturduğumu hatırlıyorum ve SQL Server'da bunlara sahip olmayı özledim).

VE, sadece test ettim ve SQLCLR ile bile yapamayacağınızı buldum, en azından her durumda çalışacağı anlamında değil. Muhafaza, Saklı Yordam parametrelerindeki kısıtlamalardır. Görünüşe göre a .veya ::parametre adında olamazsınız . Denedim:

EXEC MyStoredProc @ParamName = SchemaName.UdtName::[StaticField];

-- and:

DECLARE @Var = SchemaName.UdtName = 'something';
EXEC MyStoredProc @ParamName = @Var.[InstanceProperty];

Her iki durumda da, aşağıdaki SET PARSEONLY ON;nedenlerle ayrıştırma aşamasını bile geçemedi (kullanılarak doğrulandı ):

Msg 102, Seviye 15, Durum 1, Satır xxxxx
'.' Yakınında yanlış sözdizimi.

Öte yandan, her iki yöntem de Kullanıcı Tanımlı İşlev parametreleri için çalışmıştır:

SELECT MyUDF(SchemaName.UdtName::[StaticField]);

-- and:

DECLARE @Var = SchemaName.UdtName = N'something';
SELECT MyUDF(@Var.[InstanceProperty]);

Bu nedenle, gerçekten yapabileceğiniz en iyi şey, doğrudan UDF'ler, TVF'ler, UDA'lar (sanırım) ve sorgularla çalışan bir şeye sahip olmak için SQLCLR'yi kullanmak ve daha sonra Saklı Yordamlar ile kullanmanız gerektiğinde yerel değişkenlere atamaktır:

DECLARE @VarInt = SchemaName.UdtName::[StaticField];
EXEC MyStoredProc @ParamName = @VarInt;

Bu, gerçek bir enum değerine sahip olma fırsatı olduğunda kullandığım yaklaşımdır (kullanım / anlamına özgü bir arama tablosunda olması gereken bir arama değerinin aksine).


"Sabit" / "enum" değerini tükürmek için bir Kullanıcı Tanımlı İşlev (UDF) ile bunu denemeye gelince, bunu bir Saklı Yordam parametresi olarak geçirme açısından çalıştıramadım:

EXEC MyStoredProc @ParamName = FunctionName(N'something');

dizeyi bir sayıyla değiştirsem bile SSMS parantez içindeki her şeyi vurgulayan "Yanlış sözdizimi" hatasını veya iletilecek parametre yoksa sağ parantez döndürür.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.