UPDATE where timestamp is the latest - sql

I have a group of records all with the same data except the timestamp (Yeah, not my design)
Example:
record_id, user, tmstmp
1, myself, 2006-11-15 09:56:14.325882-05
1, myself, 2006-11-15 09:56:19.051823-05
1, myself, 2006-11-15 11:23:30.581366-05
etc...
Now I would like to UPDATE the record with the latest timestamp. Here is what I'm trying with no luck yet:
UPDATE tbl
SET user = 'TESTING'
WHERE record_id = 1
ORDER BY tmstmp DESC
LIMIT 1
The ORDER BY throws the syntax error.
I think it should be a AND condition but not seeing how. Any thoughts?
PostgreSQL is my db.

UPDATE tbl
SET user = 'TESTING'
WHERE record_id = 1
AND tms_tmp in
(select max(tms_tmp) from tbl where record_id = 1)

UPDATE mytable
SET user = 'TESTING'
WHERE ctid =
(
SELECT ctid
FROM mytable
WHERE record_id = 1
ORDER BY
tmstmp DESC
LIMIT 1
)
This will correctly handle duplicates on tmstmp, if any.

Using PL/pgsql:
DECLARE
cur CURSOR(key int) FOR
SELECT * FROM tbl WHERE tbl.record_id = key ORDER BY tmstmp DESC FOR UPDATE;
BEGIN
OPEN cur(1); -- record_id
MOVE NEXT FROM cur;
UPDATE tbl SET "user" = 'TESTING' WHERE CURRENT OF cur;
CLOSE cur;
END
You can do this in a DO block (from version 9.0) or a function.

Related

How to insert into return table values if no value is found from select

I have current function
CREATE OR REPLACE FUNCTION G_test(
i_tag_id integer,
timestamp_from timestamp,
timestamp_to timestamp
)
returns table (
o_value kepware_messages.value%type,
tag_id integer,
created_at timestamp
)
language plpgsql
as $$
begin
return query
(SELECT kepware_messages.value, kepware_messages.tag_id,kepware_messages.created_at FROM kepware_messages where kepware_messages.tag_id = i_tag_id and kepware_messages.created_at>=timestamp_from and kepware_messages.created_at<=timestamp_to)
UNION
(SELECT kepware_messages.value, kepware_messages.tag_id, kepware_messages.created_at FROM kepware_messages where kepware_messages.tag_id = i_tag_id and kepware_messages.created_at<timestamp_from ORDER BY kepware_messages.created_at DESC LIMIT 1)
UNION
(SELECT kepware_messages.value, kepware_messages.tag_id, kepware_messages.created_at FROM kepware_messages where kepware_messages.tag_id = i_tag_id and kepware_messages.created_at>timestamp_to ORDER BY kepware_messages.created_at ASC LIMIT 1)
ORDER BY created_at DESC;
end;$$
I would like to do the following if in third select from union no table entry is found, to enter my own values.
In such case I would enter
o_value = 'false';
tag_id = i_tag_id;
created_at = timestamp_from;
With help of google I found that you can use
if not found then
Though I am not yet sure how to implement it into union, however I can not find how to insert your own row of data into return table.
Also shouldn't this function return a different kind of table? It looks to me like everything is in same column.
https://i.ibb.co/tMwnXQ2/1.jpg
Sorry, I can't post images yet.
Return a row from the table or a constructed row if no requested rows exist in the table.
SELECT t.value, t.tag_id, t.created_at
FROM
(SELECT 1 rn, km.value, km.tag_id, km.created_at
FROM kepware_messages km
WHERE km.tag_id = i_tag_id and km.created_at>timestamp_to
ORDER BY km.created_at ASC
LIMIT 1
UNION ALL
SELECT 2 rn, 'false' value, i_tag_id tag_id , timestamp_from created_at
) t
ORDER BY rn
LIMIT 1
Replace the third select from your union with this one.

Loop Select and Update PostgreSQL

I'm not finding the error. I need to update a table based on the lowest date for each item by removing duplicates.
FOR temprow IN
SELECT MIN(orcitem_dtime_inclusao), orcitem_orc_id FROM orcamento_itens GROUP BY orcitem_orc_id ORDER BY orcitem_orc_id ASC
LOOP
UPDATE orcamentos SET orc_dtime_orcamento = temprow.orcitem_dtime_inclusao WHERE orc_id = temprow.orcitem_orc_id;
END LOOP;
You seem to want:
UPDATE orcamentos o
SET orc_dtime_orcamento = oi.min_orcitem_dtime_inclusao
FROM (SELECT orcitem_orc_id,
MIN(orcitem_dtime_inclusao) as min_orcitem_dtime_inclusao
FROM orcamento_itens
GROUP BY orcitem_orc_id
) oi
WHERE oi.orcitem_orc_id = o.orc_id;
You don't need a cursor or other looping mechanism. You can do this all in a single query.

Oracle SQL syntax error (missing right parenthesis)

I don't understand why this provokes a syntax error (missing right parenthesis):
UPDATE table
SET doc =
(SELECT 'table-2844-doc' || SUBSTR(doc_file, INSTR(doc_file, '.', -1))
FROM docvers
WHERE (docvers.table_name = 'other_table'
AND docvers.field_name = 'doc')
AND ROWNUM = 1
ORDER BY VERSION DESC)
WHERE table_id = 2844
This looks right to me, does get executed correctly in SQL Server, and is similar to requests found, for example, in Oracle SQL: Update a table with data from another table.
Any tip?
Do it like this:
UPDATE table
SET doc = (
select r.myval
from (
SELECT 'table-2844-doc' || SUBSTR(doc_file, INSTR(doc_file, '.', -1)) myval, ROWNUM RN
FROM docvers
WHERE docvers.table_name = 'other_table'
AND docvers.field_name = 'doc'
ORDER BY VERSION DESC
) r
where r.RN = 1
)
WHERE table_id = 2844
Select the data set first including the ROWNUM, then select from that data set the first row.

Return only one row of a select that would return a set of rows

I have a table like:
create table myTab(
id integer primary key,
is_available boolean not null default true
);
I need to do a query that returns only the first encountered row that has is_available set to false.
something like
select *
from myTab
where not is_available
order by id asc
limit 1
Try out this ..
select id,is_available from myTab
where is_available = false
order by id asc
limit 1
If you want row from last inserted then go with this ..
select id,is_available from myTab
where is_available = false
order by id desc
limit 1
Alternatively, you could use NOT EXISTS to find the first tuple, in most cases this is the fastest solution, too:
SELECT *
FROM myTab mt
WHERE mt.is_available = False
AND NOT EXISTS (
SELECT *
FROM myTab nx
WHERE nx.is_available = False
AND nx.id < mt.id
);

Delete older from a duplicate select

I have been working on a query to search and delete duplicate column values. Currently I have this query (returns duplicates):
SELECT NUIP, FECHA_REGISTRO
FROM registros_civiles_nacimiento
WHERE NUIP IN (
SELECT NUIP
FROM registros_civiles_nacimiento
GROUP BY NUIP
HAVING (COUNT(NUIP) > 1)
) order by NUIP
This work returning a table like this:
NUIP FECHA_REGISTRO
38120100138 1975-05-30
38120100138 1977-08-31
40051800275 1980-09-24
40051800275 1999-11-29
42110700118 1972-10-26
42110700118 1982-04-22
44030700535 1982-10-19
44030700535 1993-05-05
46072300777 1991-01-17
46072300777 1979-03-30
The thing is that I need to delete the rows with duplicate column values. But I need to delete the row with the oldest date, for example, for the given result, once the needed query is performed, this is the list of result that must be kept:
NUIP FECHA_REGISTRO
38120100138 1977-08-31
40051800275 1999-11-29
42110700118 1982-04-22
44030700535 1993-05-05
46072300777 1991-01-17
How can I do this using plain SQL?
--PULL YOUR SELECT OF RECS WITH DUPES INTO A TEMP TABLE
--(OR CREATE A NEW TABLE SO THAT YOU CAN KEEP THEM AROUND FOR LATER IN CASE)
SELECT NUIP,FECHA_REGISTRO
INTO #NUIP
FROM SO_NUIP
WHERE NUIP IN (
SELECT NUIP
FROM SO_NUIP
GROUP BY NUIP
HAVING (COUNT(NUIP) > 1)
)
--CREATE FLAG FOR DETERMINIG DUPES
ALTER TABLE #NUIP ADD DUPLICATETOREMOVE bit
--USE `RANK()` TO SET FLAG
UPDATE #NUIP
SET DUPLICATETOREMOVE = CASE X.RANK
WHEN 1 THEN 1
ELSE 0
END
--SELECT *
FROM #NUIP A
INNER JOIN (SELECT NUIP,FECHA_REGISTRO,RANK() OVER (PARTITION BY [NUIP] ORDER BY FECHA_REGISTRO ASC) AS RANK
FROM #NUIP) X ON X.NUIP = A.NUIP AND X.FECHA_REGISTRO = A.FECHA_REGISTRO
--HERE IS YOUR DELETE LIST
SELECT *
FROM so_registros_civiles_nacimiento R
JOIN #NUIP N ON N.NUIP = R.NUIP AND N.FECHA_REGISTRO = R.FECHA_REGISTRO
WHERE N.DUPLICATETOREMOVE = 1
--HERE IS YOUR KEEP LIST
SELECT *
FROM so_registros_civiles_nacimiento R
JOIN #NUIP N ON N.NUIP = R.NUIP AND N.FECHA_REGISTRO = R.FECHA_REGISTRO
WHERE N.DUPLICATETOREMOVE = 0
--ZAP THEM AND COMMIT YOUR TRANSACTION, YOU'VE STILL GOT A REC OF THE DELETEDS FOR AS LONG AS THE SCOPE OF YOUR #NUIP
BEGIN TRAN --COMMIT --ROLLBACK
DELETE FROM so_registros_civiles_nacimiento
JOIN #NUIP N ON N.NUIP = R.NUIP AND N.FECHA_REGISTRO = R.FECHA_REGISTRO
WHERE N.DUPLICATETOREMOVE = 1
You can use analytical functions for this:
;WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY NUIP ORDER BY FECHA_REGISTRO DESC) RN
FROM registros_civiles_nacimiento
)
DELETE FROM CTE
WHERE RN > 1;
Use RANK() to create the result set ordered by date
Use WHERE EXISTS to delete from the source.
(Note: if you run the rank function over your duplicates, you should get your results. I've just referred to the whole table below)
This statement works in Oracle (replace the select * with delete if it works for you:
SELECT *
FROM registros_civiles_nacimiento ALL_
WHERE EXISTS
(SELECT * FROM
(SELECT * FROM
(SELECT NUIP,
FECHA_REGISTRO,
RANK() OVER (PARTITION BY NUIP ORDER BY FECHA_REGISTRO) AS ORDER_
FROM registros_civiles_nacimiento)
WHERE ORDER_ = 1) OLDEST
WHERE ALL_.NUIP = OLDEST.NUIP
AND ALL_.FECHA_REGISTRO = OLDEST.FECHA_REGISTRO);