สร้างค่าคงที่ระดับฐานข้อมูล (การแจงนับ) โดยไม่ใช้ CLR หรือไม่


9

ฉันมีวัตถุ SQL หลายอย่างที่ต้องดำเนินการทางเลือกอื่นตามสถานะที่ต้องการของคำขอ มีวิธีในการสร้างค่าคงที่ระดับฐานข้อมูล (การแจงนับ) ที่สามารถส่งผ่านไปยังกระบวนงานที่เก็บไว้ฟังก์ชันค่าตารางและใช้ในแบบสอบถาม (โดยไม่ต้องใช้ CLR) หรือไม่

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

แล้วใช้มัน:

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

ไหนmyEnumTypeจะเก็บค่าการแจงนับไม่กี่

ในขั้นตอนที่ฉันจะสามารถใช้@EnumValueและทดสอบกับค่าในmyEnumTypeการทำงานที่จำเป็น ฉันจะทำให้ค่าของmyEnumTypebitmask สำหรับกรณีที่ฉันกำลังพิจารณา

สำหรับตัวอย่างง่ายๆให้พิจารณากระบวนการที่มีราคาแพงซึ่งใช้ชุดข้อมูลขนาดใหญ่และลดลงเป็นชุดข้อมูลที่มีขนาดเล็กลง แต่ยังคงมีขนาดใหญ่มาก ในกระบวนการนี้คุณต้องทำการปรับเปลี่ยนบางอย่างในระหว่างกระบวนการที่จะมีผลต่อผลลัพธ์ สมมติว่านี่เป็นตัวกรองสำหรับ (หรือต่อ) บันทึกบางประเภทตามสถานะของการคำนวณระดับกลางภายในการลดลง @EnumValueชนิดmyEnumTypeสามารถนำมาใช้ในการทดสอบสำหรับการนี้

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

เป็นค่าคงที่ระดับฐานข้อมูลเหล่านี้เป็นไปได้ใน SQL Server โดยไม่ต้องใช้ CLR หรือไม่

ฉันกำลังค้นหาการแจงนับระดับฐานข้อมูลหรือชุดของค่าคงที่ที่สามารถส่งผ่านเป็นพารามิเตอร์สำหรับกระบวนงานที่เก็บไว้ฟังก์ชันและอื่น ๆ

คำตอบ:


9

คุณสามารถสร้างประเภทการแจงนับใน SQL Server โดยใช้ XML Schema

ตัวอย่างเช่นสี

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>';

xml(dbo.ColorsEnum)ที่ช่วยให้คุณใช้ตัวแปรหรือพารามิเตอร์ชนิด

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

หากคุณลองเพิ่มสิ่งที่ไม่ใช่สี

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

คุณได้รับข้อผิดพลาด

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

การสร้าง XML แบบนั้นอาจค่อนข้างน่าเบื่อดังนั้นคุณสามารถสร้างมุมมองตัวช่วยที่เก็บค่าที่อนุญาตไว้ได้เช่นกัน

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;

และใช้มันเช่นนี้เพื่อสร้างการแจงนับ

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

หากคุณต้องการสร้างมุมมองแบบไดนามิกจาก XML Schema คุณสามารถแยกสีด้วยแบบสอบถามนี้

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);

แน่นอนว่าการแจงนับสามารถใช้เป็นพารามิเตอร์สำหรับฟังก์ชันและโพรซีเดอร์ได้

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

เนื่องจากคุณจะเห็นได้ชัดว่าใช้ SQL Server 2016 ผมอยากจะโยนออกอื่นที่เป็นไปได้ตัวเลือก SESSION_CONTEXT'-

บทความของ Leonard Lobel สถานะการแชร์ใน SQL Server 2016 ด้วยSESSION_CONTEXTข้อมูลที่ดีมากเกี่ยวกับฟังก์ชันใหม่นี้ใน SQL Server 2016

สรุปประเด็นสำคัญบางประการ:

SESSION_CONTEXTหากคุณเคยต้องการที่จะภาครัฐส่วนแบ่งการข้ามขั้นตอนการเก็บและแบตช์ตลอดอายุการใช้งานการเชื่อมต่อฐานข้อมูลที่คุณกำลังจะรัก เมื่อคุณเชื่อมต่อกับ SQL Server 2016 คุณจะได้รับพจนานุกรม stateful หรือสิ่งที่มักจะเรียกว่ากระเป๋ารัฐบางสถานที่ที่คุณสามารถเก็บค่าเช่นสตริงและตัวเลขแล้วดึงมันโดยคีย์ที่คุณกำหนด ในกรณีของSESSION_CONTEXTที่สำคัญคือสตริงใด ๆ และค่าเป็น sql_variant ซึ่งหมายความว่ามันสามารถรองรับหลากหลายประเภท

เมื่อคุณจัดเก็บบางสิ่งSESSION_CONTEXTมันจะอยู่ที่นั่นจนกว่าการเชื่อมต่อจะปิด มันไม่ได้ถูกเก็บไว้ในตารางใด ๆ ในฐานข้อมูลมันแค่อยู่ในหน่วยความจำตราบใดที่การเชื่อมต่อยังมีชีวิตอยู่ และรหัส T-SQL ใด ๆ และทั้งหมดที่ทำงานอยู่ในกระบวนงานทริกเกอร์ฟังก์ชันหรืออะไรก็ตามที่เก็บไว้สามารถแชร์สิ่งที่คุณ SESSION_CONTEXTต้องการ

สิ่งที่ใกล้เคียงที่สุดเช่นนี้เรามีมาจนถึงตอนนี้CONTEXT_INFOซึ่งช่วยให้คุณสามารถจัดเก็บและแบ่งปันค่าไบนารีเดียวยาวถึง 128 ไบต์ซึ่งมีความยืดหยุ่นน้อยกว่าพจนานุกรมที่คุณได้รับSESSION_CONTEXTซึ่งสนับสนุนค่าหลายค่าของข้อมูลที่แตกต่างกัน ประเภท

SESSION_CONTEXTใช้งานง่ายเพียงโทร sp_set_session_context เพื่อเก็บค่าโดยใช้คีย์ที่ต้องการ เมื่อคุณทำเช่นนั้นคุณระบุคีย์และค่าของหลักสูตร แต่คุณยังสามารถตั้งค่าพารามิเตอร์ read_only เป็นจริง นี่คือล็อคค่าในบริบทเซสชันดังนั้นจึงไม่สามารถเปลี่ยนแปลงได้ตลอดช่วงเวลาที่เหลือของการเชื่อมต่อ ตัวอย่างเช่นมันเป็นเรื่องง่ายสำหรับแอปพลิเคชันไคลเอนต์ที่เรียกกระบวนการนี้เก็บไว้เพื่อตั้งค่าบริบทบริบทของเซสชั่นทันทีหลังจากที่มันสร้างการเชื่อมต่อฐานข้อมูล หากแอปพลิเคชันตั้งค่าพารามิเตอร์ read_only เมื่อทำสิ่งนี้ขั้นตอนการจัดเก็บและรหัส T-SQL อื่น ๆ ที่ดำเนินการบนเซิร์ฟเวอร์สามารถอ่านค่าได้เท่านั้นพวกเขาไม่สามารถเปลี่ยนสิ่งที่ตั้งค่าโดยแอปพลิเคชันที่ทำงานบนไคลเอนต์

เป็นแบบทดสอบที่ฉันสร้างทริกเกอร์เข้าสู่ระบบเซิร์ฟเวอร์ที่กำหนดบางCONTEXT_SESSIONข้อมูล - หนึ่งSESSION_CONTEXT's @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;

ฉันเข้าสู่ระบบในฐานะผู้ใช้ใหม่อย่างสมบูรณ์และสามารถดึงSESSION_CONTEXTข้อมูล:

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

ฉันยังพยายามเปลี่ยนข้อมูลบริบท 'read_only':

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

และได้รับข้อผิดพลาด:

ข่าวสารเกี่ยวกับ 15664, ระดับ 16, สถานะ 1, โพรซีเดอร์ sp_set_session_context, บรรทัด 1 [แบทช์เริ่มบรรทัดที่ 8] ไม่สามารถตั้งค่าคีย์ 'ไม่สามารถเปลี่ยนได้' ในบริบทเซสชัน คีย์ถูกตั้งค่าเป็น read_only สำหรับเซสชันนี้

หมายเหตุสำคัญเกี่ยวกับทริกเกอร์การเข้าสู่ระบบ ( จากโพสต์นี้ )!

ทริกเกอร์การเข้าสู่ระบบสามารถป้องกันการเชื่อมต่อไปยังโปรแกรมฐานข้อมูลที่ประสบความสำเร็จสำหรับผู้ใช้ทั้งหมดรวมถึงสมาชิกของบทบาทเซิร์ฟเวอร์ถาวร sysadmin เมื่อทริกเกอร์การเข้าสู่ระบบป้องกันการเชื่อมต่อสมาชิกของบทบาทเซิร์ฟเวอร์ถาวร sysadmin สามารถเชื่อมต่อโดยใช้การเชื่อมต่อผู้ดูแลระบบโดยเฉพาะหรือโดยการเริ่มโปรแกรมฐานข้อมูลเครื่องยนต์ในโหมดการกำหนดค่าน้อยที่สุด (-f)


ข้อเสียเปรียบหนึ่งที่อาจเกิดขึ้นคือสิ่งนี้เติมอินสแตนซ์บริบทเซสชันกว้าง (ไม่ใช่ต่อฐานข้อมูล) ณ จุดนี้ตัวเลือกเดียวที่ฉันนึกได้คือ:

  1. Session_Contextตั้งชื่อคู่ค่าชื่อของคุณโดยนำหน้าด้วยชื่อฐานข้อมูลเพื่อไม่ให้เกิดการชนกันของชื่อประเภทเดียวกันในฐานข้อมูลอื่น สิ่งนี้ไม่ได้แก้ปัญหาการกำหนดSession_Contextค่าชื่อทั้งหมดล่วงหน้าสำหรับผู้ใช้ทั้งหมด
  2. เมื่อทริกเกอร์การเข้าสู่ระบบเริ่มทำงานคุณจะสามารถเข้าถึงEventData(xml) ซึ่งคุณสามารถใช้เพื่อแยกผู้ใช้เข้าสู่ระบบออกมาและคุณสามารถสร้างSession_Contextคู่ชื่อ - ค่าเฉพาะได้

4

ใน SQL Server ไม่ใช่ (แม้ว่าฉันจำการสร้างค่าคงที่ในแพ็คเกจ Oracle ย้อนกลับไปในปี 1998 และมีชนิดที่ไม่ได้อยู่ใน SQL Server)

และฉันเพิ่งทดสอบและพบว่าคุณไม่สามารถทำได้ด้วย SQLCLR อย่างน้อยก็ไม่ได้หมายความว่ามันจะใช้ได้ในทุกกรณี การระงับคือข้อ จำกัด เกี่ยวกับพารามิเตอร์ Stored Procedure ดูเหมือนว่าคุณไม่สามารถมี.หรือ::ในชื่อพารามิเตอร์ ฉันเหนื่อย:

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

-- and:

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

ในทั้งสองกรณีมันไม่ได้ผ่านขั้นตอนการแยกวิเคราะห์ (ตรวจสอบโดยใช้SET PARSEONLY ON;) เนื่องจาก:

ข่าวสารเกี่ยวกับ 102 ระดับ 15 สถานะ 1 บรรทัด xxxxx
ไวยากรณ์ไม่ถูกต้องใกล้ '.'

ในทางกลับกันทั้งสองวิธีทำงานกับพารามิเตอร์ฟังก์ชั่นที่ผู้ใช้กำหนดเอง:

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

-- and:

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

ดังนั้นสิ่งที่ดีที่สุดที่คุณสามารถทำได้คือใช้ SQLCLR เพื่อให้มีบางสิ่งที่ทำงานได้โดยตรงกับ UDF, TVFs, UDA (ฉันถือว่า) และข้อความค้นหาจากนั้นกำหนดให้กับตัวแปรท้องถิ่นเมื่อต้องการใช้กับ Stored Procedure:

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

นี่เป็นวิธีที่ฉันได้ทำเมื่อมีโอกาสที่จะมีค่า enum จริง (ตรงข้ามกับค่าการค้นหาซึ่งควรอยู่ในตารางการค้นหาที่เฉพาะเจาะจงกับการใช้งาน / ความหมาย)


ด้วยความพยายามที่จะทำสิ่งนี้ด้วยฟังก์ชั่นที่ผู้ใช้กำหนดเอง (UDF) เพื่อคายค่า "คงที่" / "enum" ฉันไม่สามารถทำงานได้ทั้งในแง่ของการส่งผ่านเป็นพารามิเตอร์ Stored Procedure:

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

ส่งกลับข้อผิดพลาด "ไวยากรณ์ที่ไม่ถูกต้อง" โดยที่ SSMS จะเน้นทุกอย่างภายในวงเล็บแม้ว่าฉันจะแทนที่สตริงด้วยตัวเลขหรือวงเล็บขวาถ้าไม่มีพารามิเตอร์ที่จะผ่าน

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