เหตุใดการอัปเดตนี้จึงล้มเหลวเนื่องจากมีการละเมิดข้อ จำกัด คีย์ที่ไม่ซ้ำกัน


11

ฉันเป็น DBA "บังเอิญ" ซึ่งค่อนข้างไม่มีประสบการณ์และรู้สึกงุนงงกับปัญหานี้

ใช้ MS SQL Server 2012 ปัญหานี้เกิดขึ้นกับคำสั่ง UPDATE นี้:

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'

ซึ่งควรปรับปรุงเพียงแถวในตาราง tAccts ซึ่งจะถูกส่งกลับโดยมุมมอง vReclaimable

มุมมอง vReclaimable ขึ้นอยู่กับตาราง tAccts และส่งคืนชุดย่อยของแถวใน tAccts

เมื่อฉันเรียกใช้มันล้มเหลวด้วยข้อผิดพลาดของคีย์ที่ไม่ซ้ำกัน:

(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.

ยุติธรรมแล้วตาราง tAccts มีข้อ จำกัด ของคีย์ที่ไม่ซ้ำกัน:

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) 

แต่นี่คือสิ่งที่แปลก ถ้าฉันเรียกใช้แบบสอบถามทั้งสองนี้:

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

ผลตอบแทนแรกแถวที่สอง (ตามที่คาดไว้):

(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

และผลตอบแทนที่สอง 0 แถว (ตามที่คาดไว้)

ถ้า“ FROM vReclaimable WHERE OHR_EmpStatus <> 'A'” ส่งคืน 0 แถวเหตุใด UPDATE จึงพยายามอัปเดตแถวที่ DNIS = 68497

(ฉันหวังว่าฉันจะอธิบายเรื่องนี้อย่างเพียงพอฉันรู้สึกว่าฉันขาดอะไรบางอย่างที่ชัดเจน)

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

แสดงให้เราเห็นCREATE VIEWคำสั่ง
ypercubeᵀᴹ

และเป็นOHR_EmpStatusคอลัมน์ของตารางมุมมองหรือทั้งสองอย่าง?
ypercubeᵀᴹ

1
ฉันรู้สึกทึ่งกับจำนวนคำตอบที่รวดเร็วและยอดเยี่ยมสำหรับคำถามของฉัน อย่างที่ฉันพูดไปมันอาจเป็นสิ่งที่เห็นได้ชัดว่าฉันมองข้ามหรือเข้าใจผิด แต่ฉันไม่คิดว่าเป็นเพราะฉันลงเอยด้วยการ cross-join ฉันควรโพสต์คำจำกัดความสำหรับตาราง tAccts และ vReclaimable view ฉันจะตรวจสอบคำตอบทั้งหมดที่โพสต์จนถึงตอนนี้และถ้าฉันยังคิดว่าคำตอบนั้นไม่ตรงเป้าหมายฉันจะเพิ่มคำจำกัดความเหล่านั้นลงในคำถาม ในขณะเดียวกันขอบคุณมากสำหรับทุกคนที่ตอบสนอง
ArleyD

การเพิ่มคำสั่ง CREATE สำหรับทั้ง tAccts และ vReclaimable ตามที่ร้องขอโดย ypercube
ArleyD

คำตอบ:


18

มันเดือดลงไปในสิ่งที่UPDATEคำสั่งไม่ ไม่ชัดเจนทั้งหมด แต่ข้อความของคุณเทียบเท่ากับข้อความนี้:

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

เนื่องจากไม่มีการเอ่ยถึงdbo.tAcctsตารางในFROMและไม่เข้าร่วมหรือที่เงื่อนไขระหว่างตารางและมุมมองมันทำให้เกิดการCROSSเข้าร่วมและความพยายามในการปรับปรุงแถวทั้งหมดของตาราง (และไม่ใช่แค่จากมุมมอง) และอาจเป็นหลาย ๆ ครั้งเช่นกัน!


คุณสามารถเพิ่มเงื่อนไขการเข้าร่วม (หรือที่ไหน) ด้วย:

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

หรือ (ใช้รุ่นของคุณ):

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 ;

หรือมิฉะนั้นคุณสามารถ (อาจ) เพียงอัปเดตมุมมอง สำหรับเรื่องนี้จะทำงานมุมมองที่มีเพื่อให้สอดคล้องกับข้อ จำกัด เกี่ยวกับการ"ปรับปรุงได้ชม" ดูวรรคที่เกี่ยวข้องในเอกสาร MSDN: CREATE VIEW, ปรับปรุงได้เข้าชม :

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

ดูเหมือนว่าคุณจะไม่ได้เข้าร่วมระหว่างตารางในคิวรีอัปเดตของคุณ

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'

ที่นี่จะต้องมีบางสิ่งบางอย่างในการจับคู่แถวระหว่างตารางเช่นที่ tAccts.id = vReclaimable.id


2

อีกวิธีในการใส่:

ปัญหาคือความเชื่อของคุณว่าคำสั่ง "ควรอัปเดตเฉพาะแถวในตาราง tAccts ซึ่งส่งคืนโดยมุมมอง vReclaimable"

นั่นไม่ใช่กรณี มันจะอัปเดตแถวทั้งหมดจากtAccts(ตารางที่กล่าวถึงหลังจากนั้นUPDATE) ซึ่งตรงกับOHR_EmpStatus <> 'A'(เงื่อนไขในWHERE) อาจใช้ข้อมูลจากvReclaimableการทำเช่นนั้น (แต่คุณไม่ได้อ้างอิงถึงสิ่งใด ๆ )

หากคุณต้องการ จำกัด ให้อยู่ในแถวที่vReclaimableนอกเหนือไปจากตัวเลือกอื่น ๆ ที่นำเสนอคุณสามารถใช้แบบสอบถามย่อย:

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

หากแบบสอบถามด้านล่างส่งคืนมากกว่าหนึ่งแถว:

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

จากนั้นคุณพยายามอัปเดตหลายแถวด้วยค่าเดียวกันดังนั้นจึงเป็นการละเมิดข้อ จำกัด ที่ไม่ซ้ำกัน


อาจเป็นสิ่งที่ควรพิจารณาเช่นกัน: stackoverflow.com/questions/2648445/…
infiniteLoop

0

ตัวเลือกอื่นคือ: คุณไม่ต้องการ

จาก dbo.vReclaimable

เนื่องจากคุณไม่ได้ใช้ค่าใด ๆ จากตารางนี้ในคำสั่งการอัพเดทของคุณ


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