unable to use alias in Update statment in sql - sql

I working of update query for with the help of CTEs. Actually I want to update a table records, on the basis of duplicate records I just want to update one one row among those duplicate rows. My code is mentioned below:
with toupdate as (
select c.*,
count(*) over (partition by c.ConsumerReferenceNumber) as cnt,
max(c.ID) over (partition by c.ID) as onhand_value
from [dbo].[tbl_NADRA_CPS] c
)
update [dbo].[tbl_NADRA_CPS]
set StatusID = 38
where cnt > 1;
I am unable to use 'cnt' in my update where clause.
Thanks in advance.

Because cnt is a field of your CTE, not of [dbo].[tbl_NADRA_CPS]
with toupdate as (
select c.*,
count(*) over (partition by c.ConsumerReferenceNumber) as cnt,
max(c.ID) over (partition by c.ID) as onhand_value
from [dbo].[tbl_NADRA_CPS] c
)
update toupdate
set StatusID = 38
where cnt > 1;

If you want to update one row among the duplicates, then your query will not do that. Instead:
with toupdate as (
select c.*,
count(*) over (partition by c.ConsumerReferenceNumber) as cnt,
row_number() over (partition by c.ConsumerReferenceNumber order by c.ID) as seqnum
from [dbo].[tbl_NADRA_CPS] c
)
update toupdate
set StatusID = 38
where cnt > 1 and seqnum = 1;
The cnt > 1 gets reference numbers with more than one row. The seqnum = 1 ensures that just one is updated.

Related

Update Values Returned from Select Statement

I have the following sql query that returns all duplicates (except most recent).
select *
from CodeData
inner join
(select max(cdID) as lastId, cdName
from CodeData
where cdName in (select cdName from CodeData
group by cdName
having count(*) > 1)
group by cdName) duplic on duplic.cdName = CodeData.cdName
where CodeData.cdID < duplic.lastId;
How would I go about updating a column for each result returned in the above query? Say I have an arbitrary column A, I was thinking about something along the lines of this at the end of the query.
update CodeData
set A = 0
where CodeData.cdID < CodeData.duplic.lastId;
But this ends up updating the entire table, and not just the returned results above.
Any tips?
Use an updatable CTE and window functions:
with toupdate as (
select cd.*,
row_number() over (partition by cdName order by cdId desc) as seqnum
from codedata cd
)
update toupdate
set a = 0
where seqnum > 1;
Note: You might find that row_number() is sufficient to handle the "duplication" issue and you don't need to store an additional column at all.
And you might really want:
update toupdate
set a = (case when seqnum > 1 then 0 else 1 end);

SQL - delete record where sum = 0

I have a table which has below values:
If Sum of values = 0 with same ID I want to delete them from the table. So result should look like this:
The code I have:
DELETE FROM tmp_table
WHERE ID in
(SELECT ID
FROM tmp_table WITH(NOLOCK)
GROUP BY ID
HAVING SUM(value) = 0)
Only deletes rows with ID = 2.
UPD: Including additional example:
Rows in yellow needs to be deleted
Your query is working correctly because the only group to total zero is id 2, the others have sub-groups which total zero (such as the first two with id 1) but the total for all those records is -3.
What you're wanting is a much more complex algorithm to do "bin packing" in order to remove the sub groups which sum to zero.
You can do what you want using window functions -- by enumerating the values for each id. Taking your approach using a subquery:
with t as (
select t.*,
row_number() over (partition by id, value order by id) as seqnum
from tmp_table t
)
delete from t
where exists (select 1
from t t2
where t2.id = t.id and t2.value = - t.value and t2.seqnum = t.seqnum
);
You can also do this with a second layer of window functions:
with t as (
select t.*,
row_number() over (partition by id, value order by id) as seqnum
from tmp_table t
),
tt as (
select t.*, count(*) over (partition by id, abs(value), seqnum) as cnt
from t
)
delete from tt
where cnt = 2;

SQL query null value replaced based on another

So I have query to return data and a row number using ROW_NUMBER() OVER(PARTITION BY) and I place it into a temp table. The initial output looks the screenshot:
.
From here I need to, in the bt_newlabel column, replace the nulls respectively. So Rownumber 1-4 would be in progress, 5-9 would be underwriting, 10-13 would be implementation, and so forth.
I am hitting a wall trying to determine how to do this. Thanks for any help or input of how I would go about this.
One method is to assign groups, and then the value. Such as:
select t.*, max(bt_newlabel) over (partition by grp) as new_newlabel
from (select t.*, count(bt_newlabel) over (order by bt_stamp) as grp
from t
) t;
The group is simply the number of known values previously seen in the data.
You can update the field with:
with toupdate as (
select t.*, max(bt_newlabel) over (partition by grp) as new_newlabel
from (select t.*, count(bt_newlabel) over (order by bt_stamp) as grp
from t
) t
)
update toupdate
set bt_newlabel = new_newlabel
where bt_newlabel is null;
If I understood what you are trying to do, this is the type of update you need to do on your temp table:
--This will update rows 1-4 to 'Pre-Underwritting'
UPDATE temp_table SET bt_newlabel = 'Pre-Underwritting'
WHERE rownumber between
1 AND (SELECT TOP 1 rownumber FROM temp_table WHERE bt_oldlabel = 'Pre-Underwritting');
--This will update rows 5-9 to 'Underwritting'
UPDATE temp_table SET bt_newlabel = 'Underwritting'
WHERE rownumber between
(SELECT TOP 1 rownumber FROM temp_table WHERE bt_oldlabel = 'Pre-Underwritting')
AND
(SELECT TOP 1 rownumber FROM temp_table WHERE bt_oldlabel = 'Underwritting');
--This will update rows 10-13 to 'Implementation'
UPDATE temp_table SET bt_newlabel = 'Implementation'
WHERE rownumber between
(SELECT TOP 1 rownumber FROM temp_table WHERE bt_oldlabel = 'Underwritting')
AND
(SELECT TOP 1 rownumber FROM temp_table WHERE bt_oldlabel = 'Implementation');
I made a working Fiddle to check out the results: http://sqlfiddle.com/#!18/1cae2/1/3

delete statement with a having count(*) > 1

I have some duplication's in a table that I wish to delete. However SQL doesn't like my query below.
delete from tblS
where Field = 'spread'
group by FundCode, Region, DateEntered
having count(*) > 1
So i tried the below query again though SQL doesn't like this. How should my query look?
delete s
from tblS s
join
(
select FundCode, Region, DateEntered, count(*)
from tblS
where Field = 'spread'
group by FundCode, Region, DateEntered
having count(*) > 1
) as d on s.FundCode = d.FundCode and s.DateEntered = d.DateEntered and s.Region= d.Region
Normally when you want to delete duplicates, you want to keep one of them. The right function to use is row_number() and SQL Server supports updatable CTEs. So:
with toupdate as (
select t.*,
row_number() over (partition by FundCode, Region, DateEntered order by FundCode) as seqnum
from tblS t
where Field = 'spread'
)
delete toupdate
where seqnum > 1;
If you actually want to delete all duplicates, then use count(*) instead of row_number().

How to update rows based only on ROW_NUMBER()?

Such SQL query:
SELECT ROW_NUMBER() OVER (PARTITION BY ID, YEAR order by ID ), ID, YEAR
from table t
give me following query set:
1 1000415591 2012
1 1000415591 2013
2 1000415591 2013
1 1000415591 2014
2 1000415591 2014
How could I update records with ROW_NUMBER() equals to 2? Other fields of this records is identically (select distinct from table where id = 1000415591 gives 3 records when there are 5 without distinct keyword), so I can depend only on ROW_NUMBER() value.
I need solution for Oracle, because I saw something similar for SQL-Server but it won't work with Oracle.
You could use a MERGE statement which is quite verbose and easy to understand.
For example,
MERGE INTO t s
USING
(SELECT ROW_NUMBER() OVER (PARTITION BY ID, YEAR order by ID ) RN,
ID,
YEAR
FROM TABLE t
) u ON (s.id = u.id)
WHEN MATCHED THEN
UPDATE SET YEAR = some_value WHERE u.RN = 2)
/
Note You cannot merge the same column which is used to join in the ON clause.
Try to use ROWID field:
UPDATE T
SET t.year = t.year*1000
WHERE (rowid,2) in (SELECT rowid,
ROW_NUMBER()
OVER (PARTITION BY ID, t.YEAR order by ID )
FROM T)
SQLFiddle demo
If you need to delete range of ROWNUMBERS then :
UPDATE T
SET t.year = t.year*1000
WHERE rowid in ( SELECT rowid FROM
(
SELECT rowid,
ROW_NUMBER()
OVER (PARTITION BY ID, t.YEAR order by ID ) as RN
FROM T
) T2 WHERE RN >=2 AND RN <=10
)
SQLFiddle demo
This is not the update statement but this is how to get the 2 rows you wanted to update:
SELECT *
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY ID, YEAR order by ID ) as rn, ID, YEAR
from t )
where rn = 2
SQLFIDDLE
When I've posted thq question, I've found that this could be wrong approach. I could modify table and add new fields. So better solution to create one more field IDENTITY and update it with numbers from the new sequence from 1 to total row numbers. Then I could update fields based on this IDENTIY field.
I'll keep this question opened if someone come up with solution based on ROW_NUMBER() analytic function.
update TABLE set NEW_ID = TABLE_SEQ.nextval
where IDENTITY in (
select IDENTITY from (
select row_number() over(PARTITION BY ID, YEAR order by ID) as row_num, t.ID, t."YEAR", t.IDENTITY
from TABLE t
) where row_num > 1
)