Update same data from the same table - sql

I have a table that has registration of several CAR (repeated and not repeated), so I intend to update a field (active) of all repeated registrations leaving 1 more recent line. Exemple of table data:
I want my data to be like this:
I tried to make this code, but it is not working correctly. Because updated the first row
-----create table
create table dbo.test( id int, CAR varchar(30), ACTIVE int)
insert into dbo.test(id, CAR, ACTIVE)
values
(1, 'AAA-25-35', 0),
(2, 'LDB-25-35', 0),
(3, 'LDB-00-35', 0),
(4, 'LDB-25-35', 0),
(5, 'LDB-00-35', 0),
(6, 'LDC-10-10', 0),
(7, 'LDC-10-10', 0),
(8, 'LDB-00-35', 0)
select * from dbo.test
----update table
WITH CTE AS
(
SELECT
row_number() over (partition by CAR order by id) as t
, CAR , ACTIVE
FROM dbo.test
)
update CTE
SET ACTIVE = 1
WHERE t=1
select * from dbo.test

You could just add an EXISTS portion:
-----create table
CREATE TABLE #Test(ID INT, CAR VARCHAR(30), ACTIVE INT)
INSERT INTO #Test(ID, CAR, ACTIVE)
VALUES
(1, 'AAA-25-35', 0),
(2, 'LDB-25-35', 0),
(3, 'LDB-00-35', 0),
(4, 'LDB-25-35', 0),
(5, 'LDB-00-35', 0),
(6, 'LDC-10-10', 0),
(7, 'LDC-10-10', 0),
(8, 'LDB-00-35', 0)
----update table
;WITH CTE AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY CAR ORDER BY ID) AS t,
CAR,
ACTIVE
FROM #Test
)
UPDATE CTE
SET ACTIVE = 1
WHERE t=1
AND EXISTS (SELECT 1 FROM CTE c WHERE c.CAR = CTE.CAR GROUP BY CAR HAVING COUNT(*) > 1)
SELECT *
FROM #Test

You can use count analytical function in your query as follows:
WITH CTE AS
(
SELECT
row_number() over (partition by CAR order by id) as t
, count(*) over (partition by car) as cnt
, CAR , ACTIVE
FROM dbo.test
)
update CTE
SET ACTIVE = 1
WHERE t=1 and cnt>1

Related

Update same data from the same table depending on a column

I have a similar case of a table, Like the case of this link Update same data from the same table
but in my case it must update depending on the column "dependency". In other words, it updates the repetitions in the tables always leaving the most recent line and for table that only have one line it does not update. My data is like this:
I want it to be updated like this:
I tryed this code:
create table dbo.test( id int, CAR varchar(30), ACTIVE int, dependency int)
insert into dbo.test(id, CAR, ACTIVE, dependency)
values
(1, 'AAA-25-35', 0,1),
(2, 'LDB-25-35', 0,2),
(3, 'LDB-00-35', 0,2),
(4, 'LDB-25-35', 0,2),
(5, 'LDB-00-35', 0,2),
(6, 'LDC-10-10', 0,2),
(7, 'LDC-10-10', 0,2),
(8, 'LDB-00-35', 0,2),
(9, 'AAA-25-35', 0,1),
(10, 'AAA-25-35', 0,3),
(11, 'AAA-25-35', 0,3),
(12, 'BBB-25-35', 0,2),
(13, 'BBB-25-35', 0,3),
(14, 'BBB-25-35', 0,3)
GO
SELECT * FROM TEST
WITH CTE AS
(
SELECT ROW_NUMBER() OVER(PARTITION BY CAR ORDER BY ID) AS t,
CAR,
ACTIVE
FROM Test
)
UPDATE CTE
SET ACTIVE = 1
WHERE t=1
AND EXISTS (SELECT 1 FROM CTE c WHERE c.CAR = CTE.CAR GROUP BY CAR HAVING COUNT(*) > 1)
go
SELECT * FROM test
Try changing the SELECT and WHERE clauses:
WITH CTE AS (
SELECT ROW_NUMBER() OVER(PARTITION BY CAR, dependency ORDER BY ID) AS t,
LEAD(id) OVER (PARTITION BY CAR, dependency ORDER BY ID) as next_id,
CAR,
ACTIVE
FROM Test
)
UPDATE CTE
SET ACTIVE = 1
WHERE t = 1 AND next_id IS NOT NULL

TSQL - Select Latest Date by 2 Type

I need select data in SQL Server.
I have a Data example like this
DECLARE #tblA TABLE (ID int, SubId int, Createdate DateTime)
INSERT INTO #tblA VALUES (1, 1, '10/21/2020')
, (2, 1, '10/21/2020')
, (3, 1, '10/27/2020')
, (4, 2, '10/21/2020')
, (5, 2, '10/21/2020')
, (6, 1, '10/21/2020')
, (7, 2, '10/23/2020')
, (8, 2, '10/22/2020')
, (9, 1, '10/25/2020')
, (10, 3, '10/21/2020')
I want to get 4 records (in table will have a lot of SubId)
2 records latest date by SubId = 1
2 records latest date by SubId = 2
Following example, I expect Select with like table Output
DECLARE #tblOutput Table (ID int, SubId int, Createdate DateTime)
INSERT INTO #tblOutput VALUES (3, 1, '10/27/2020')
, (9, 1, '10/25/2020')
, (7, 2, '10/23/2020')
, (8, 2, '10/22/2020')
"tooltip"
I try with Union but It's only Order after Union. It's not the result I want.
Please, Help me to select this.
Thank you.
You can use the Row_Number() function as shown below:
Create table tblA (ID int, SubId int, Createdate DateTime)
Insert Into tblA values (1, 1, '10/21/2020')
, (2, 1, '10/21/2020')
, (3, 1, '10/27/2020')
, (4, 2, '10/21/2020')
, (5, 2, '10/21/2020')
, (6, 1, '10/21/2020')
, (7, 2, '10/23/2020')
, (8, 2, '10/22/2020')
, (9, 1, '10/25/2020')
, (10, 3, '10/21/2020')
select Id
, SubId
, Createdate
from(
select *
, Row_Number() Over (Partition By SubId order by Createdate desc) as RNo
from tblA
)temp where RNo <= 2 and SubId in (1, 2)
Order by SubId
Live db<>fiddle demo

SQL select distinct data without ordering the column

I have table which contains data as below:
DECLARE #CheckList TABLE
(
RowNumber INT IDENTITY(1,1) INT,
CheckId INT,
Treat INT
)
INSERT INTO #CheckList
VALUES (1, 1, 1), (1, 3, 1), (1, 2, 1), (1, 1, 1),
(1, 3, 1), (1, 2, 1), (1, 1, 3), (1, 3, 3),
(1, 2, 3);
SELECT DISTINCT Id, CheckId
FROM #CheckList
WHERE Id = 1
Where I want to select the data from this table as
Id CheckId
------------
1 1
1 3
1 2
I want the same order of the check id which is available in table. Can you help me with this?
Retain original ordering by RowNumber identity column
select Id,CheckId
from(
select distinct Id,CheckId, min(rownumber) over(partition by Id,CheckId) rn
from #CheckList
where Id=1
) t
order by rn;
Db fiddle
you can check this
DECLARE #CheckList TABLE
( Id INT , CheckId INT, Treat INT )
INSERT INTO #CheckList
VALUES (1, 1, 1), (1, 3, 1), (1, 2, 1), (1, 1, 1),(1, 3, 1), (1, 2, 1), (1, 1, 3),(1,3,3), (1, 2, 3);
SELECT DISTINCT Id, CheckId
FROM #CheckList
WHERE Id = 1

Create range of ID without cursor

Not sure if this or similar question is asked already but i could not find one.
The Requirement to create range of IDs while the Value is not changed. This schema can be used:
declare #mytable as table(ID int, Val int)
insert into #mytable values
(1, 1),
(2, 1),
(3, 1),
(4, 2),
(5, 2),
(6, 2),
(7, 2),
(8, 1),
(9, 1),
(10, 1),
(11, 4),
(12, 4),
(13, 4),
(14, 4),
(15, 4),
(16, 5);
And the expected result would be
StartID EndID Val
1 3 1
4 7 2
8 10 1
11 15 4
16 16 5
Now I can achieve this by running cursor and If n case the number of records will be millions, I think, cursor will be slower. I hope it can be written using some compound query but could not figure-out how.
So I need help in writing that kind of query and needless to mention yet, it is not a school/collage project/assignment.
This is a gaps-and-islands scenario where you're trying to group records together based on the change in Val.
This is using window functions to determine when the Val changes, and assign the island_nbr.
Answer:
select min(b.ID) as StartID
, max(b.ID) as EndID
, max(b.Val) as Val
from (
select a.ID
, a.Val
, sum(a.is_chng_flg) over (order by a.ID asc) as island_nbr
from (
select m.ID
, m.Val
, case lag(m.Val, 1, m.Val) over (order by m.ID asc) when m.Val then 0 else 1 end is_chng_flg
from #mytable as m
) as a
) as b
group by b.island_nbr --forces the right records to show up
order by 1
This is a gaps-and-islands problem. But the simplest method is the difference of row numbers:
select min(id) as startId, max(id) as endId, val
from (select t.*,
row_number() over (order by id) as seqnum,
row_number() over (partition by val order by id) as seqnum_v
from #mytable t
) t
group by (seqnum - seqnum_v), val
order by startId;

SQL recursive logic

I have a situation where I need to configure existing client data to address a problem where our application was not correctly updating IDs in a table when it should have been.
Here's the scenario. We have a parent table, where rows can be inserted that effectively replace existing rows; the replacement can be recursive. We also have a child table, which has a field that points to the parent table. In existing data, the child table could be pointing at rows that have been replaced, and I need to correct that. I can't simply update each row to the replacing row, however, because that row could have been replaced as well, and I need the latest row to be reflected.
I was trying to find a way to write a CTE that would accomplish this for me, but I'm struggling to find a query that finds what I'm actually looking for. Here's a sample of the tables that I'm working with; the 'ShouldBe' column is what I'd like my update query to end up with, taking into account the recursive replacement of some of the rows.
DECLARE #parent TABLE (SampleID int,
SampleIDReplace int,
GroupID char(1))
INSERT INTO #parent (SampleID, SampleIDReplace, GroupID)
VALUES (1, -1, 'A'), (2, 1, 'A'), (3, -1, 'A'),
(4, -1, 'A'), (5, 4, 'A'), (6, 5, 'A'),
(7, -1, 'B'), (8, 7, 'B'), (9, 8, 'B')
DECLARE #child TABLE (ChildID int, ParentID int)
INSERT INTO #child (ChildID, ParentID)
VALUES (1, 4), (2, 7), (3, 1), (4, 3)
Desired results in child table, after the update script has been applied:
ChildID ParentID ParentID_ShouldBe
1 4 6 (4 replaced by 5, 5 replaced by 6)
2 7 9 (7 replaced by 8, 8 replaced by 9)
3 1 2 (1 replaced by 2)
4 3 3 (unchanged, never replaced)
The following returns what you are looking for:
with cte as (
select sampleid, sampleidreplace, 1 as num
from #parent
where sampleidreplace <> -1
union all
select p.sampleid, cte.sampleidreplace, cte.num+1
from #parent p join
cte
on p.sampleidreplace = cte.sampleId
)
select c.*, coalesce(p.sampleid, c.parentid)
from #child c left outer join
(select ROW_NUMBER() over (partition by sampleidreplace order by num desc) as seqnum, *
from cte
) p
on c.ParentID = p.SampleIDReplace and p.seqnum = 1
The recursive part keeps track of every correspondence (4-->5, 4-->6). The addition number is a "generation" count. We actually want the last generation. This is identified by using the row_number() function, ordering by the num in decreasing order -- hence the p.seqnum = 1.
Ok, so it took me a while and there are probably better ways to do it, but here is one option.
DECLARE #parent TABLE (SampleID int,
SampleIDReplace int,
GroupID char(1))
INSERT INTO #parent (SampleID, SampleIDReplace, GroupID)
VALUES (1, -1, 'A'), (2, 1, 'A'), (3, -1, 'A'),
(4, -1, 'A'), (5, 4, 'A'), (6, 5, 'A'),
(7, -1, 'B'), (8, 7, 'B'), (9, 8, 'B')
DECLARE #child TABLE (ChildID int, ParentID int)
INSERT INTO #child (ChildID, ParentID)
VALUES (1, 4), (2, 7), (3, 1), (4, 3)
;WITH RecursiveParent1 AS
(
SELECT SampleIDReplace, SampleID, 1 RecursionLevel
FROM #parent
WHERE SampleIDReplace != -1
UNION ALL
SELECT A.SampleIDReplace, B.SampleID, RecursionLevel + 1
FROM RecursiveParent1 A
INNER JOIN #parent B
ON A.SampleId = B.SampleIDReplace
),RecursiveParent2 AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY SampleIdReplace ORDER BY RecursionLevel DESC) RN
FROM RecursiveParent1
)
SELECT A.ChildID, ISNULL(B.ParentID,A.ParentID) ParentID
FROM #child A
LEFT JOIN ( SELECT SampleIDReplace, SampleID ParentID
FROM RecursiveParent2
WHERE RN = 1) B
ON A.ParentID = B.SampleIDReplace
OPTION(MAXRECURSION 500)
I've got a iterative SQL loop that I think sorts this out as follows:
WHILE EXISTS (SELECT * FROM #child C INNER JOIN #parent P ON C.ParentID = P.SampleIDReplace WHERE P.SampleIDReplace > -1)
BEGIN
UPDATE #child
SET ParentID = SampleID
FROM #parent
WHERE #child.ParentID = SampleIDReplace
END
Basically, the while condition compares the contents of the parent ID column in the child table and sees if there is a matching value in the SampleIDReplace column of the parent table. If there is, it goes and gets the SampleID of that record. It only stops when the join results in every SampleIDReplace being -1, meaning we have nothing else to do.
On your sample data, the above results in the expected output.
Note that I had to use temp tables rather than table variables here in order for the table to be accessible within the loop. If you have to use table variables then there would need to be a bit more surgery done.
Clearly if you have deep replacement hierarchies then you'll do quite a few updates, which may be a consideration when looking to perform the query against a production database.