Home > Software engineering >  Get multiple records between particular start and end point in the table
Get multiple records between particular start and end point in the table

Time:08-02

I have table data like below

enter image description here

In the given table, some records are outdated data that is generated by a bug in a system.

I want to remove all the outdated records

Correct data condition: if itemStatus rows start and end from status 4 and 6 without 13 or 14 status then it is correct. I do not need to delete that row. Example: Id 18 to 20 that are correct data. Note: Id 21 and 22 is correct because status 13,14 start after status 6 (id 20)

Incorrect data conditions: If itemStatus row starts and ends from status 4 and 6, with 13 and 14 status. Then it is incorrect data and I have to delete that from a DB.

Example: 24 and 25 then 29 and 30

My query

IF OBJECT_ID('tempdb..#TempResult') IS NOT NULL DROP TABLE #TempResult

CREATE TABLE #TempResult (
    Id int 
)

select ItemId
into #TempItemGroup
from Item
group by itemid

declare @SelectedItemId int

while exists (select ItemId from #TempItemGroup)
begin
    select @SelectedItemId = (select top 1  ItemId
                        from #TempItemGroup
                        order by ItemId asc
                        )
    IF OBJECT_ID('tempdb..#TempSingleItemGroup') IS NOT NULL DROP TABLE #TempSingleItemGroup

    SELECT i1.*
        into #TempSingleItemGroup
        FROM Item i1
        WHERE i1.[Id] >= ( SELECT TOP 1 [Id] FROM Item
                             WHERE [ItemStatus] = 4 and ItemId = @SelectedItemId )
         AND i1.[Id] <= ( SELECT TOP 1 [Id] FROM Item
                             WHERE [ItemStatus] = 6 and ItemId = @SelectedItemId  )

        INSERT INTO #TempResult (Id) (SELECT Id FROM #TempSingleItemGroup where ItemStatus = 13 or ItemStatus = 14)

    IF OBJECT_ID('tempdb..#TempSingleItemGroup') IS NOT NULL DROP TABLE #TempSingleItemGroup

    delete #TempItemGroup
    where ItemId = @SelectedItemId
end

-- Delete or do other operation if required
IF(EXISTS(SELECT count(Id) FROM #TempResult))
BEGIN
    -- write a query to delete the data
      select * from #TempResult
END


IF OBJECT_ID('tempdb..#TempResult') IS NOT NULL DROP TABLE #TempResult
IF OBJECT_ID('tempdb..#TempItemGroup') IS NOT NULL DROP TABLE #TempItemGroup

Expected result There are some differences between screenshot and actual table data that you can find at the end of this question

Screenshot: 24,25,29,30 Table data: 13,14,15 ,26,27, 31,32

I have two queries

  1. My above query is only getting first status which have 4 and 6 status, example Id 24 and 25, and I am unable to get next wrong data for 29 and 30
  2. I am using while loop that may not a good practices. Please suggest me a better way to write a query.

Table schema and data

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Item](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [ItemId] [int] NOT NULL,
    [ItemStatus] [int] NOT NULL,
 CONSTRAINT [PK_Item] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
SET IDENTITY_INSERT [dbo].[Item] ON 
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (1, 23, 2)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (2, 23, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (3, 23, 7)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (4, 23, 6)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (5, 23, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (6, 23, 14)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (7, 23, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (8, 23, 6)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (9, 23, 3)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (10, 45, 2)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (11, 45, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (12, 45, 7)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (13, 45, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (14, 45, 14)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (15, 45, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (16, 45, 6)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (17, 45, 3)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (18, 25, 2)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (19, 25, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (20, 25, 7)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (21, 25, 6)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (23, 25, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (24, 25, 14)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (25, 25, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (26, 25, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (27, 25, 14)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (28, 25, 6)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (29, 25, 3)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (30, 25, 4)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (31, 25, 13)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (32, 25, 14)
GO
INSERT [dbo].[Item] ([Id], [ItemId], [ItemStatus]) VALUES (33, 24, 6)
GO
SET IDENTITY_INSERT [dbo].[Item] OFF
GO

CodePudding user response:

My reading of your requirement is this:

For each itemId, find rows with itemStatus in (13, 14) that lie between a row with the same itemId and itemStatus 4, and a row with the same itemid and itemStatus 6, in order of id ascending.

Your current requirements do not state whether we should return rows with item status in (13, 14) where there is a prior 4 row, but no subsequent 6 row (the "end" of the itemid group is reached), or what to do if there is a subsequent 6 row, but no prior 4 row (the "start" of the itemid group is reached).

I make the following interpretation: itemStatus 4 opens a "status block". itemStatus 6 closes a "status block". There should be no rows with status 13 or 14 in an open block.

If that is a correct interpretation, all we really need to do is find rows with item status 13 or 14, and then find the nearest prior row in order of id descending with a status of either 4 or 6. If the status of that prior row is 4 then the rows are in an open block. If the status of that prior row is 6, or no prior row can be found, then the rows are not in an open block.

Note: In the comments you mentioned expected results including id 31 and 32. But your sample data does not include any row with id 31 or 32. You have now provided these rows and I have added them to my data.


create table #items
(
   Id         int primary key,
   ItemId     int not null,
   ItemStatus int not null,
);

insert #items values
(2, 23, 4),
(3, 23, 7),
(4, 23, 6),
(5, 23, 13),
(6, 23, 14),
(7, 23, 4),
(8, 23, 6),
(9, 23, 3),
(10, 45, 2),
(11, 45, 4),
(12, 45, 7),
(13, 45, 13),
(14, 45, 14),
(15, 45, 13),
(16, 45, 6),
(17, 45, 3),
(18, 25, 2),
(19, 25, 4),
(20, 25, 7),
(21, 25, 6),
(23, 25, 13),
(24, 25, 14),
(25, 25, 4),
(26, 25, 13),
(27, 25, 14),
(28, 25, 6),
(29, 25, 3),
(30, 25, 4),
(31, 25, 13),
(32, 25, 14),
(33, 24, 6);

select      item13or14.*
from        #items item13or14
cross apply (
               select   top 1 itemStatus
               from     #items
               where    itemId = item13or14.itemId
                        and id < item13or14.id
                        and itemStatus in (4, 6)
               order by id desc
            ) prior4or6
where       item13or14.itemStatus in (13, 14)
            and prior4or6.itemStatus = 4;

/* produces:

Id    ItemId   ItemStatus
13    45       13
14    45       14
15    45       13
26    25       13
27    25       14
31    25       13
32    25       14

*/

  • Related