Update table with window function - sql

I've table in Redshift with duplicated row that i want to delete,
for that I created filed Id and i want to update him to delete duplicated rows
I'm trying to run this query but it doesn't work
update mr_usage
set id=row_number () over (partition by uid,date(ts),title order by ts)
I received the following error:
ERROR: cannot use window function in UPDATE
I'm looking for a way to update that field

The other possible solution (without CTE) is using UPDATE .. FROM syntax with subquery directly
UPDATE mr_usage outer
SET id = sub.new_id
FROM (
SELECT
id, ROW_NUMBER() OVER (PARTITION BY uid, date(ts), title ORDER BY ts) AS new_id
FROM
mr_usage
) sub
WHERE outer.id = sub.id
But it is also available since PostgreSQL 8.4.

You can try a CTE to achieve this, although such an UPDATE won't remove any duplicate rows.
WITH n AS (
SELECT
id AS current_id,
ROW_NUMBER() OVER (PARTITION BY uid, date(ts), title ORDER BY ts) AS new_id
FROM
mr_usage
)
UPDATE
mr_usage
SET
id = n.new_id
FROM
n
WHERE
mr_usage.id = n.current_id;

Related

SQL: Deleting Duplicates using Not in and Group by

I have the following SQL Syntax to delete duplicate rows, but never are any rows affected.
DELETE FROM content_stacks WHERE id NOT IN (
SELECT id
FROM content_stacks
GROUP BY user_id, content_id
);
The subquery itself is returning the id list of first entries correctly.
SELECT id
FROM content_stacks
GROUP BY user_id, content_id
When I'm inserting the results list as a string it is working, too:
DELETE FROM content_stacks WHERE id NOT IN (239,231,217,218,219,232,233,220,230,226,234,235,224,225,221,223,222,227,228,229,236,237,238,216,208,209,210,204,211,212,242,203,240,201,241,205,206,207,213,214,215);
I checked many similar examples and this should be working in my opinion. What am I missing?
First find first rows using ROW_NUMBER Then delete record with row number greater than 1:
WITH CTE AS (
SELECT id , ROW_NUMBER() OVER(PARTITION BY user_id, content_id, ORDER BY id) rn
FROM content_stacks
)
DELETE cs
FROM content_stacks cs
INNER JOIN CTE ON CTE.id = cs.id
WHERE rn > 1
Am sorry to ask but if your deleting why would u need to group the records.
Are not just increasing the runtime.
The code from Meyssam Toluie is not working as it is but I made a similar solution with the same idea with rownumbers:
DELETE FROM content_stacks WHERE id IN
(SELECT id FROM (
SELECT id, ROW_NUMBER() OVER(PARTITION BY user_id, content_id)row_num
FROM content_stacks
) sub
WHERE row_num > 1)
This is working for me now.
My first command did not work because: The group by command does not show all ids in the output, but they are still there, so in fact all ids were returned in the NOT IN id-list. The row number seems to be the easiest way for this problem.

Listing multiple columns in a single row in SQL

(select ID,EXTERNAL_TRANSACTION_ID,EXTERNAL_TRANSACTION_TYPE,ROW_NUMBER() OVER(PARTITION BY EXTERNAL_TRANSACTION_ID ORDER BY ID ) AS SEQNUM
from AC_POS_TRANSACTION_TRK aptt WHERE [RESULT] ='Success'
GROUP BY ID, EXTERNAL_TRANSACTION_ID,EXTERNAL_TRANSACTION_TYPE )
Hello,
On above query, I want to get rows of transaction id's which has seqnum=1 and seqnum=2
But if that transaction id has no second row (seqnum=2), I dont want to get any row for that transaction id.
Thanks!!
Something like this
Not 100% sure if this is correct without you table definition, but my understanding is that you want to EXCLUDE records if that record has an entry with seqnum=2 -- you can't use a where clause alone because that would still return seqnum = 1.
You can use an exists /not exists or in/not in clause like this
(select ID,EXTERNAL_TRANSACTION_ID,EXTERNAL_TRANSACTION_TYPE,ROW_NUMBER() OVER(PARTITION BY EXTERNAL_TRANSACTION_ID ORDER BY ID ) AS SEQNUM
from AC_POS_TRANSACTION_TRK aptt WHERE [RESULT] ='Success'
and not exists ( select 1 from AC_POS_TRANSACTION_TRK a where a.id = aptt.id
and a.seqnum = 2)
GROUP BY ID, EXTERNAL_TRANSACTION_ID,EXTERNAL_TRANSACTION_TYPE )
basically what this does is it excludes records if a record exists as specified in the NOT EXISTS query.
One option you can try is to add a count of rows per group using the same partioning critera and then filter accordingly. Not entirely sure about your query without seeing it in context and with sample data - there's no aggregation so why use group by?
However can you try something along these lines
select * from (
select ID,EXTERNAL_TRANSACTION_ID,EXTERNAL_TRANSACTION_TYPE,
Row_Number() over(partition by EXTERNAL_TRANSACTION_ID order by ID) as SEQNUM,
Count(*) over(partition by EXTERNAL_TRANSACTION_ID) Qty
from AC_POS_TRANSACTION_TRK
where [RESULT] ='Success'
)x
where SEQNUM in (1,2) and Qty>1
This should do the job.
With Qry As (
-- Your original query goes here
),
Select Qry.*
From Qry
Where Exists (
Select *
From Qry Qry1
Where Qry1.EXTERNAL_TRANSACTION_ID = Qry.EXTERNAL_TRANSACTION_ID
And Qry1.SEQNUM = 1
)
And Exists (
Select *
From Qry Qry2
Where Qry2.EXTERNAL_TRANSACTION_ID = Qry.EXTERNAL_TRANSACTION_ID
And Qry2.SEQNUM = 2
)
BTW, your original query looks problematic to me, specifically I think that instead of a GROUP BY columns those columns should be in the PARTITION BY clause of the OVER statement, but without knowing more about the table structures and what you're trying to achieve, I could not say for sure.

Update table based on the condition

I need to update the staging table based on the type if ZMD2 is present then update the records else update PNTP records.
UPDATE ITEMS_STAGING SET TYPE=b.TYPE,VALUE=b.VALUE
FROM ITEMS_STAGING a,ITEMS b
WHERE a.PARENT=b.PARENT
In the above statement I need to pick only ZMD2 records for the same parent if exists if not PNTP records. I tried to do UNION for the ITEMS it dint help.
Staging table Output:
Kindly help.
Thanks
You need to use analytical function row_number which will group the rows by parent column to give them numbers and then we will take only one record from each group to update staging table using merge statement as following:
MERGE INTO ITEM_STAGING M
USING (
SELECT T.*,
ROW_NUMBER() OVER(PARTITION BY T.PARENT ORDER BY T.TYPE DESC) RN
FROM ITEMS T
)
ON (M.PARENT = T.PARENT AND T.RN = 1)
WHEN MATCHED THEN
UPDATE SET M.TYPE = T.TYPE AND M.VALUE = T.VALUE;
Cheers!!
You may try below query -
SELECT *
FROM (SELECT IS.*, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY TYPE DESC) RN
FROM ITEMS_STAGING)
WHERE RN = 1;
I am not sure what you want to update in this table.

Sql Server query problem in Update Statement

I have Table Job in which i have a column name MainJob_Id name
So i just want to Update Container_TypeId Column Value as 1,2,3...go on...
when MainJob_id is same...
else MainJob_id is new then start again as 1
Execpted Result
Try using an updatable CTE:
WITH cte AS (
SELECT ContainerTypeId,
ROW_NUMBER() OVER (PARTITION BY MainJob_Id ORDER BY LengthId DESC) rn
FROM yourTable
)
UPDATE cte
SET ContainerTypeId = rn;
However, you might want to not do this update and instead just select the sequence you want at the time you query.

Update with Where, Order by and Limit does not work

I am using SQLite 3.
When I input the following query
UPDATE MyTable Set Flag = 1 WHERE ID = 5 Order By OrderID DESC LIMIT 1;
I will always get an error:
near Order By, syntax error
I cannot figure out what is the problem with my query
To use LIMIT and ORDER BY in an UPDATE or DELETE statement, you have to do two things:
Build a custom version of the sqlite3.c amalgamation from source, configuring it with the --enable-update-limit option.
Compile that custom sqlite3.c into your project with SQLITE_ENABLE_UPDATE_DELETE_LIMIT defined to 1.
"Order By OrderID DESC LIMIT 1" is for selecting top one ordered result
so you should use it in select query.
you should do a subquery where you first get the id and then update it:
UPDATE MyTable
SET Flag = 1
WHERE (ID,OrderID) IN (SELECT ID,OrderID
FROM MyTable
WHERE ID = 5
ORDER BY OrderID DESC LIMIT 1);
Demo
You could use ROWID:
UPDATE MyTable
SET Flag = 1
WHERE ROWID IN (SELECT ROWID FROM MyTable WHERE ID = 5
ORDER BY OrderID DESC LIMIT 1);
db<>fiddle demo
or (ID,OrderID) tuple:
UPDATE MyTable
SET Flag = 1
WHERE (ID, ORDERID) IN (SELECT ID, ORDERID FROM MyTable WHERE ID = 5
ORDER BY OrderID DESC LIMIT 1);
db<>fiddle demo2
And if you need to do it in bulk for every ID(SQLite 3.25.0):
WITH cte AS (
SELECT *,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY OrderID DESC) AS rn FROM tab
)
UPDATE tab
SET Flag = 1
WHERE (ID, OrderID) IN (
SELECT ID, OrderID
FROM cte
WHERE rn = 1
);
Order By statement will not work in update query.
You have to use alternate way
UPDATE MyTable Set Flag = 1 WHERE ID = 5
and OrderId = (select max(OrderId) from MyTable where Id = 5);
If you have used the query like above it will work.
Introduction
Below I'm presenting two solutions to solve the issue and perform a proper UPDATE. At the end of each solution there's a live example on sample data included.
First does not require you to input any id and works for the entire table by picking the latest orderid for each id and changing it's flag to 1
Second requires you to input an id and works only for updating one id on the run
I'd personally go with first solution, but I am not sure about your requirement, thus posting two possibilities.
First solution - update entire table
Explanation here, for code scroll down.
For this we will use Row Value construction (id, orderid) just like for the second solution.
It will find the latest row based on orderid and update only that row for given (id, orderid) pair. More on that is included in Explanation of second solution.
We will also need to simulate row_number function to assign each row ranking numbers, to find out which row has the latest orderid for every id and mark then as 1 to be able to pull only those for update. This will allow us to update multiple rows for different ids in one statement. SQLite will have this functionality built in version 3.2.5 but for now, we will work with a subquery.
To generate row numbers we will use this:
select
*,
(select count(*) from mytable m1 where m1.id = m2.id and m1.orderid >= m2.orderid) as rn
from mytable m2
Then we just need to filter the output on rn = 1 and we have what we need.
That said, the whole UPDATE statement will look like:
Code
update mytable
set flag = 1
where (id,orderid) in (
select id, orderid
from (
select *, (select count(*) from mytable m1 where m1.id = m2.id and m1.orderid >= m2.orderid) as rn
from mytable m2
) m
where
m.rn = 1
and m.id = mytable.id
);
Live DEMO
Here's db fiddle to see this solution live on sample data.
Second solution - update only one ID
If you know your ID to be updated and want to run UPDATE statement for only one id, then this will work:
Code
update mytable
set flag = 1
where (id,orderid) in (
select id, orderid
from mytable
where id = 5
order by orderid desc
limit 1
);
Explanation
(id, orderid) is a construction called Row Value for which SQLite compares scalar values from left to right.
Example taken from documentation:
SELECT (1,2,3) = (1,2,3) -- outputs: 1
Live DEMO
Here's db fiddle to see this solution live on sample data.
SQL
UPDATE MyTable
SET Flag = 1
WHERE ID = 5
AND OrderID IN (SELECT OrderID
FROM MyTable
WHERE ID = 5
ORDER BY OrderID DESC
LIMIT 1);
Demo
SQLFiddle demo: http://sqlfiddle.com/#!5/6d596/2