1

I have a view with a clustered index on it.

sometimes, when I update Order table (using EF Core that uses transaction)

UPDATE dbo.Order SET OrderStatus = @orderStatus
INSERT INTO dbo.History VALUES (....) //no relationship to orders tabe

I get errors like

Cannot insert duplicate key row in object 'MyView' with unique index 'MyClusteredIndex'

Rebuilding he view index does not help. When I drop and recreate the indexed view, the same update works.

Any idea what might be causing it?

I'm using SQL Server 2014 I've upgraded from SQL Server 2014 to 2022 Standard Edition and set compatibility level to latest.


View definition:

CREATE VIEW [manufacture].[MaterialNeededForOrdersByIO]
WITH SCHEMABINDING
AS
    SELECT Order.ID
        OrderedProductMaterials.OrderedProductId, 
        MaterialEnum.MaterialCode, 
        Needed = SUM(IIF(Order.STATUS <> 22 AND ISNULL(OrderedProducts.STATUS, 0) <> 22, ISNULL(OrderedProductMaterials.Quantity * OrderedProducts.Quantity, 0), 0)), 
        NeededAll = SUM(ISNULL(OrderedProductMaterials.Quantity * OrderedProducts.Quantity, 0)),
        IsSHV = IIF(Order.CustomerId = 2472 AND OrderedProducts.ProductionDate IS NULL, 1, 0),
        IsPT = IIF(Order.CustomerId IN(2566, 2662) AND OrderedProducts.ProductionDate IS NULL, 1, 0), 
        OrderStatus = Order.Status,
        IOStatus = OrderedProducts.Status,
        COUNT_ = COUNT_BIG(*)
    FROM dbo.Order
        INNER JOIN dbo.OrderedProducts ON Order.ID = OrderedProducts.IDZákazky
        INNER JOIN dbo.OrderedProductMaterials ON OrderedProducts.ID = OrderedProductMaterials.OrderedProductId 
        INNER JOIN dbo.MaterialEnum ON MaterialEnum.ID = OrderedProductMaterials.MaterialId 
    WHERE        
    --status
    ((Order.STATUS IN(3, 16, 19, 21, 22) AND ISNULL(OrderedProducts.STATUS, 0) = 0)
    OR (Order.STATUS NOT IN(1, 2, 20, 12, 14) AND OrderedProducts.STATUS IN(3, 16, 19, 21, 22))) --end status 

    GROUP BY OrderedProductMaterials.OrderedProductId, 
            MaterialEnum.MaterialCode, 
            Order.ID, 
            IIF(Order.CustomerId = 2472 AND OrderedProducts.ProductionDate IS NULL, 1, 0),
            IIF(Order.CustomerId IN(2566, 2662) AND OrderedProducts.ProductionDate IS NULL, 1, 0),
            Order.Status,
            OrderedProducts.ProductionDate, OrderedProducts.Status
GO

CREATE UNIQUE CLUSTERED INDEX [IX_MaterialNeededForOrdersByIO] ON [manufacture].[MaterialNeededForOrdersByIO]
(
    [MaterialCode] ASC,
    [OrderedProductId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
GO

enter image description here

Liero
  • 25,216
  • 29
  • 151
  • 297
  • 1
    SQL Server 2012 is out of support. If there's a bug, it won't be fixed unless it's already included in the latest Service Pack and CUs. Is the server fully patched? Isn't it time to upgrade to a recent version? The oldest version in mainstream support is SQL Server 2019 - even 2017 went out of mainstream in October 2022 – Panagiotis Kanavos Nov 16 '22 at 12:11
  • 2
    "I assume this is because the index on the view is corrupted.". does `DBCC CHECKTABLE' find any? – Martin Smith Nov 16 '22 at 12:11
  • What's the actual version of SQL Server? What does `SELECT @@VERSION` return? – Panagiotis Kanavos Nov 16 '22 at 12:12
  • @MartinSmith: I've tried DBCC CHECKTABLE on the view, but not on the tables. I will next time – Liero Nov 16 '22 at 12:18
  • @PanagiotisKanavos: yes, I plan to upgrade to 2022, but they are delaying the release. The version is `Microsoft SQL Server 2014 (SP3-GDR) (KB4583463) - 12.0.6164.21 (X64) Nov 1 2020 04:25:14 Copyright (c) Microsoft Corporation Standard Edition (64-bit) on Windows NT 6.3 (Build 9600: ) (Hypervisor) `, so 2014, not 2012. My mistake – Liero Nov 16 '22 at 12:19
  • 2
    SQL Server 2014 is out of support as well. [The latest CU4 for SP3 is missing](https://sqlserverbuilds.blogspot.com/). Is NOLOCK used anywhere? There was a [fix for a similar error](https://support.microsoft.com/en-us/topic/kb2878968-fix-nonclustered-index-corruption-may-occur-when-you-run-a-complex-update-statement-together-with-a-nolock-hint-against-a-table-in-sql-server-e9327f96-eb9b-3f3a-2fc2-9c27d2d8dc86) that was released in CU1. Using NOLOCK is a bug in itself - it doesn't cause things to go fast, it results in dirty reads and duplicate rows and errors. Similar to what you're seeing – Panagiotis Kanavos Nov 16 '22 at 12:32
  • @PanagiotisKanavos: I've scripted the DB schema and haven't found any NOLOCK occurrence – Liero Nov 16 '22 at 13:08
  • "*Cannot insert duplicate key row in object 'MyView' with unique index 'MyClusteredIndex'*" - This isn't necessarily a sign of corruption, and rather a type of error that can occur with internal engine "*bugs*" with Indexed Views. Though your other error is a sign of corruption. – J.D. Nov 17 '22 at 03:51
  • I've upgraded to 2022 and increased compatibility level. Still getting the error – Liero Mar 24 '23 at 13:05
  • There used to be some bugs in the indexed view, perhaps that's another manifestation of them. Your index is kinda weird, shouldn't OrderID be part of the uniquity? – siggemannen Mar 28 '23 at 18:30
  • @siggemannen: could be, but given the relationship, OrderedProductId is always unique for all Orders. – Liero Mar 29 '23 at 06:34
  • Okay. Does it make a difference if you add it? Also, what exactly are you updating? How does it look like. And are you using MERGE operator to update? – siggemannen Mar 29 '23 at 07:39
  • @siggemannen: I've now included the update statement in question. Also, when I recreate the view and index, I can perform the update without problem. – Liero Mar 29 '23 at 07:53
  • Could it be multiple processed doing updates? which messes up the view? That's why it works after rebuilding. Either way, it seems like you have a bug on your hands – siggemannen Mar 29 '23 at 08:28
  • __"Could it be multiple processed doing updates"__ - no, I was able to backup and restore the database in isolated test environment and reproduce the issue – Liero Mar 29 '23 at 08:37

2 Answers2

1

Based on the schema and view definition, it appears that updating the order status adds a new row to the view (likely because it now satisfies the where condition). I believe the issue may be related to the group by clause, as it does not match the unique index definition, potentially allowing duplicated rows in the view.

To address this, try rewriting the view by grouping only by the unique index columns (MaterialCode and OrderedProductId). You can use a MIN aggregate function to retrieve data for the other fields in the original group by clause (OrderId, IsSHV, IsPT, OrderStatus, IOStatus).

Although it may seem that including OrderProductId in the group by clause would automatically make each row unique, there may be lack of foreign key constraints defined on tables that I am not aware of, and therefore, I cannot be certain.

It is possible that with this mismatch between the group by and index definition, the database engine could make incorrect assumptions when managing the index.

  • Unfortunately, MIN/MAX is not allowed in indexed view. Also, when I drop and recreate the view and the index, I'm able to perform the update, so the rows are unique indeed. It seems that either the index is corrupted or there is a bug in SQL Server. – Liero Mar 31 '23 at 11:20
  • You could try split the view. The first view is the indexed view that group data by MaterialCode, OrderProductId and also OrderId (in this case adding all three fields to the index). The second view is a "normal" view that use the first and join order and order products tables to get calculated data. Probably there will be no particular performance degradation. – Roberto Ferraris Mar 31 '23 at 13:22
  • Did you observe the issue on any combination of original order status and new order status. Or only happen when you make an update that change the result of the `where` clause? – Roberto Ferraris Mar 31 '23 at 13:58
  • Splitting the view helped (although without OrderID). I think it only happens when the result of `where` is changed – Liero Apr 04 '23 at 19:03
  • I'm glad it helped. Advanced functionality of SQL Server are very complex so sometimes it's a matter of trying a different approach – Roberto Ferraris Apr 06 '23 at 06:49
0

This error message does not indicate at all a corruption of your index. Index corruptions can be viewed with DBCC CHECKTABLE on the clustered index of the view... But I think you will not find any corruption.

Have you try to UPDATE with the same data from EFcore into SSMS ? I wont be surprise that SSMS will not raise any exception, while RF Core will... Because I suspect that the UPDATE in EFCore is not straight and perhaps do a "replace" that violate the unique key cnstraints of the clustered index...

SQLpro
  • 3,994
  • 1
  • 6
  • 14
  • This is happening outside EF Core as well. I tried to perform the update manually using SSMS. – Liero Apr 02 '23 at 18:54
  • But this is not a corruption. Only the fact that you try to insert values that ar already present in the UNIQUE key of the index view... – SQLpro Apr 02 '23 at 18:59
  • You are probably right, but when I drop and recreate the indexed view, and I execute the same update command, it works. Same data, same view, same index, same update. I suspect it is a bug in SQL Server – Liero Apr 03 '23 at 07:07
  • The 2014 version of SQL Server is old enough that such a bug has already been seen by many users around the world and already fixed by Microsoft. So I do not believe at all in a forgotten SQL Server bug, unless you reproduce this problem in all new versions of SQL Server in 2014 backward compatibility mode – SQLpro Apr 04 '23 at 08:11
  • I've reproduced it in 2022, in both 2014 and latest compatibility modes. – Liero Apr 04 '23 at 10:48