Loop Select and Update PostgreSQL - sql

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.

Related

Update date column from min date in another column in the same table

I need to update a field called "FirstPayment" with the date from another field in the same table.
Here is the code:
UPDATE R1
SET R1.FirstPayment = (select min(r1.effective_date) where R1.amount>0)
from CTTC_RentalCarPayments R1, CTTC_RentalCarPayments R2
where r1.id=r2.id
This results in "An aggregate may not appear in the set list of an UPDATE statement"
I know I am missing something simple here.
I also know the FirstPayment field isn't necessary because I could get that date in a query but the application I am feeding this data too needs it in this column.
Use window functions:
with toupdate as (
select r.*,
min(case when r1.amount > 0 then r.effective_date end) over (partition by id) as new_effective_date
from CTTC_RentalCarPayments r
)
update toupdate
set FirstPayment = new_effective_date;
You could set value of the field you want to get to a variable and then update the field to that variable:
Declare #firstPayment datetime;
Set #firstPayment = (select min(r1.effective_date) from r1 where R1.amount>0);
Declare #firstPaymentID int;
Set #firstPaymentID = (select id from r1 where #firstPayment = FirstPayment);
UPDATE r1
SET FirstPayment = #firstPayment where Id = #firstPaymentID;
Let me know if this helps. It's not a recursive CTE but it might work too.

Error with SELECT statement in a FOR loop

When I try to execute the code below I receive this error here:
Error(9,4): PLS-00103: Encountered the symbol "SELECT" when expecting
one of the following: ( ) - + case mod new not null table continue avg count current exists max min prior sql
stddev sum variance execute multiset the both leading trailing
forall merge year month day hour minute second timezone_hour
timezone_minute timezone_region timezone_abbr time timestamp
interval date
CREATE OR REPLACE PROCEDURE PROC_LIST_SIMILAR_TVSERIES
(seriesName IN SERIES.NAME%TYPE)
AS
CURSOR series IS (SELECT IDS FROM SERIES WHERE NAME = seriesName);
allSeries SERIES%ROWTYPE;
BEGIN
FOR series IN allSeries
(SELECT 2* ( SELECT COUNT(*)
FROM DICT d
WHERE d.idt IN ( SELECT DISTINCT IDT
FROM POSTING
WHERE IDS = series
INTERSECT
SELECT DISTINCT IDT
FROM POSTING
WHERE IDS = allSeries.IDS
)
)
/ ( ( SELECT DISTINCT COUNT(IDT)
FROM POSTING
WHERE IDS = series
) +
( SELECT DISTINCT COUNT(IDT)
FROM POSTING
WHERE IDS = allSeries.IDS )
)
INTO similarity
FROM SERIES s1
SERIES s2
WHERE s1.IDS = series
AND s2.IDS != series
);
IF similarity > 0.7 THEN
DBMS_OUTPUT.PUT_LINE('ok');
END LOOP;
END;
/
What the code does is take in a name, find it's ID, and compare it to other id's (and avoid comparing it to the same ID). I'm trying to print out "ok" whenever the similarity calculation is over 0.7 . No idea why this doesn't work.
First, if you have a cursor named the same as the table, what is the series%rowtype going to look like? The cursor or the table? Bad idea.
Second, you never execute the cursor to get the ID, so your subsequent cursor loop is looking for records that match allSeries.IDS which is null because you haven't populated it.
Try this as a starting point, although I'm guessing that you still will have work to do on your cursor query. Still, at least it points you to the right code structures...
CREATE OR REPLACE PROCEDURE PROC_LIST_SIMILAR_TVSERIES
(seriesName IN SERIES.NAME%TYPE)
AS
CURSOR seriesCur IS (SELECT IDS FROM SERIES WHERE NAME = seriesName);
allSeries seriesCur%ROWTYPE;
BEGIN
OPEN seriesCur;
FETCH seriesCur INTO allSeries;
IF seriesCur%NOTFOUND
THEN
CLOSE seriesCur;
raise_application_error(-20001,'Your SeriesName does not exist');
END IF;
CLOSE seriesCur;
FOR seriesRec IN
-- this query is a mess! Tried to fix up some aspects of it according to what I THINK you're trying to do.
(SELECT 2*
(SELECT COUNT(*) FROM DICT d WHERE d.idt IN (
SELECT DISTINCT IDT FROM POSTING WHERE IDS = allSeries.IDS
INTERSECT
SELECT DISTINCT IDT FROM POSTING WHERE IDS = allSeries.IDS))
/ ((SELECT DISTINCT COUNT(IDT) FROM POSTING WHERE IDS = allSeries.IDS) +
(SELECT DISTINCT COUNT(IDT) FROM POSTING WHERE IDS = allSeries.IDS) ) similarity
FROM SERIES s1, SERIES s2
WHERE s1.IDS = allSeries.IDS
AND s2.IDS != allSeries.IDS)
LOOP
IF seriesRec.similarity > 0.7 THEN
DBMS_OUTPUT.PUT_LINE('ok');
END IF;
END LOOP;
END;
/
I am still trying to understand the logic in the SQL statement. But i
have hopefully tried to remove the syntactical error. Hope it helps.
CREATE OR REPLACE PROCEDURE PROC_LIST_SIMILAR_TVSERIES(
seriesName IN SERIES.NAME%TYPE)
AS
similarity PLS_INTEGER;
BEGIN
FOR i IN
(SELECT IDS FROM SERIES WHERE NAME = seriesName
)
LOOP
--The logic i am still not able to understand
SELECT *,
(SELECT COUNT(*)
FROM DICT d
WHERE d.idt IN
( SELECT DISTINCT IDT FROM POSTING WHERE IDS = I.IDS
INTERSECT
SELECT DISTINCT IDT
FROM POSTING
WHERE IDS = allSeries.IDS
) / (
(SELECT DISTINCT COUNT(IDT) FROM POSTING WHERE IDS = i.IDS
) +
(SELECT DISTINCT COUNT(IDT) FROM POSTING WHERE IDS = I.IDS
) )
)
INTO similarity
FROM SERIES s1,
SERIES s2
WHERE s1.IDS = s2.IDS
AND s2.IDS != I.IDS;
IF similarity > 0.7 THEN
DBMS_OUTPUT.PUT_LINE('ok');
END IF;
END LOOP;
END;
/

SQL Update - Cant specify target table in select

I have the following table
#key | #value
colour | red
weather | blue
Now I want to update the value of the row with the key colour to be the value of the row with the key weather. So I am doing:
UPDATE table_name
SET value = (SELECT value FROM table_name WHERE key = "weather")
WHERE key = "colour";
But this update gives me following error message:
You can't specify target table for update in FROM clause
How can I do that query without error?
This may be because it may be the case that the select query returns more than one value for the column value
UPDATE table_name
SET value = (SELECT max(value) FROM table_name WHERE key = "weather")
WHERE key = "colour";
or
UPDATE table_name
SET value = (SELECT value FROM table_name WHERE key = "weather" limit 1)
WHERE key = "colour";
You may try to change it like this by replacing the instance of table_name in the sub-query with (SELECT * FROM table_name):
UPDATE table_name
SET table_name.A =
(
SELECT B
FROM (SELECT * FROM table_name) AS something
INNER JOIN ...
)
Also check How to select from an update target in MySQL
You don’t want to just SELECT * FROM table in the subquery in real
life; I just wanted to keep the examples simple. In reality you should
only be selecting the columns you need in that innermost query, and
adding a good WHERE clause to limit the results, too.
EDIT:-
As you have already commented that but I have answered above to use the temporary table like this:-
UPDATE table_name
SET value = (SELECT value FROM (SELECT value FROM table_name WHERE key="weather") AS x)
WHERE key="colour"
you might try this if your sql supports 'LIMIT'
UPDATE table_name SET value = (SELECT value FROM table_name WHERE key = "weather" LIMIT 1 ) WHERE key = "colour";
UPDATE table_name
SET colour ='red',weather =blue
WHERE column_name = some_value;
It won't work that way. You can't have a read and a write query at the same time for the same table. With two different tables it will work.
UPDATE `table` SET value = (SELECT value FROM `Table_b` WHERE `other_value` = 'xy' LIMIT 1) WHERE `key` = 'colour'

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);

UPDATE where timestamp is the latest

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.