Soy un DBA "accidental", relativamente inexperto y desconcertado por este problema.
Ejecución de MS SQL Server 2012. El problema está en esta declaración de ACTUALIZACIÓN:
UPDATE dbo.tAccts SET
Ticket = 'ARP.ExGE'
, Method = 'smtp'
, AcctOwner = 'r00417819'
, DisplayName = '~AppLight HBSFax-Inactive'
, Destination = '[email protected]'
, UpdatedBy = SYSTEM_USER
, UpdatedOn = CAST(GetDate() AS DATE)
FROM dbo.vReclaimable
WHERE OHR_EmpStatus <> 'A'
Que debería actualizar solo las filas de la tabla tAccts que devuelve la vista vReclaimable.
La vista vReclaimable se basa en la tabla tAccts y devuelve un subconjunto de las filas en tAccts.
Cuando lo ejecuto, falla con un error de clave único:
(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.
Es justo que la tabla tAccts tenga una restricción de clave única:
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)
Pero aquí está lo extraño. Si ejecuto estas dos consultas:
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
El primero devuelve dos filas (como se esperaba):
(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
y el segundo devuelve 0 filas (como se esperaba).
Si “FROM vReclaimable WHERE OHR_EmpStatus <> 'A'” devuelve 0 filas, ¿por qué ACTUALIZACIÓN intenta actualizar la fila donde DNIS = 68497?
(Espero haberlo descrito adecuadamente. Tengo la sensación de que me falta algo obvio)
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
declaración.OHR_EmpStatus
una columna de la tabla, la vista o ambas?Respuestas:
Se reduce a lo
UPDATE
que hace la declaración. No es del todo obvio, pero su declaración es equivalente a esta:Dado que no hay mención de la
dbo.tAccts
tabla en laFROM
condición y no join o where entre la tabla y la vista, resulta en unaCROSS
combinación y un intento de actualizar todas las filas de la tabla (y no solo de la vista), y probablemente múltiples veces también!Puede agregar una condición de unión (o dónde) con:
o (usando su versión):
Alternativamente, puede (probablemente) simplemente actualizar la vista. Para que esto funcione, la vista debe cumplir con las limitaciones sobre "Vistas actualizables" . Ver el apartado correspondiente en la documentación de MSDN:
CREATE VIEW
, vistas actualizables :fuente
parece que no tiene una unión entre las tablas en su consulta de actualización.
aquí debe haber algo que coincida con las filas entre las tablas, como donde tAccts.id = vReclaimable.id
fuente
Otra forma de decir esto:
El problema es que usted cree que la declaración "debería actualizar solo las filas de la tabla tAccts que devuelve la vista vReclaimable".
Ese no es el caso. Actualiza todas las filas de
tAccts
(la tabla mencionada justo despuésUPDATE
) que coincidenOHR_EmpStatus <> 'A'
(la condición en elWHERE
). Puede usar datosvReclaimable
al hacerlo (pero no hace referencia a ellos en absoluto).Si desea restringirlo a las filas que se encuentran
vReclaimable
, además de las otras opciones presentadas, puede usar una subconsulta:fuente
Si la consulta a continuación devuelve más de una fila:
entonces está intentando actualizar varias filas con los mismos valores, por lo tanto, viola la restricción única.
fuente
otra opción es: no necesita el
DE dbo.vReclaimable
porque no utiliza ningún valor de esta tabla en su declaración de actualización.
fuente
vReclaimable
estaba destinada sin duda a filtrar la tabla que se está actualizando. Aunque no es necesario para laSET
cláusula, es efectivamente parte de laWHERE
cláusula.