ข้อ จำกัด ค่าเริ่มต้นคุ้มค่าหรือไม่


19

ฉันมักจะออกแบบฐานข้อมูลตามกฎต่อไปนี้:

  • ไม่มีใครอื่นนอกจาก db_owner และดูแลระบบสามารถเข้าถึงตารางฐานข้อมูล
  • บทบาทของผู้ใช้จะถูกควบคุมที่ชั้นแอปพลิเคชัน ฉันมักจะใช้บทบาท db หนึ่งรายการเพื่อให้สิทธิ์เข้าถึงมุมมองโพรซีเดอร์และฟังก์ชันที่เก็บไว้ แต่ในบางกรณีฉันเพิ่มกฎที่สองเพื่อปกป้องโพรซีเดอร์ที่เก็บไว้บางส่วน
  • ฉันใช้ TRIGGERS เพื่อตรวจสอบข้อมูลที่สำคัญในขั้นต้น

CREATE TRIGGER <TriggerName>
ON <MyTable>
[BEFORE | AFTER] INSERT
AS
    IF EXISTS (SELECT 1 
               FROM   inserted
               WHERE  Field1 <> <some_initial_value>
               OR     Field2 <> <other_initial_value>)
    BEGIN
        UPDATE MyTable
        SET    Field1 = <some_initial_value>,  
               Field2 = <other_initial_value>  
        ...  
    END
  • DML ดำเนินการโดยใช้กระบวนงานที่เก็บไว้:

sp_MyTable_Insert(@Field1, @Field2, @Field3, ...);
sp_MyTable_Delete(@Key1, @Key2, ...);
sp_MyTable_Update(@Key1, @Key2, @Field3, ...);

คุณคิดว่าในสถานการณ์นี้คุ้มค่าที่จะใช้ DEFAULT CONSTRAINT หรือไม่หรือฉันกำลังเพิ่มงานพิเศษและไม่จำเป็นให้กับเซิร์ฟเวอร์ DB?

ปรับปรุง

ฉันเข้าใจว่าโดยใช้ข้อ จำกัด DEFAULT ฉันจะให้ข้อมูลเพิ่มเติมแก่บุคคลอื่นที่ต้องจัดการฐานข้อมูล แต่ฉันสนใจการแสดงเป็นส่วนใหญ่

ฉันคิดว่าฐานข้อมูลกำลังตรวจสอบค่าเริ่มต้นเสมอแม้ว่าฉันจะให้ค่าที่ถูกต้องดังนั้นฉันจึงทำงานเดียวกันสองครั้ง

ตัวอย่างเช่นมีวิธีที่จะหลีกเลี่ยงข้อ จำกัด การตั้งค่าเริ่มต้นภายในการดำเนินการทริกเกอร์หรือไม่?


1
หากคุณกำลังใช้โพรซีเดอร์สำหรับ DML ทั้งหมดของคุณฉันจะใช้ตรรกะการตรวจสอบของคุณที่นั่น ใช้ข้อ จำกัด เพื่อป้องกันไม่ให้ใครก็ตามที่สามารถเข้าถึงตารางฐานจากการทำผิดพลาด แต่ทริกเกอร์จะชะลอตัวลงอย่างมาก
Jonathan Fite

คุณกำลังตรวจสอบสิ่งใดในทริกเกอร์ สามารถทำกับข้อ จำกัด การตรวจสอบแทนได้หรือไม่?
Martin Smith

@Martin มันขึ้นอยู่กับ แต่การตรวจสอบข้อ จำกัด ถูกนำไปใช้เสมอฉันจำเป็นต้องตรวจสอบค่าเริ่มต้นเช่นผู้ใช้เวลาปัจจุบัน ฯลฯ ดูคำถามอื่น ๆ ของฉัน: dba.stackexchange.com/questions/164678/ …
McNets

1
หากคุณต้องการคำตอบ "ทั่วไป" คุณควรลบส่วน "ข้อ จำกัด " ใน SQL ข้อ จำกัดตรวจสอบค่าอินพุต พวกเขาไม่ได้กำหนดค่าเริ่มต้นค่า ไม่มีสิ่งเช่น "ข้อ จำกัด เริ่มต้น" ใน SQL ฉันเพิ่มแท็กsql-serverและtsqlเนื่องจากรหัสในคำถามของคุณมีความเฉพาะเจาะจงอย่างยิ่งสำหรับ DBMS นั้น
a_horse_with_no_name

คำตอบ:


21

ฉันคิดว่าฐานข้อมูลกำลังตรวจสอบค่าเริ่มต้นเสมอแม้ว่าฉันจะให้ค่าที่ถูกต้องดังนั้นฉันจึงทำงานเดียวกันสองครั้ง

อืมทำไมคุณถึงคิดเช่นนั้น? ;-) ระบุว่าค่าเริ่มต้นมีอยู่เพื่อให้ค่าเมื่อคอลัมน์ที่แนบมานั้นไม่ปรากฏในINSERTคำสั่งฉันจะถือว่าตรงกันข้าม: พวกเขาจะถูกละเว้นอย่างสมบูรณ์หากคอลัมน์ที่เกี่ยวข้องอยู่ในINSERTงบ

โชคดีที่พวกเราทั้งคู่ไม่จำเป็นต้องคาดเดาสิ่งใดเนื่องจากคำแถลงนี้ในคำถาม:

ฉันสนใจการแสดงเป็นส่วนใหญ่

คำถามเกี่ยวกับประสิทธิภาพมักทดสอบได้ ดังนั้นเราเพียงแค่ต้องทำการทดสอบเพื่อให้ SQL Server (ผู้มีอำนาจที่แท้จริงที่นี่) ตอบคำถามนี้

ติดตั้ง

รันต่อไปนี้หนึ่งครั้ง:

SET NOCOUNT ON;

-- DROP TABLE #HasDefault;
CREATE TABLE #HasDefault
(
  [HasDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  [SomeInt] INT NULL,
  [SomeDate] DATETIME NOT NULL DEFAULT (GETDATE())
);

-- DROP TABLE #NoDefault;
CREATE TABLE #NoDefault
(
  [NoDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  [SomeInt] INT NULL,
  [SomeDate] DATETIME NOT NULL
);

-- make sure that data file and Tran Log file are grown, if need be, ahead of time:
INSERT INTO #HasDefault ([SomeInt])
  SELECT TOP (2000000) NULL
  FROM   [master].sys.[all_columns] ac1
  CROSS JOIN [master].sys.[all_columns] ac2;

ดำเนินการทดสอบ 1A และ 1B เป็นรายบุคคลไม่ได้อยู่ด้วยกันเพราะจะทำให้จังหวะนั้นแย่ เรียกใช้แต่ละคนหลายครั้งเพื่อให้ได้ความรู้สึกของเวลาเฉลี่ยสำหรับแต่ละคน

ทดสอบ 1A

TRUNCATE TABLE #HasDefault;
GO

PRINT '#HasDefault:';
SET STATISTICS TIME ON;
INSERT INTO #HasDefault ([SomeDate])
  SELECT TOP (1000000) '2017-05-15 10:11:12.000'
  FROM   [master].sys.[all_columns] ac1
  CROSS JOIN [master].sys.[all_columns] ac2;
SET STATISTICS TIME OFF;
GO

ทดสอบ 1B

TRUNCATE TABLE #NoDefault;
GO

PRINT '#NoDefault:';
SET STATISTICS TIME ON;
INSERT INTO #NoDefault ([SomeDate])
  SELECT TOP (1000000) '2017-05-15 10:11:12.000'
  FROM   [master].sys.[all_columns] ac1
  CROSS JOIN [master].sys.[all_columns] ac2;
SET STATISTICS TIME OFF;
GO

ดำเนินการทดสอบแบบ 2A และ 2B แบบแยกทีละตัว เรียกใช้แต่ละคนหลายครั้งเพื่อให้ได้ความรู้สึกของเวลาเฉลี่ยสำหรับแต่ละคน

ทดสอบ 2A

TRUNCATE TABLE #HasDefault;
GO

DECLARE @Counter INT = 0,
        @StartTime DATETIME,
        @EndTime DATETIME;

BEGIN TRAN;
--SET STATISTICS TIME ON;
SET @StartTime = GETDATE();
WHILE (@Counter < 100000)
BEGIN
  INSERT INTO #HasDefault ([SomeDate]) VALUES ('2017-05-15 10:11:12.000');
  SET @Counter = @Counter + 1;
END;
SET @EndTime = GETDATE();
--SET STATISTICS TIME OFF;
COMMIT TRAN;
PRINT DATEDIFF(MILLISECOND, @StartTime, @EndTime);

ทดสอบ 2B

TRUNCATE TABLE #NoDefault;
GO

DECLARE @Counter INT = 0,
        @StartTime DATETIME,
        @EndTime DATETIME;

BEGIN TRAN;
--SET STATISTICS TIME ON;
SET @StartTime = GETDATE();
WHILE (@Counter < 100000)
BEGIN
  INSERT INTO #NoDefault ([SomeDate]) VALUES ('2017-05-15 10:11:12.000');
  SET @Counter = @Counter + 1;
END;
SET @EndTime = GETDATE();
--SET STATISTICS TIME OFF;
COMMIT TRAN;
PRINT DATEDIFF(MILLISECOND, @StartTime, @EndTime);

คุณจะเห็นว่าไม่มีความแตกต่างในการจับเวลาระหว่างการทดสอบ 1A และ 1B หรือระหว่างการทดสอบ 2A และ 2B ดังนั้นไม่มีการลงโทษที่จะมีDEFAULTกำหนด แต่ไม่ได้ใช้

นอกจากนี้ยังเป็นเพียงแค่การบันทึกพฤติกรรมที่ตั้งใจคุณต้องจำไว้ว่าส่วนใหญ่คุณที่ใส่ใจเกี่ยวกับคำสั่ง DML ที่มีอยู่อย่างสมบูรณ์ภายในขั้นตอนการจัดเก็บของคุณ ฝ่ายสนับสนุนไม่สนใจ นักพัฒนาในอนาคตอาจไม่ทราบว่าคุณต้องการให้ DML ทั้งหมดถูกห่อหุ้มไว้ในกระบวนงานที่เก็บไว้หรือไม่สนใจแม้ว่าพวกเขาจะรู้ และใครก็ตามที่รักษาฐานข้อมูลนี้หลังจากที่คุณหายไป (ไม่ว่าจะเป็นโครงการหรืองานอื่น) อาจไม่สนใจหรืออาจไม่สามารถป้องกันการใช้ ORM ได้ไม่ว่าพวกเขาจะประท้วงเท่าใด ดังนั้นค่าเริ่มต้นสามารถช่วยในการที่พวกเขาให้คน "ออก" เมื่อทำINSERTโดยเฉพาะอย่างยิ่งโฆษณาที่INSERTทำโดยตัวแทนสนับสนุนเนื่องจากเป็นหนึ่งคอลัมน์ที่พวกเขาไม่จำเป็นต้องรวม (ซึ่งเป็นเหตุผลที่ฉันมักจะใช้ค่าเริ่มต้นในการตรวจสอบ คอลัมน์วันที่)


และมันเพิ่งเกิดขึ้นกับฉันว่ามันสามารถแสดงค่อนข้างเป็นกลางหรือไม่มีการDEFAULTตรวจสอบเมื่อคอลัมน์ที่เกี่ยวข้องอยู่ในINSERTงบ: เพียงแค่ให้ค่าที่ไม่ถูกต้อง การทดสอบต่อไปนี้ไม่เพียงแค่นั้น:

-- DROP TABLE #BadDefault;
CREATE TABLE #BadDefault
(
  [BadDefaultID] INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
  [SomeInt] INT NOT NULL DEFAULT (1 / 0)
);


INSERT INTO #BadDefault ([SomeInt]) VALUES (1234); -- Success!!!
SELECT * FROM #BadDefault; -- just to be sure ;-)



INSERT INTO #BadDefault ([SomeInt]) VALUES (DEFAULT); -- Error:
/*
Msg 8134, Level 16, State 1, Line xxxxx
Divide by zero error encountered.
The statement has been terminated.
*/
SELECT * FROM #BadDefault; -- just to be sure ;-)
GO

อย่างที่คุณเห็นเมื่อมีการระบุคอลัมน์ (และค่าไม่ใช่คำหลักDEFAULT) ค่าเริ่มต้นจะถูกละเว้น 100% เรารู้สิ่งนี้เพราะINSERTประสบความสำเร็จ แต่หากมีการใช้ค่าเริ่มต้นจะมีข้อผิดพลาดตามที่ได้รับการดำเนินการในที่สุด


มีวิธีใดที่จะหลีกเลี่ยงข้อ จำกัด DEFAULT ภายในการเรียกใช้ทริกเกอร์ได้หรือไม่

ในขณะที่ต้องการหลีกเลี่ยงข้อ จำกัด เริ่มต้น (อย่างน้อยในบริบทนี้) ไม่จำเป็นอย่างสมบูรณ์เพื่อความสมบูรณ์ของมันก็สามารถสังเกตได้ว่ามันจะเป็นไปได้ที่จะ "หลีกเลี่ยง" ข้อ จำกัด เริ่มต้นภายในINSTEAD OFทริกเกอร์ แต่ไม่อยู่ภายในAFTERทริกเกอร์ ตามเอกสารประกอบของCREATE TRIGGER :

หากมีข้อ จำกัด ในตารางทริกเกอร์พวกเขาจะถูกตรวจสอบหลังจากการดำเนินการทริกเกอร์ INSTEAD OF และก่อนการดำเนินการทริกเกอร์ AFTER หากมีการละเมิดข้อ จำกัด การกระทำของทริกเกอร์ INSTEAD OF จะถูกย้อนกลับและทริกเกอร์ AFTER จะไม่เริ่มทำงาน

แน่นอนว่าการใช้INSTEAD OFTrigger จำเป็นต้องมี:

  1. ปิดการใช้งานข้อ จำกัด เริ่มต้น
  2. การสร้างAFTERทริกเกอร์ที่เปิดใช้งานข้อ จำกัด

อย่างไรก็ตามฉันไม่แนะนำให้ทำเช่นนี้


1
@McNets ไม่มีปัญหา :-) คำถามนี้ให้โอกาสที่ดีในการค้นคว้าบางสิ่ง และในการทำเช่นนั้นฉันพยายามทดสอบ MySQL (ตั้งแต่แรกที่คุณถามเกี่ยวกับ RDBMS โดยทั่วไป) และพบว่าค่าเริ่มต้นใน MySQL ต้องเป็นค่าคงที่โดยมีข้อยกเว้นสำหรับCURRENT_TIMESTAMPคอลัมน์วันที่และเวลาเท่านั้น ดังนั้นการทดสอบ "ง่าย" ของการใช้นิพจน์ที่ไม่ถูกต้องจะไม่ทำงานที่นั่น (และไม่สามารถใช้ค่าที่ไม่ถูกต้องในการCREATE TABLEตรวจสอบ) และฉันไม่มีเวลาในการสร้างการทดสอบประสิทธิภาพ แต่ฉันสงสัยว่า RDBMS ส่วนใหญ่จะปฏิบัติต่อพวกเขาในลักษณะเดียวกัน
โซโลมอน Rutzky

9

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

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