Update sequence based on pattern SQL Server - sql-server
I was not able to quote correct tile for the question.
Below is my table.
Expected Output : With ID and SequenceNo column. Was unable to upload image.
ID Act SequenceNo1
1 1 1
2 2 NULL
3 3 NULL
4 4 NULL
5 1 5
6 2 NULL
7 3 NULL
8 4 NULL
9 5 NULL
10 6 NULL
11 5 11
12 6 NULL
13 1 13
14 2 NULL
15 3 NULL
16 4 NULL
17 5 NULL
18 6 NULL
19 1 19
20 2 NULL
21 3 NULL
22 4 NULL
23 5 NULL
24 6 NULL
At the start last column SequenceNo is NULL.`
My requirement is to update value of ID column to SequenceNo column whenever new series of Act column is started.
Act column has value of 1 to 6. There can be case where any number from 1 to 6 is missing from Act.
Example1 : ID 1 to 4 - Act is correct but, in next row (ID=5) Act is restarted. Hence need to update SequenceNo column.
Example2 : ID 5 to 10 are correct. But next row (ID=11;Act=5) has new sequence hence need to update SequenceNo column.
CREATE TABLE #tmp
(
ID int
, ScheduleID varchar(50)
,DCNumber VARCHAR(50)
, BuildingID varchar(10)
, StoreNumber int
, [DayOfWeek] int
, [Tm] varchar(10)
,[Act] int
, SequenceNo int
)
INSERT INTO #tmp SELECT 1,'WAS',9003,900301,254,1,'00:00',1,NULL
INSERT INTO #tmp SELECT 2,'WAS',9003,900301,254,1,'00:00',2,NULL
INSERT INTO #tmp SELECT 3,'WAS',9003,900301,254,1,'00:00',3,NULL
INSERT INTO #tmp SELECT 4,'WAS',9003,900301,254,1,'00:00',4,NULL
INSERT INTO #tmp SELECT 5,'WAS',9003,900301,254,2,'00:00',1,NULL
INSERT INTO #tmp SELECT 6,'WAS',9003,900301,254,2,'00:00',2,NULL
INSERT INTO #tmp SELECT 7,'WAS',9003,900301,254,2,'00:00',3,NULL
INSERT INTO #tmp SELECT 8,'WAS',9003,900301,254,2,'00:00',4,NULL
INSERT INTO #tmp SELECT 9,'WAS',9003,900301,254,2,'00:00',5,NULL
INSERT INTO #tmp SELECT 10,'WAS',9003,900301,254,2,'00:00',6,NULL
INSERT INTO #tmp SELECT 11,'WAS',9003,900301,254,3,'00:00',5,NULL
INSERT INTO #tmp SELECT 12,'WAS',9003,900301,254,3,'00:00',6,NULL
INSERT INTO #tmp SELECT 13,'WAS',9003,900301,254,4,'00:00',1,NULL
INSERT INTO #tmp SELECT 14,'WAS',9003,900301,254,4,'00:00',2,NULL
INSERT INTO #tmp SELECT 15,'WAS',9003,900301,254,4,'00:00',3,NULL
INSERT INTO #tmp SELECT 16,'WAS',9003,900301,254,4,'00:00',4,NULL
INSERT INTO #tmp SELECT 17,'WAS',9003,900301,254,5,'00:00',5,NULL
INSERT INTO #tmp SELECT 18,'WAS',9003,900301,254,5,'00:00',6,NULL
INSERT INTO #tmp SELECT 19,'WAS',9003,900301,254,6,'00:00',1,NULL
INSERT INTO #tmp SELECT 20,'WAS',9003,900301,254,6,'00:00',2,NULL
INSERT INTO #tmp SELECT 21,'WAS',9003,900301,254,6,'00:00',3,NULL
INSERT INTO #tmp SELECT 22,'WAS',9003,900301,254,6,'00:00',4,NULL
INSERT INTO #tmp SELECT 23,'WAS',9003,900301,254,7,'00:00',5,NULL
INSERT INTO #tmp SELECT 24,'WAS',9003,900301,254,7,'00:00',6,NULL
I have build one logic but that is time consuming.
DECLARE #Act INT, #iStart INT, #iMax INT, #iFirst INT
SET #iFirst = 7
SET #iMax = (SELECT MAX(ID) FROM #tmp)
SET #iStart = 1
WHILE(#iStart <= #iMax)
BEGIN
SET #Act = (SELECT Act FROM #tmp WHERE ID = #iStart)
IF(#iFirst > #Act )
BEGIN
UPDATE #tmp SET SequenceNo = #iStart WHERE ID = #iStart
END
SET #iFirst = #Act
SET #iStart = #iStart + 1
END
I am looking out for any alternative optimized solution.
Is this what you wanted?
update tupd
set
SequenceNo = tupd.ID
from (
select
*
, LAG(t.Act, 1, null) over(order by t.ID) as LagAct
from #tmp t
) tt
inner join #tmp tupd on tt.ID = tupd.ID
where tt.Act < tt.LagAct
select
*
from #tmp tt
order by tt.ID
You can find breaks in the Act sequence if you compare the current row with the previous. In current SQL Server versions, ie 2012+ you can do that with the LAG() method. In previous versions, you have to perform a self join on the current ID value and its previous value. If the difference between the current and previous Act values isn't 1, there is a break in the sequence. This only works if there are no gaps in ID.
This query will return 1 in the SeqBreak column for each row where Act restarts
select
t1.ID,
t1.SequenceNo ,
case when t1.act = t2.act+1 then '0' else '1' end as SeqBreak
from #tmp t1 left join #tmp t2 on t1.id=t2.id+1
You can use this in a CTE to select only the rows where Act breaks. You can update the CTE directly, at least in SQL Server 2014.
with x as (
select
t1.ID,
t1.SequenceNo ,
case when t1.act = t2.act+1 then '0' else '1' end as SeqBreak
from #tmp t1 left join #tmp t2 on t1.id=t2.id+1 )
update x
set SequenceNo=x.ID
where seqbreak=1 and id>1
If that doesn't work with SQL Server 2008, you'll have to join between the cte and the table:
with x as (
select
t1.ID,
t1.SequenceNo ,
case when t1.act = t2.act+1 then '0' else '1' end as SeqBreak
from #tmp t1 left join #tmp t2 on t1.id=t2.id+1 )
update #tmp
set SequenceNo=x.ID
from #tmp inner join x on x.ID=#tmp.ID
where seqbreak=1 and #tmp.id>1
The equivalent query using LAG(), one of the windowing functions in SQL Server 2012 is simpler and twice as fast, since it avoids the self join:
with x as (
select
ID,
SequenceNo ,
case when act = 1 +LAG(act,1) OVER (ORDER BY ID) then '0' else '1' end as SeqBreak
from #tmp)
update x
set SequenceNo=x.ID
where seqbreak=1 and id>1
;WITH CTE
AS
(
SELECT ID,Act,RANK() OVER (PARTITION BY [DayOfWeek] ORDER BY Act) RN
FROM #tmp
)
UPDATE t
SET t.SequenceNo = t.id
FROM #tmp t
inner join CTE C
ON t.ID = C.ID
WHERE C.RN = 1
If I'm getting your question correctly below query should fetch the desired result.
update t3 set sequenceno = t3.id
from
#temp t1
cross apply (select id+1 as idsum
from #temp
tmp where t1.id = tmp.id)t2
inner join #temp t3
on t3.id = t2.idsum
where t1.act>t3.act
Related
T-SQL Group by multiple batches of 2
So the wonderful people here on stackoverflow helped me with a "find consecutive failures" type query. (Status =4 is a failure). I thought I had cracked the second part of my problem because my test case seems to work fine but whenever I run it on our test environment I get dodgy results, so I must be doing something wrong. The goal is to find X number of consecutive failures. So the below is set to find 2 consecutive failures. I'm using SQL Server 2008 R2 DECLARE #t TABLE ( [InstructionId] INT, [InstructionDetailId] INT, [Sequence] INT, [Status] INT ) INSERT INTO #t SELECT 222,111,1, 2 INSERT INTO #t SELECT 222,112,2,2 INSERT INTO #t SELECT 222,113,3,4 INSERT INTO #t SELECT 222,114,4,4 INSERT INTO #t SELECT 222,115,5,2 INSERT INTO #t SELECT 222,116,6,4 INSERT INTO #t SELECT 222,117,7,2 INSERT INTO #t SELECT 222,118,8,4 INSERT INTO #t SELECT 222,119,9,4 INSERT INTO #t SELECT 222,120,10,2 INSERT INTO #t SELECT 222,121,11,2 INSERT INTO #t SELECT 222,124,12,4 INSERT INTO #t SELECT 222,126,13,4 INSERT INTO #t SELECT 222,128,14,4 INSERT INTO #t SELECT 223,126,13,4 INSERT INTO #t SELECT 223,128,14,4 INSERT INTO #t SELECT 223,129,15,2 INSERT INTO #t SELECT 223,130,16,4 INSERT INTO #t SELECT 224,111,17,4 INSERT INTO #t SELECT 224,112,18,4 INSERT INTO #t SELECT 223,160,33,4 INSERT INTO #t SELECT 223,161,34,4 INSERT INTO #t SELECT 223,162,35,4 INSERT INTO #t SELECT 223,163,40,4 ;with HardcoreCTE AS ( select t.*, t.[Sequence] - ROW_NUMBER() OVER(PARTITION BY t.instructionId ORDER BY t.InstructionDetailId) AS ItemCount from #t t outer apply ( select top (1) t1.* from #t t1 where t1.InstructionId = t.InstructionId and t1.Sequence < t.Sequence order by t1.Sequence desc ) t1 outer apply ( select top (1) t2.* from #t t2 where t2.InstructionId = t.InstructionId and t2.Sequence > t.Sequence order by t2.Sequence ) t2 where t.status = 4 and (t.status = t1.status or t.status = t2.status) ) , HardCoreCTE2 AS ( select *, Count(1) OVER(PARTITION BY ItemCount) AS ItemCount2 from HardcoreCTE ) select * from HardCoreCTE2 where ItemCount2 =2 So the above works brilliants to find results where there are specifically only 2 consecutive failures with these results: Now from the above results the only ones it finds are the records where there are 2 consecutive failures but whenever I convert the above to the actual test environment tables it doesn't seem to work. Test Env Results: As you can see for the "InstructionId" of 2518380 it brought back one record and the for "InstructionId" 2614351. It's meant to bring back sets of 2 records. Test Env Query: (Pretty much identical) ;with InitialDataCTE AS ( SELECT Instruction.InstructionID,InstructionDetail.InstructionDetailID, InstructionDetail.InstructionDetailStatusID AS [Status], InstructionDetail.Sequence FROM Instruction INNER JOIN InstructionDetail ON Instruction.InstructionID = InstructionDetail.InstructionID where InstructionDetailStatusID =4 and InstructionDetail.PaymentDateOriginal between '2015-01-05' AND '2018-09-08' ), HardCoreCTE AS ( select t.*, t.Sequence - ROW_NUMBER() OVER(PARTITION BY t.instructionId ORDER BY t.InstructionDetailId) AS ItemCount from InitialDataCTE t outer apply ( select top (1) t1.* from InitialDataCTE t1 where t1.InstructionId = t.InstructionID and t1.Sequence < t.Sequence order by t1.Sequence desc ) t1 outer apply ( select top (1) t2.* from InitialDataCTE t2 where t2.InstructionId = t.InstructionId and t2.Sequence > t.Sequence order by t2.Sequence ) t2 where t.Status = 4 and (t.Status = t1.Status or t.Status = t2.Status) ) , HardCoreCTE2 AS ( select *, Count(1) OVER(PARTITION BY ItemCount) AS ItemCount2 from HardCoreCTE ) select * from HardCoreCTE2 where ItemCount2 =2 order by InstructionID, Sequence Really appreciate if someone can tell me where I am going wrong, I've been messing around with variations of the Count(*) but nothing successful yet. Thanx alot
I came to the next query: with a as ( select *, row_number() over(partition by InstructionId order by Sequence)- row_number() over(partition by InstructionId, [Status] order by Sequence) g from #t ), b as ( select *, count(*) over(partition by InstructionId, [Status], g) c from a where [Status] = 4 ) select * from b where c > 2 order by 1, 3; For your test data, I got the following result: InstructionId InstructionDetailId Sequence Status g c 222 224 312 4 6 3 222 226 413 4 6 3 222 228 514 4 6 3 223 161 84 4 2 3 223 162 95 4 2 3 223 163 140 4 2 3 You can test this query here.
Case-when w.r.to Count in sql server
I am getting error in the below sql query. if count is >1 i need to execute when statement, if not else statement. SELECT CASE WHEN (COUNT(VALUE) FROM TABLE1 WHERE ID=111)>1 ) THEN SELECT VALUE FROM TABLE2 WHERE ID=111 ELSE SELECT 2 Please help
Try this: if (select count(*) from table1 where id = 111 group by id) > 1 select value from table2 where id = 111 else select 2 Demo The same thing, written using case when... select case when (select count(*) from table1 where id = 111 group by id) > 1 then value else 2 end from table2 where id = 111
Try this sample code DECLARE #Table1 TABLE ( id INT, value INT ) INSERT #Table1 VALUES(1, 10) INSERT #Table1 VALUES(2, 20) DECLARE #TABLE2 TABLE ( id INT, c2 INT ) INSERT #TABLE2 VALUES(1, 11 ) INSERT #TABLE2 VALUES(2, 22 ) SELECT CASE WHEN (SELECT count(value) FROM #Table1) > 0 THEN (SELECT T2.c2 FROM #TABLE2 T2 WHERE T1.id = T2.id) ELSE (SELECT T2.id FROM #TABLE2 T2 WHERE T1.id = T2.id) END FROM #Table1 t1
IF EXISTS(SELECT TOP 1 1 FROM TABLE1 WHERE ID='111' HAVING COUNT(VALUE)>1) BEGIN SELECT VALUE FROM TABLE2 WHERE ID=111 END ELSE BEGIN SELECT 2 END
updating a record based on parent id
i have a table intProductID vchProductName intParentCategory intCategoryId 1 Post Cards NULL 3 2 Packaging Boxe NULL 5 3 12- Page Booklets 1 NULL 4 16- Page Booklets 12 NULL i want to update intcategory id of which rows which have intcategory id is null also i want to update intCategoryId with a value which its parent (intParentCategory) have. for example intproductid 3 have intparentid 1 so i want intcategoryid 3 for intproductid 3 which its parent have.
update t1 set intcategoryID = t2.intCategoryId from <table> t1 join <table> t2 on t1.intParentCategory = t2.intProductID where t1.intCategoryId is null Here is a solution with test table that will update the entire tree for parent hierarchies declare #t table(intProductID int, vchProductName varchar(20), intParentCategory int, intCategoryId int) insert #t values(1, 'Post Cards',NULL,3), (2,'Packaging Boxe', NULL,5), (3,'12- Page Booklets', 1,NULL), (4,'16- Page Booklets',12, NULL), (5,'tst', 3, null) --select intCategoryId, intProductID --from #t where intCategoryId is not null and intProductID is not null ;with cte as ( select intCategoryId, intProductID from #t where intCategoryId is not null and intProductID is not null union all select cte.intCategoryId, t.intProductID from #t t join cte on t.intParentCategory = cte.intProductID and t.intCategoryId is null ) update t set t.intCategoryId = cte.intCategoryId from #t t join cte on t.intProductID = cte.intProductID option (maxrecursion 5000) select * from #t
SQL Server CTE -Find top parentID forEach childID?
I have a table which contains hierarchy data - something like: childID | parentID ____________________ 1 | 5 5 | 9 9 | 20 2 | 4 3 | 7 7 | 8 8 | 8 20 | 20 4 | 4 8 | 8 desired output: I've created a recursive CTE which finds me the top fatherID. Something like: ;WITH cte AS ( SELECT a.childID ,a.parentID ,1 AS lvl FROM [Agent_Agents] a WHERE a.childID = 214 //<==== value to begin with !! - thats part the problem UNION ALL SELECT tmp.childID ,tmp.parentID ,cte.lvl+1 FROM [Agent_Agents] tmp INNER JOIN cte ON tmp.childID = cte.parentID WHERE cte.childID<>cte.parentID ) SELECT * FROM cte WHERE lvl = ( SELECT MAX(lvl) FROM cte ) The problem: I executed the CTE with explicit childID value to begin with (214) ! So it gives me the value for 214 only. the CTE do the recursive part and find topParent for childID. but I want ForEach row in the Table - to execute the CTE with the childID value ! I have tried to do it with CROSS APPLY: Something like: select * from myTable Cross Apply ( ;WITH cte AS (....) ) but IMHO (from my testing !!) - its impossible. The other idea of putting the recursive CTE in a UDF has a performance penalty (udf's problem as we know). How can I create this query so that it'll actually work? ( or some near solution )? here is what I've tried https://data.stackexchange.com/stackoverflow/query/edit/69458
Can't you do something like this? ;WITH cte AS (....) SELECT * FROM cte CROSS APPLY dbo.myTable tbl ON cte.XXX = tbl.XXX Put the CROSS APPLY after the CTE definition - into the one SQL statement that refers back to the CTE. Wouldn't that work?? OR: - flip around your logic - do a "top-down" CTE, that picks the top-level nodes first, and then iterates through the hiearchy. This way, you can easily determine the "top-level father" in the first part of the recursive CTE - something like this: ;WITH ChildParent AS ( SELECT ID, ParentID = ISNULL(ParentID, -1), SomeName, PLevel = 1, -- defines level, 1 = TOP, 2 = immediate child nodes etc. TopLevelFather = ID -- define "top-level" parent node FROM dbo.[Agent_Agents] WHERE ParentID IS NULL UNION ALL SELECT a.ID, ParentID = ISNULL(a.ParentID, -1), a.SomeName, PLevel = cp.PLevel + 1, cp.TopLevelFather -- keep selecting the same value for all child nodes FROM dbo.[Agent_Agents] a INNER JOIN ChildParent cp ON r.ParentID = cp.ID ) SELECT ID, ParentID, SomeName, PLevel, TopLevelFather FROM ChildParent This would give you nodes something like this (based on your sample data, slightly extended): ID ParentID SomeName PLevel TopLevelFather 20 -1 Top#20 1 20 4 -1 TOP#4 1 4 8 -1 TOP#8 1 8 7 8 ChildID = 7 2 8 3 7 ChildID = 3 3 8 2 4 ChildID = 2 2 4 9 20 ChildID = 9 2 20 5 9 ChildID = 5 3 20 1 5 ChildID = 1 4 20 Now if you select a particular child node from this CTE output, you'll always get all the infos you need - including the "level" of the child, and its top-level parent node.
Not sure I understand what you are looking for but it could be this. ;WITH c AS (SELECT childid, parentid, parentid AS topParentID FROM #myTable WHERE childid = parentid UNION ALL SELECT T.childid, T.parentid, c.topparentid FROM #myTable AS T INNER JOIN c ON T.parentid = c.childid WHERE T.childid <> T.parentid) SELECT childid, topparentid FROM c ORDER BY childid SE-Data It is the same as answer by marc_s with the difference that I use your table variable and the fact that you have childID = parentID for root nodes where the answer by marc_s has parent_ID = null for root nodes. In my opinion it is better to have parent_ID = null for root nodes.
I have not yet the time to look further into your question and am not sure whether or not i've understood your problem, but couldn't you use this svf to get the top father's id? CREATE FUNCTION [dbo].[getTopParent] ( #ChildID INT ) RETURNS int AS BEGIN DECLARE #result int; DECLARE #ParentID int; SET #ParentID=( SELECT ParentID FROM ChildParent WHERE ChildID = #ChildID ) IF(#ParentID IS NULL) SET #result = #ChildID ELSE SET #result = [dbo].[getTopParent](#ParentID) RETURN #result END Then you should be able to find each top parent in this way: SELECT ChildID , [dbo].[getTopParent](ChildID) AS TopParentID FROM ChildParent
select distinct a.ChildID,a.ParentID, --isnull(nullif(c.parentID,b.parentID),a.parentID) as toppa, B.parentID --,c.parentID ,isnull(nullif(d.parentID,a.parentID),c.parentID) as toppa1,a.name from myTable a inner join myTable c on a.parentID=c.parentID inner join myTable b on b.childID=a.parentID inner join myTable d on d.childID=b.parentID I have using the without CTE expression and then using joins to get the step to step parent for child and then more important Common table expressions were introduced in SQL Server 2005 not in server 2000 so using joins to get values this is basic way for to get parentid for a child value
select dbo.[fn_getIMCatPath](8) select Cat_id,Cat_name,dbo.[fn_getIMCatPath](cat_id) from im_category_master Create FUNCTION [dbo].[fn_getIMCatPath] (#ID INT) returns NVARCHAR(1000) AS BEGIN DECLARE #Return NVARCHAR(1000), #parentID INT, #iCount INT SET #iCount = 0 SELECT #Return = Cat_name, #parentID = parent_id FROM im_category_master WHERE [cat_id] = #ID WHILE #parentID IS NOT NULL BEGIN SELECT #Return = cat_name + '>' + #Return, #parentID = parent_id FROM im_category_master WHERE [cat_id] = #parentID SET #iCount = #iCount + 1 IF #parentID = -1 BEGIN SET #parentID = NULL END IF #iCount > 10 BEGIN SET #parentID = NULL SET #Return = '' END END RETURN #Return END
Consider this sample data and respective SQL to access child records along with their top parent. Sample DATA SQL code: ;WITH c AS ( SELECT Id, Name, ParentId as CategoryId, Id as MainCategoryId, Name AS MainCategory FROM pmsItemCategory WHERE ParentId is null UNION ALL SELECT T.Id, T.Name, T.ParentId, MainCategoryId, MainCategory FROM pmsItemCategory AS T INNER JOIN c ON T.ParentId = c.Id WHERE T.ParentId is not null ) SELECT Id, Name, CategoryId, MainCategoryId, MainCategory FROM c order by Id
select distinct a.ChildID,a.ParentID, --isnull(nullif(c.parentID,b.parentID),a.parentID) as toppa, B.parentID --,c.parentID ,isnull(nullif(d.parentID,a.parentID),c.parentID) as toppa1,a.name from myTable a inner join myTable c on a.parentID=c.parentID inner join myTable b on b.childID=a.parentID inner join myTable d on d.childID=b.parentID
With cte as ( Select ChileId,Name,ParentId from tblHerarchy where ParentId is null union ALL Select h.ChileId,h.Name,h.ParentId from cte inner join tblHerarchy h on h.ParentId=cte.ChileId ) Select * from cte
With cteherarchy as ( Select ChileId,Name,ParentId from tblHerarchy where ParentId is null union ALL Select h.ChileId,h.Name,h.ParentId from cte inner join tblHerarchy h on h.ParentId=cte.ChileId ) Select * from cteherarchy
Check if table does not have records then insert 3 records in table
I want to Check if table does not have records then only insert 3 records in table for sql server as well as oracle.
This sort of construct will work in SQL Server and Oracle: SQL> insert into t34 2 select * from emp where id <= 3 3 and 0 in ( select count(*) from t34 ) 4 / 3 rows created. SQL> r 1 insert into t34 2 select * from emp where rownum <= 3 3* and 0 in ( select count(*) from t34 ) 0 rows created. SQL> But whether it can solve your problem really depends on the source of your three rows.
what was the problem with doing this .. try from your part.. int count = select count(*) from table-name; if(count==0){ //your insert statements as many as insert into table-name values(); }
You just need to do something like this: IF (Select count(*) from tablename) = 0 BEGIN -- INSERT VALUES END
if you want to insert values in a single insert sentence you can do like this: insert into yourTable (yourFields) select value1 as yourField1, ... where (select count(*) from yourTable ) =0 Testing: create table #t (k int); insert into #t select 1 where (select count(*) from #t ) =0 insert into #t select 2 as k; insert into #t select 3 where (select count(*) from #t ) =0 select * from #t; Notice that only '1' and '2' are inserted, not '3'.