Tại sao CẬP NHẬT này thất bại với một vi phạm ràng buộc khóa duy nhất?


11

Tôi là một DBA "tình cờ", tương đối thiếu kinh nghiệm và gặp khó khăn bởi vấn đề này.

Chạy MS SQL Server 2012. Vấn đề là với câu lệnh CẬP NHẬT này:

UPDATE dbo.tAccts SET
       Ticket               = 'ARP.ExGE'
       , Method             = 'smtp'
       , AcctOwner          = 'r00417819'
       , DisplayName = '~AppLight HBSFax-Inactive'
       , Destination = 'r00417819@mail.ad.ge.com'
       , UpdatedBy          = SYSTEM_USER
       , UpdatedOn          = CAST(GetDate() AS DATE)
FROM dbo.vReclaimable
WHERE OHR_EmpStatus <> 'A'

nên cập nhật chỉ các hàng trong bảng tAccts được trả về bởi quan điểm vReclaimable.

Chế độ xem vReclaimable dựa trên bảng tAccts và trả về một tập hợp con của các hàng trong tAccts.

Khi tôi chạy nó, nó không thành công với một lỗi khóa duy nhất:

(0 row(s) affected)
Msg 2627, Level 14, State 1, Line 67
Violation of UNIQUE KEY constraint 'UQ__tAccounts_DNIS.Method.Destination.Phones'. Cannot insert duplicate key in object 'dbo.tAccts'. The duplicate key value is (68497, smtp, r00417819@mail.ad.ge.com, 800-905-8793, none).
The statement has been terminated.

Đủ công bằng, bảng tAccts có một ràng buộc khóa duy nhất:

CONSTRAINT [UQ__tAccounts_DNIS.Method.Destination.Phones] UNIQUE NONCLUSTERED 
(
                [DNIS] ASC,[Method] ASC,[Destination] ASC,[Phone_TF] ASC,[Phone_Local] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) 

Nhưng đây là điều kỳ lạ. Nếu tôi chạy hai truy vấn này:

select 'tAccts table', dnis, method, destination, phone_tf, phone_local from tAccts where dnis=68497
select 'vReclaimable view', dnis, method, destination, phone_tf, phone_local, daysidle from vReclaimable where dnis=68497

Hàng đầu tiên trả về hai hàng (như mong đợi):

(No column name)     dnis   method destination   phone_tf      phone_local
tAccts table  68497  ftp    ftp://faxuser@ap1plm02cige/appliances    800-905-8793  none
tAccts table  68497  unc    \\\\for4as01applge\\cfs_portfolio\\cfs_faxdocs  800-905-8793  none

và hàng thứ hai trả về 0 hàng (như mong đợi).

Nếu VỪA TỪ vReclaimable WHERE OHR_EmpStatus <> 'A' Trả về 0 hàng, tại sao CẬP NHẬT cố gắng cập nhật hàng trong đó DNIS = 68497?

(Tôi hy vọng tôi đã mô tả điều này đầy đủ. Tôi có cảm giác tôi đang thiếu một cái gì đó rõ ràng)

USE [TEST-GEAFax_arley_NEW]
GO

/****** Object:  Table [dbo].[tAccts]    Script Date: 12/9/2015 1:39:41 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

CREATE TABLE [dbo].[tAccts](
    [Ticket] [varchar](30) NOT NULL,
    [Method] [varchar](15) NOT NULL,
    [AcctOwner] [varchar](15) NOT NULL,
    [DisplayName] [varchar](75) NOT NULL,
    [Destination] [varchar](75) NOT NULL,
    [DNIS] [varchar](20) NOT NULL,
    [DNIS2] [varchar](20) NULL,
    [Phone_TF] [varchar](30) NOT NULL,
    [Phone_Local] [varchar](30) NOT NULL,
    [Phone_PBX] [varchar](255) NOT NULL,
    [UpdatedBy] [varchar](50) NOT NULL,
    [UpdatedOn] [date] NOT NULL,
    [FaxNotes] [varchar](255) NULL,
    [TelcomNotes] [varchar](255) NULL,
    [AcctID] [int] IDENTITY(0,1) NOT NULL,
 CONSTRAINT [PK__tAccounts_AcctID] PRIMARY KEY CLUSTERED 
(
    [AcctID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY],
 CONSTRAINT [UQ__tAccounts_DNIS.Method.Destination.Phones] UNIQUE NONCLUSTERED 
(
    [DNIS] ASC,
    [Method] ASC,
    [Destination] ASC,
    [Phone_TF] ASC,
    [Phone_Local] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

---------------------------------------------------------------------------------

USE [TEST-GEAFax_arley_NEW]
GO

/****** Object:  View [dbo].[vReclaimable]    Script Date: 12/9/2015 1:39:57 PM ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO


/***********************************************************************
* Written By    : N. Arley Dealey (200018252
* Written On    :
* Updated By    :
* Updated On    :
* Description   : Returns data from tAccts, vRxAl, vWLT_AllGE
* Notes         :
***********************************************************************/
CREATE VIEW [dbo].[vReclaimable] AS
SELECT
        a.Ticket
        , a.Method
        , a.AcctOwner
        , a.DisplayName
        , a.Destination
        , a.DNIS
        , a.DNIS2
        , a.Phone_TF
        , a.Phone_Local
        , a.Phone_PBX
        , a.UpdatedBy
        , a.UpdatedOn
        , a.FaxNotes
        , a.TelcomNotes
        , a.AcctID
        , COUNT(jt.JobID) AS 'FaxesRcvd'
        , CAST(MIN(jt.TimeStamp_UTC) AS DATE) AS 'FirstRcvd'
        , CAST(MAX(jt.TimeStamp_UTC) AS DATE) AS 'LastRcvd'
        , DATEDIFF(dd, MAX(jt.TimeStamp_UTC), GETDATE()) AS 'DaysIdle'
        , o.OHR_EmpSSO
        , o.OHR_EmpStatus
        , o.OHR_EmpName
        , o.OHR_EmpTitle
        , o.OHR_BizIndustryGroup
        , o.OHR_BizSegment
        , o.OHR_BizUnit
        , o.OHR_BizDept
        , o.OHR_BizDomain
FROM
    dbo.tAccts AS a
    LEFT OUTER JOIN dbo.tAccts_Retain AS r ON (a.AcctID = r.AcctID)
    LEFT OUTER JOIN dbo.vWLT_AllGE AS o ON (a.AcctOwner = o.OHR_EmpSSO)
    LEFT OUTER JOIN dbo.vRxAll AS jt ON (a.DNIS = jt.DNIS)
    WHERE ( 1                                               -- place holder, has no effect
            AND r.RetainID IS NULL                          -- out of scope: in Retain table
            AND a.Method = 'smtp'                           -- out of scope: ftp, unc, cifs, printers
            AND a.Phone_Local NOT LIKE '216-%'              -- out of scope: NELA numbers
            AND a.AcctOwner <> 'r00417819'                  -- out of scope: reclaimed numbers
            AND a.AcctOwner <> 'r00336832'                  -- out of scope: never assigned numbers
            AND a.AcctOwner <> 'r00971729'                  -- out of scope: invalid numbers
            AND a.Destination NOT LIKE 'g%@mail.ad.ge.com'  -- out of scope: distribution lists
            AND a.Destination NOT LIKE 'r%@mail.ad.ge.com'  -- out of scope: shared mailboxes
        )
    GROUP BY
        a.DNIS
        -- remaining columns are just for syntax reasons
        , a.Ticket, a.Method, a.AcctOwner, a.DisplayName, a.Destination, a.DNIS2, a.Phone_TF, a.Phone_Local, a.Phone_PBX, a.UpdatedBy, a.UpdatedOn, a.FaxNotes, a.TelcomNotes, a.AcctID
        , o.OHR_EmpSSO, o.OHR_EmpStatus, o.OHR_EmpName, o.OHR_EmpTitle
        , o.OHR_BizIndustryGroup, o.OHR_BizSegment, o.OHR_BizUnit, o.OHR_BizDept, o.OHR_BizDomain

GO

cho chúng tôi thấy CREATE VIEWtuyên bố.
ypercubeᵀᴹ

Và là OHR_EmpStatusmột cột của bảng, xem hay cả hai?
ypercubeᵀᴹ

1
Tôi chỉ đơn giản là ASTONISHED với số lượng phản hồi nhanh chóng và tuyệt vời cho câu hỏi của tôi. Như tôi đã nói, có lẽ có điều gì đó rõ ràng mà tôi đã bỏ qua hoặc hiểu lầm nhưng tôi không nghĩ đó là vì tôi đã kết thúc với việc tham gia chéo. Tôi nên đã đăng các định nghĩa cho bảng tAccts và chế độ xem vReclaimable. Tôi sẽ xem xét tất cả các câu trả lời được đăng cho đến nay và, nếu tôi vẫn nghĩ rằng chúng không đúng mục tiêu, tôi sẽ thêm các định nghĩa đó vào câu hỏi. Trong khi đó, một LỚN cảm ơn tất cả những người đã phản hồi.
ArleyD

Thêm câu lệnh CREATE cho cả tAccts và vReclaimable, theo yêu cầu của ypercube
ArleyD

Câu trả lời:


18

Nó sôi sùng sục với những gì UPDATEtuyên bố. Nó không hoàn toàn rõ ràng nhưng tuyên bố của bạn tương đương với điều này:

UPDATE upd SET
         Ticket             = 'ARP.ExGE'
       , Method             = 'smtp'
       , AcctOwner          = 'r00417819'
       , DisplayName = '~AppLight HBSFax-Inactive'
       , Destination = 'r00417819@mail.ad.ge.com'
       , UpdatedBy          = SYSTEM_USER
       , UpdatedOn          = CAST(GetDate() AS DATE)
FROM 
    dbo.tAccts AS upd 
  CROSS JOIN
    dbo.vReclaimable AS v
WHERE OHR_EmpStatus <> 'A' ;

Vì không có đề cập đến dbo.tAcctsbảng trong FROMvà không tham gia hoặc điều kiện giữa bảng và chế độ xem, nên kết quả là CROSStham gia và cố gắng cập nhật tất cả các hàng của bảng (và không chỉ từ chế độ xem) và có thể là nhiều lần nào cũng vậy!


Bạn có thể thêm một điều kiện tham gia (hoặc ở đâu) với:

UPDATE upd SET
         Ticket             = 'ARP.ExGE'
       , Method             = 'smtp'
       , AcctOwner          = 'r00417819'
       , DisplayName = '~AppLight HBSFax-Inactive'
       , Destination = 'r00417819@mail.ad.ge.com'
       , UpdatedBy          = SYSTEM_USER
       , UpdatedOn          = CAST(GetDate() AS DATE)
FROM 
    dbo.tAccts AS upd 
  JOIN
    dbo.vReclaimable AS v
      ON v.PK = upd.PK              -- whatever the PK column is
WHERE OHR_EmpStatus <> 'A' ;

hoặc (sử dụng phiên bản của bạn):

UPDATE dbo.tAccts SET
       Ticket               = 'ARP.ExGE'
       , Method             = 'smtp'
       , AcctOwner          = 'r00417819'
       , DisplayName = '~AppLight HBSFax-Inactive'
       , Destination = 'r00417819@mail.ad.ge.com'
       , UpdatedBy          = SYSTEM_USER
       , UpdatedOn          = CAST(GetDate() AS DATE)
FROM dbo.vReclaimable
WHERE OHR_EmpStatus <> 'A'
  AND vReclaimable.PK = tAccts.PK ;

Ngoài ra, bạn có thể (có thể) chỉ cần cập nhật chế độ xem. Để điều này hoạt động, chế độ xem phải tuân theo các giới hạn về "Chế độ xem cập nhật" . Xem đoạn có liên quan tại tài liệu MSDN : CREATE VIEW, Lượt xem cập nhật :

UPDATE dbo.vReclaimable SET
       Ticket               = 'ARP.ExGE'
       , Method             = 'smtp'
       , AcctOwner          = 'r00417819'
       , DisplayName = '~AppLight HBSFax-Inactive'
       , Destination = 'r00417819@mail.ad.ge.com'
       , UpdatedBy          = SYSTEM_USER
       , UpdatedOn          = CAST(GetDate() AS DATE)

WHERE OHR_EmpStatus <> 'A' ;

2

có vẻ như bạn không có sự tham gia giữa các bảng trong truy vấn cập nhật của bạn.

UPDATE dbo.tAccts SET
       Ticket               = 'ARP.ExGE'
       , Method             = 'smtp'
       , AcctOwner          = 'r00417819'
       , DisplayName = '~AppLight HBSFax-Inactive'
       , Destination = 'r00417819@mail.ad.ge.com'
       , UpdatedBy          = SYSTEM_USER
       , UpdatedOn          = CAST(GetDate() AS DATE)
FROM dbo.vReclaimable
WHERE OHR_EmpStatus <> 'A'

ở đây phải có một cái gì đó để khớp các hàng giữa các bảng, chẳng hạn như nơi tAccts.id = vReclaimable.id


2

Một cách khác để đặt điều này:

Vấn đề là bạn tin rằng tuyên bố "chỉ nên cập nhật các hàng trong bảng tAccts được trả về bởi chế độ xem vReclaimable".

Đó không phải là trường hợp. Nó cập nhật tất cả các hàng từ tAccts(bảng được đề cập ngay sau UPDATEđó) khớp với OHR_EmpStatus <> 'A'(điều kiện trong WHERE). Nó có thể sử dụng dữ liệu từvReclaimable khi làm như vậy (nhưng bạn không tham khảo gì về nó).

Nếu bạn muốn giới hạn nó ở các hàng nằm trong vReclaimable, ngoài các tùy chọn khác được trình bày, bạn có thể sử dụng truy vấn con:

UPDATE dbo.tAccts SET
       Ticket               = 'ARP.ExGE'
       , Method             = 'smtp'
       , AcctOwner          = 'r00417819'
       , DisplayName = '~AppLight HBSFax-Inactive'
       , Destination = 'r00417819@mail.ad.ge.com'
       , UpdatedBy          = SYSTEM_USER
       , UpdatedOn          = CAST(GetDate() AS DATE)
WHERE OHR_EmpStatus <> 'A' AND tAccts.key IN (SELECT key FROM vReclaimable)

0

Nếu truy vấn dưới đây trả về nhiều hơn một hàng:

select 'tAccts table', dnis, method, destination, phone_tf, phone_local 
from tAccts
where OHR_EmpStatus <> 'A'

sau đó, bạn đang cố cập nhật nhiều hàng có cùng giá trị do đó vi phạm ràng buộc duy nhất.


Cũng có thể đáng để xem xét điều này: stackoverflow.com/questions/2648445/ trên
infiniteLoop

0

một lựa chọn khác là: bạn không cần

TỪ dbo.vReclaimable

bởi vì bạn không sử dụng bất kỳ giá trị nào từ bảng này trong tuyên bố cập nhật của bạn.


Đối với bản ghi: trong trường hợp này, việc tham gia vReclaimableđược dự định chắc chắn để lọc bảng đang được cập nhật. Mặc dù nó không cần thiết cho SETmệnh đề, nhưng nó thực sự là một phần của WHEREmệnh đề.
Jon của tất cả các giao dịch
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.