2

datetime を含むテーブルがあり、挿入された値に対して +-30 分の期間に datetime を含むエントリがあるかどうかを確認したいと考えています。だから私はこの制約を書きます:

USE [Test]
GO
/****** Object:  UserDefinedFunction [dbo].[CanInsertReception]    Script Date: 23.09.2015 12:19:51 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[CanInsertReception] (@receptionBegin datetime)
RETURNS bit
AS 
BEGIN
  DECLARE @result bit;
  IF EXISTS(SELECT * FROM Main
            where DATEDIFF(MINUTE, ReceptionBegin, @receptionBegin) <= 30 or DATEDIFF(MINUTE, @receptionBegin, ReceptionBegin) <= 30)
    SET @result = 0
  ELSE
    SET @result = 1

  return @result;    
END;
GO;

ALTER TABLE Main 
  ADD CONSTRAINT CheckIfCanInsertReception 
  CHECK (dbo.CanInsertReception(ReceptionBegin) = 1); 

しかし、データを挿入しようとすると、このチェックでは挿入が許可されませんが、このスクリプトを実行すると、同じことが行われます:

  DECLARE @receptionBegin datetime = '2015-01-01 09:00:00'

  DECLARE @result bit;
  IF EXISTS(SELECT * FROM Main
            where DATEDIFF(MINUTE, ReceptionBegin, @receptionBegin) <= 30 or DATEDIFF(MINUTE, @receptionBegin, ReceptionBegin) <= 30)
    SET @result = 0
  ELSE
    SET @result = 1

  SELECT @result

期待される出力が得られます1

ここで何が間違っていますか?

4

2 に答える 2

1

チェック制約の代わりにトリガーを使用するのが良いオプションかもしれません!

次のようなテスト テーブルを作成した場合:

ReceptionId ReceptionText   ReceptionBegin      ReceptionEnd 
1               A           2015-09-23 13:00    2015-09-23 13:45 
2               B           2015-09-23 14:00    2015-09-23 14:45
3               C           2015-09-23 15:00    2015-09-23 15:45
4               D           2015-09-23 16:00    2015-09-23 16:45

トリガーは次のようになります。

    CREATE TRIGGER dbo.IO_IU_Test ON dbo.Test
    INSTEAD OF INSERT,UPDATE
    AS
    BEGIN
        SET NOCOUNT ON;

        DECLARE @IsUpdate BIT = 0

        DECLARE @ErrMessage NVARCHAR(255)

        IF EXISTS (SELECT TOP 1 1 FROM deleted d)
            SET @IsUpdate = 1

        SET @ErrMessage = 'Value(s) can not be ' + IIF(@IsUpdate=1,'updated','inserted') + ' - there are existing Receptions which are less than 30 Minutes away!'

        IF EXISTS(
            SELECT TOP 1 1
            FROM    inserted i
                    INNER JOIN dbo.Test t ON
                      ABS(DATEDIFF(MINUTE,i.ReceptionBegin,t.ReceptionBegin)) <= 30
            )

        BEGIN
            RAISERROR(@ErrMessage,16,1)
            RETURN
        END

        IF @IsUpdate = 1
        BEGIN
            UPDATE  t
            SET     t.ReceptionText = i.ReceptionText,
                    t.ReceptionBegin = i.ReceptionBegin,
                    t.ReceptionEnd = t.ReceptionEnd
            FROM    dbo.Test t
                    INNER JOIN inserted i ON t.ReceptionId = i.ReceptionId
        END
        ELSE
        BEGIN
            INSERT INTO dbo.Test(ReceptionText,ReceptionBegin,ReceptionEnd)
            SELECT i.ReceptionText, i.ReceptionEnd, i.ReceptionEnd FROM inserted i
        END
    END

利点:

  • セットベースの操作
  • カスタムエラーメッセージを発生させることができます
  • try-catch やトランザクションを使用することで改善できます

テスト:

INSERT INTO dbo.Test (ReceptionText, ReceptionBegin, ReceptionEnd)
VALUES ('E','2015-09-23 12:31','2015-09-23 12:36')

エラーが発生します:Value(s) can not be inserted - there are existing Receptions which are less than 30 Minutes away!

UPDATE dbo.Test SET ReceptionBegin = '2015-09-23 13:30'
WHERE ReceptionId = 1

エラーが発生します:Value(s) can not be updated - there are existing Receptions which are less than 30 Minutes away!

INSERT INTO dbo.Test (ReceptionText, ReceptionBegin, ReceptionEnd)
VALUES ('E','2015-09-23 10:00','2015-09-23 10:21')

これは機能しています -> 09:30 から 10:30 の間は受付を開始しません

于 2015-09-23T11:11:44.697 に答える
1

結果は、テーブルのデータによって異なります。テーブルが空の場合、この制約を使用してテーブルにレコードを挿入できないようです。メインのテーブルには何行ありますか? しかし、いずれにせよ、より良い使用

WHERE ReceptionBegin BETWEEN  DATEADD (minute , -30 , @receptionBegin) AND DATEADD (minute , 30 , @receptionBegin)

必要なインデックスがない場合でも、高速に動作します。

于 2015-09-23T09:57:38.823 に答える