SQL: Update a field looping on unique values of another field - sql

I've a table with two fileds. I want to update ID_ELEM with a counter for each unique value of ID_TASS.
This should be the correct output table:
ID_TASS, ID_ELEM
1 , POL_1
1 , POL_2
1 , POL_3
2 , POL_1
2 , POL_2
3 , POL_1
4 , POL_1
4 , POL_2
4 , POL_3
4 , POL_4
Please, any help is welcome! Thanks

I'm at all sure if it will work:
UPDATE tbl
SET ID_ELEM = rn
FROM
( SELECT ctid AS id
, ROW_NUMBER() OVER (PARTITION BY ID_TASS) AS rn
FROM tbl
) AS tmp
WHERE tbl.ctid = tmp.ctid
or maybe this:
UPDATE tbl
SET ID_ELEM = ROW_NUMBER() OVER (PARTITION BY ID_TASS)

very ugly but works on mysql
SET #rank:=0;
update t1, (select id_tass, count(*) as c
from t1
group by id_tass) T
set
t1.t1.id_elem = case
when #rank >= T.c then
#rank:=0
else #rank
end,
t1.id_elem = #rank:=#rank+1
where t1.id_tass = T.id_tass

If you want;
ID_TASS ID_ELEM
1 3
1 3
2 2
2 2
3 1
4 4
4 4
4 4
4 4
1 3
You can (t-sql);
update tbl
set ID_ELEM = (select COUNT(ID_TASS) from tbl where tbl.ID_TASS = T.ID_TASS)
from tbl T

Related

SQL Server : how to write the correct query?

This is my query
SELECT
ROW_NUMBER() OVER (ORDER BY [OtpInfoId] ASC ) AS RowNumber,
[OtpInfoId] ,
[OtpStatusId]
INTO
#TempTable
FROM
RequestOTP.Main.TbOtpStatusHistory
And this is part of the results:
RowNumber OtpInfoId OtpStatusId
----------------------------------
1 1 2
2 1 1
3 1 9
4 1 5
1 2 2
2 2 1
3 2 9
4 2 5
1 3 2
2 3 1
3 3 9
4 3 5
5 3 7
I want write a query to return what OtpInfoId have OtpStatusId with at least one state 7
In this example, the query should return this result:
RowNumber OtpInfoId OtpStatusId
----------------------------------
1 3 2
2 3 1
3 3 9
4 3 5
5 3 7
But I don't know how to write it.
You can use a correlated subquery with a WHERE EXIST clause.
SELECT
ROW_NUMBER() OVER (ORDER BY [OtpInfoId] ASC ) AS RowNumber ,
[OtpInfoId] ,
[OtpStatusId]
INTO #TempTable
FROM RequestOTP.Main.TbOtpStatusHistory osh
WHERE EXISTS (
SELECT 1
FROM RequestOTP.Main.TbOtpStatusHistory
WHERE OtpStatusId = 7 AND OtpInfoId = osh.OtpInfoId
)
One method is to use an EXISTS.
SELECT *
FROM (SELECT row_number() OVER (ORDER BY [OtpInfoId] ASC) RowNumber,
[OtpInfoId],
[OtpStatusId]
FROM [RequestOTP].[Main].[TbOtpStatusHistory]) x
WHERE EXISTS (SELECT *
FROM [RequestOTP].[Main].[TbOtpStatusHistory] y
WHERE y.[OtpInfoId] = x.[OtpInfoId]
AND y.[OtpStatusId] = 7)
INTO #TempTable;
Try this:
SELECT * FROM RequestOTP.Main.TbOtpStatusHistory
WHERE OtpInfoId IN (
SELECT OtpInfoId FROM RequestOTP.Main.TbOtpStatusHistory WHERE OtpStatusId = 7
)
INTO #TempTable;
This subquery returns all the OtpInfoId that have OtpStatusId = 7
Here is one approach:
SELECT
RowNumber,
OtpInfoId,
OtpStatusId
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY [OtpInfoId] ORDER BY OtpInfoId) AS RowNumber,
COUNT(CASE WHEN OtpStatusId = 7 THEN 1 END) OVER (PARTITION BY OtpInfoId) cnt
[OtpInfoId],
[OtpStatusId]
FROM RequestOTP.Main.TbOtpStatusHistory
) t
WHERE cnt > 0
INTO #TempTable;
Note that the row numbering in your expected is not clear, because there is no obvious column which provides that ordering. What is missing is a partition on the OtpInfoId, so I added one.
Please try following script to see if it satisfies your requirement.
create table RequestOTP.Main.TbOtpStatusHistory
(
OtpInfoId int,
OtpStatusId int
)
insert into RequestOTP.Main.TbOtpStatusHistory values
(1,2),(1,1),(1,9),(1,5),(2,2),(2,1),
(2,9),(2,5),(3,2),(3,1),(3,9),(3,5),
(3,7)
;with cte as
(
SELECT
ROW_NUMBER() OVER (partition by OtpInfoId ORDER BY [OtpInfoId] ASC ) AS RowNumber,
[OtpInfoId] ,
[OtpStatusId]
FROM RequestOTP.Main.TbOtpStatusHistory
)
select * from cte
where OtpInfoId in
(select OtpInfoId from RequestOTP.Main.TbOtpStatusHistory where OtpStatusId=7)
/*
RowNumber OtpInfoId OtpStatusId
-------------------- ----------- -----------
1 3 2
2 3 1
3 3 9
4 3 5
5 3 7
*/
Best Regards,
Rachel

Getting the minimum of column on the basis of other field

I have a scenario wherein I have
Id|rank| date
1 | 7 |07/08/2015
1 | 7 |09/08/2015
1 | 8 |16/08/2015
1 | 8 |17/08/2015
1 | 7 |19/08/2015
1 | 7 |15/08/2015
2 | 7 |01/08/2015
2 | 7 |02/08/2015
2 | 8 |16/08/2015
2 | 8 |17/08/2015
2 | 7 |26/08/2015
2 | 7 |28/08/2015
My desired solution is
1 | 7 |07/08/2015
1 | 8 |16/08/2015
1 | 7 |15/08/2015
2 | 7 |01/08/2015
2 | 8 |16/08/2015
2 | 7 |26/08/2015
i.e for each block of id and rank I want the minimum of date.
I have tried using while loop as there are thousands of records it is taking 2 hours to load.Is there any other way to do please suggest.
For each row give unique row number using necessary order. (As I get Id is more important than date and date is more important than rank).
Join resulting table to itself using row numbers shifted by one row (d1.RowNum = d2.RowNum+1).
Select only rows that are joined to "other block" rows (d1.Id <> d2.Id or d1.Rank <> d2.rank).
Depending on shifting direction and selected table either maximal or minimal date will be selected.
Don't forget "edge case" - row that due to shifting can't be joined (that's why not inner join and d1.RowNum = 1 condition used).
;WITH dataWithRowNums as (
select Id, Rank, Date,
RowNum = ROW_NUMBER() OVER (ORDER BY Id,date,rank)
from YourTable
)
select d1.Id, d1.Rank, d1.Date
from dataWithRowNums d1
left join dataWithRowNums d2
on d1.RowNum = d2.RowNum+1 and (d1.Id <> d2.Id or d1.Rank <> d2.rank)
where not d2.Id is null or d1.RowNum = 1
This code returns result bit different from yours:
Id Rank Date
1 7 2015-08-07
1 8 2015-08-16
1 7 2015-08-19 <-- you've got here 2015-08-15
2 7 2015-08-01
2 8 2015-08-16
2 7 2015-08-26
As block (Rank 8 Id 1) have started at 16/08 so row 15/08 for rank 7 is related to first block (rank7 Id1).
If you still need your sorting (so 15/08 rank 7 is related to second block (rank7 id1)) then you should provide your own RowSorting data and then ask here about another solution for another task )
Here is the query using row_number()
;WITH cte_rec
as (SELECT Id,Rank,Date
,ROW_NUMBER()OVER (partition by Id,Rank ORDER BY date) as RNO
FROM YourTable)
SELECT Id,Rank,Date
FROM cte_rec
WHERE RNO =1
This is what I have tried and is running as expected
create table #temp
(
iden int identity(1,1),
ID int,
[rank] int,
[date] date,
dr_id int,
rownum_id int,
grouprecord int
)
Insert into #temp(id,rank,date)
select 1 , 7 ,'07/08/2015'
union all select 1 , 7 ,'09/08/2015'
union all select 1 , 8 ,'08/16/2015'
union all select 1 , 8 ,'08/17/2015'
union all select 1 , 7 ,'08/19/2015'
union all select 1 , 7 ,'08/15/2015'
union all select 2 , 7 ,'08/01/2015'
union all select 2 , 7 ,'08/02/2015'
union all select 2 , 8 ,'08/16/2015'
union all select 2 , 8 ,'08/17/2015'
union all select 2 , 7 ,'08/26/2015'
union all select 2 , 7 ,'08/28/2015'
update t1
set dr_id = t2.rn
from #temp t1 inner join
(select iden, dense_rank() over(order by id) as rn from #temp) t2
on t1.iden = t2.iden
update t1
set rownum_id = t2.rn
from #temp t1 inner join
(select iden, row_number() over(partition by dr_id order by id) as rn from #temp) t2
on t1.iden = t2.iden
select *,row_number() over(order by iden)rn into #temp1 from
(
select t2.*
from #temp t1 inner join #temp t2
on (t1.dr_id = t2.dr_id or t2.dr_id = (t1.dr_id +1) ) and ( t1.rank<>t2.rank or t2.dr_id = (t1.dr_id +1) )
and t2.iden = t1.iden + 1
)a
declare #id int,#miniden int,#maxiden int,#maxid int
set #id = 1
select #maxid = max(iden) from #temp
while exists(select 1 from #temp1 where rn = #id)
begin
Select #miniden = iden from #temp1
where rn = #id
Select #maxiden = iden from #temp1
where rn = #id+1
update #temp
set grouprecord = #id +1
where iden between #miniden and #maxiden
IF(#maxiden IS NULL)
BEGIN
Update #temp
set grouprecord = #id +1
where iden between #miniden and #maxid
END
set #id = #id + 1
SET #miniden =NULL
SET #maxiden = NULL
end
UPDATE #TEMP
SET GROUPRECORD = 1
WHERE GROUPRECORD IS NULL
select min(date) as mindate,grouprecord from #temp
group by grouprecord
Thanks everyone the help :)

Update sort Column linked with group

How can i change the position of one row to change the order
Best to explain with example
I have following table with statuses
Id Name StatusOrder StatusGroup
1 Open 1 1
2 Start 2 1
3 Load 3 1
4 Close 4 1
5 Begin 1 2
6 Open 2 2
7 Close 3 2
I would like to Switch from group one only Status order 2 with 3.
The jump can be more than one row, ex. its also possible that within the same group the order from open moves to status order 3
Sow when i do following select
SELECT * FROM Status WHERE (StatusGroup =1)
Result Set:
Id Name StatusOrder StatusGroup
1 Open 1 1
3 Load 2 1
2 Start 3 1
4 Close 4 1
5 Begin 1 2
6 Open 2 2
7 Close 3 2
I already found example with following article but i do not succeed in it to intgrate that only for one group the order changes
Using a sort order column in a database table
How Can help me?
If correctly understood, here you go:
QUERY
create table #t
(
Id INT,
Name VARCHAR(20),
StatusOrder INT,
StatusGroup INT
)
insert into #t values
(1 ,'Open', 1 , 1),
(2 ,'Start', 2 , 1),
(3 ,'Load', 3 , 1),
(4 ,'Close', 4 , 1),
(5 ,'Begin', 1 , 2),
(6 ,'Open', 2 , 2),
(7 ,'Close', 3 , 2)
;with cte as (
select *, row_number() over(partition by StatusGroup order by Id) rn
from #t
)
select case when StatusOrder = 2 then 3 when StatusOrder = 3 then 2 else Id end as Id,
case when StatusOrder = 2 then 'Load' when StatusOrder = 3 then 'Start' else Name end as Name,
StatusOrder,
StatusGroup
from cte
where rn = id
union all
select Id, Name, StatusOrder, StatusGroup
from cte
where rn <> id
drop table #t
OUTPUT
Id Name StatusOrder StatusGroup
1 Open 1 1
3 Load 2 1
2 Start 3 1
4 Close 4 1
5 Begin 1 2
6 Open 2 2
7 Close 3 2
UPDATE
So if you have table where you need update records you can do something like:
;with cte as (
select *, row_number() over(partition by StatusGroup order by Id) rn
from #t
)
update t
set t.Id = (case when cte.StatusOrder = 2 then 3
when cte.StatusOrder = 3 then 2 else t.Id end),
t.Name = (case when cte.StatusOrder = 2 then 'Load'
when cte.StatusOrder = 3 then 'Start' else t.Name end)
from cte
join #t t on cte.id = t.id
where cte.rn = cte.id

Last record based on 2 column values

pass_id retry_id
1 1
1 2
2 1
2 2
2 3
3 1
3 2
Need to pick up record with MAX(retry_id) within MAX(pass_id).
pass_id retry_id
3 2
Thanks in advance!
The easiest way is with order by and just choosing one row:
select t.pass_id, t.retry_id
from (select t.*
from t
order by pass_id desc, retry_id desc
) t
where rownum = 1
Here is a SQLFiddle.
SELECT *
FROM MyTable
WHERE
retry_id = (Select MAX(retry_id) FROM MyTable) AND
pass_id = (Select MAX(pass_id) FROM MyTable)
Not positive on oracle syntax, but something like
SELECT MAX(retry_ID)
FROM Table
WHERE Pass_ID = (SELECT MAX(Pass_ID)
FROM Table)

Set value of column to 0 for only one of the duplicate records?

I have the following table, and I want to set the value of deleted column to zero for only one of the records that have duplicate names.
id name deleted
------------------------
1 a 1
2 a 1
3 a 1
4 b 1
5 c 1
6 d 1
so the output will be:
id name deleted
------------------------
1 a 0
2 a 1
3 a 1
4 b 0
5 c 0
6 d 0
If your dbms is SQL-Server( >= 2005) you can use a CTE with ROW_NUMBER:
WITH CTE AS
(
SELECT ID, Name, Deleted,
RN = ROW_NUMBER() OVER(PARTITION BY name ORDER BY ID)
FROM dbo.T
)
UPDATE CTE
SET Deleted = 0
WHERE RN = 1
DEMO
UPDATE Tbl SET deleted = 0 WHERE id IN
(SELECT MIN(id) FROM Tbl GROUP BY name)
UPDATE A
SET A.deleted = 0
FROM Tbl A
LEFT JOIN Tbl B
ON A.Name = B.Name
AND A.Id > B.Id
WHERE B.Id IS NULL