DB2 LUW MERGE using same table to update a different row - sql

My table data looks like this
My poorly attempted SQL is this...
MERGE INTO PRINT target
USING
(
select ID,PDF_FILE
from PRINT
where date(PRINTED) = '2022-01-06'
and PDF_FILE is not null
) sause
ON (target.ID = sause.ID)
WHEN MATCHED THEN
UPDATE SET target.PDF_FILE = sause.PDF_FILE
It is updating all rows in the table. I do not want this.
How can I make it ONLY update the 1 latest PRINTED row which has an empty PDF_FILE ?

The idea is to enumerate target rows and update only the 1-st one.
MERGE INTO
(
SELECT ID, PDF_FILE, ROW_NUMBER () OVER (PARTITION BY ID ORDER BY PRINTED DESC) AS RN_
FROM PRINT
WHERE
-- Below is an appropriate condition for
-- the target rows to distinguish them from the source row
-- PDF_FILE IS NULL
date (PRINTED) <> '2022-01-06'
) target
USING
(
select ID,PDF_FILE
from PRINT
where date (PRINTED) = '2022-01-06'
and PDF_FILE is not null
) sause
ON target.ID = sause.ID AND target.RN_ = 1
WHEN MATCHED THEN
UPDATE SET target.PDF_FILE = sause.PDF_FILE

Related

DB2 - SQL UPDATE statement using JOINS and SELECT statement

Morning,
I'm running the following SELECT statement on a DB2 server (IBM Power System) and it returns the latest record from tableB based on a Timestamp (all good).
SELECT * FROM library1/tableA
JOIN library1/tableB on tableB.PRDCOD = tableA.NPROD
WHERE tableB.PRDCOD = '5520' and tableA.SPRTXT01 <> '0/9'
ORDER BY tableB.timstp DESC FETCH NEXT 1 ROWS ONLY
I now need to change this statement to update tableA and set field SPRTXT01 = '0/9', but only if tableB.SRVRSP= 'SUCCESSFUL' i.e. the latest record from tableB has a response of 'SUCCESSFUL'.
But I don't know how to format this statement correctly. Can anyone assist please?
I've tried the below, but this updated ALL rows in the table
UPDATE library1/tableA
SET tableA.SPRTXT01 = '0/9'
Where exists (
Select '1'
FROM library1/tableA
JOIN library1/tableB on
tableB.PRDCOD = tableA.NPROD
WHERE tableB.PRDCOD = '5520' and tableB.SRVRSP = 'SUCCESSFUL'
and tableA.SPRTXT01 <> '0/1'
ORDER BY tableB.timstp DESC FETCH NEXT 1 ROWS ONLY)
and I don't think it's applying the selection correctly i.e. rather than selecting the latest record from table B and then applying the RVSRP = 'SUCCESSFUL' check, it is only selecting the latest record for table B where SRVSRP = 'SUCCESSFUL'.
Thanks
Try this:
CREATE TABLE tableA (NPROD VARCHAR (10), SPRTXT01 VARCHAR (3));
CREATE TABLE tableB (PRDCOD VARCHAR (10), SRVRSP VARCHAR (20), timstp TIMESTAMP);
INSERT INTO tableA (NPROD, SPRTXT01)
VALUES ('5520', ''), ('XXXX', '');
INSERT INTO tableB (PRDCOD, SRVRSP, timstp)
VALUES
('5520', 'SUCCESSFUL', CURRENT TIMESTAMP)
, ('5520', 'UNSUCCESSFUL', CURRENT TIMESTAMP + 1 SECOND)
-- Comment out the next row to make it NOT update the SPRTXT01 column
, ('5520', 'SUCCESSFUL', CURRENT TIMESTAMP + 2 SECOND)
;
UPDATE tableA
SET tableA.SPRTXT01 = '0/9'
Where tableA.NPROD = '5520' AND tableA.SPRTXT01 <> '0/9'
AND exists
(
SELECT 1
FROM tableB
JOIN (SELECT PRDCOD, MAX (timstp) AS timstp FROM tableB GROUP BY PRDCOD) G
ON (G.PRDCOD, G.timstp) = (tableB.PRDCOD, tableB.timstp)
WHERE tableB.PRDCOD = tableA.NPROD
AND tableB.SRVRSP = 'SUCCESSFUL'
);
SELECT * FROM tableA;
NPROD
SPRTXT01
5520
0/9
XXXX
fiddle

How do I return the count for both matching column in a single return

I'm trying to get a number of count for exist and non-exist for my validation to insert my data. As refer to the image below, I'm inserting 3 data in which 1 data consist of same ID number as destination table, 1 data consist of same quotation number as destination table and the last data is a new entry.
Okay here's my requirement. I am sending my source table data to insert into the dest table. So before inserting, I want to do validation by mapping the entire dest table like this:
SELECT COUNT(*) FROM sourceTable WHERE exists(
SELECT * FROM sourceTable WHERE QuotationId IN
(SELECT A.QuotationId FROM sourceTable A
JOIN DestTable B ON A.QuotationId = B.QuotationId
JOIN DestTable C ON A.QuotationNum = C.QuotationNum)
Without further details on table structure and so on it's difficult to tell, but something like the following might do the trick:
WITH cteMatch AS(
SELECT s.QuotationID AS src, d.QuotationID as dst
FROM sourceTable s
LEFT JOIN destTable d
)
SELECT CASE WHEN dst IS NULL THEN N'NonExist' ELSE N'Exist' END AS ValExist, COUNT(*) cnt
FROM cteMatch
GROUP BY CASE WHEN dst IS NULL THEN N'NonExist' ELSE N'Exist' END
You seem to want:
select count(*)
from sourcetable s
where exists(
select 1
from desttable d
where d.quotationid = s.quotationid or d.quotationnum = s.quotationnum
)
This counts how many rows in the source table have a quotation id or num that exists in the target table. If you want the count of both existing and non-existing rows, I would recommend:
select sum(flag) as cnt_exists, sum(1 - flag) as cnt_not_exists
from (
select
case when exists (
select 1
from desttable d
where d.quotationid = s.quotationid or d.quotationnum = s.quotationnum
) then 1 else 0 end as flag
from sourcetable s
) t

How do I UPDATE from a SELECT in Informix?

I'm trying to do an update using data from another table. I've tried this answer (the second part), but it is not working for me. I'm receiving a generic error message of syntax error.
I've also tried this solution and received a syntax error message too.
If I try to update just one column, it works:
UPDATE dogs
SET name =
(
SELECT 'Buddy'
FROM systables
WHERE tabid = 1
);
But I need to update multiples columns. Unfortunately, this is not working:
UPDATE dogs
SET (name, breed) =
(
SELECT 'Buddy', 'pug'
FROM systables
WHERE tabid = 1
);
Informix version is 12.10.FC8
You are missing 1 more set of parentheses around the subquery.
From the Informix manual:
The subquery must be enclosed between parentheses. These parentheses
are nested within the parentheses that immediately follow the equal (
= ) sign. If the expression list includes multiple subqueries, each subquery must be enclosed between parentheses, with a comma ( , )
separating successive subqueries:
UPDATE ... SET ... = ((subqueryA),(subqueryB), ... (subqueryN))
The following examples show the use of subqueries in the SET clause:
UPDATE items
SET (stock_num, manu_code, quantity) =
(
(
SELECT stock_num, manu_code
FROM stock
WHERE description = 'baseball'
),
2
)
WHERE item_num = 1 AND order_num = 1001;
UPDATE table1
SET (col1, col2, col3) =
(
(
SELECT MIN (ship_charge), MAX (ship_charge)
FROM orders
),
'07/01/2007'
)
WHERE col4 = 1001;
So in order for your update to be accepted by Informix it has to be:
UPDATE dogs
SET (name, breed) =
(
(
SELECT 'Buddy', 'pug'
FROM systables
WHERE tabid = 1
)
);

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'

SQL Server MERGE, if exist then update else insert

I've a table called data with columns ip, report_date, group, value. The primary key is ip, report_date and group together.
When the table is empty and I run the statement below there is nothing inserted. What's wrong with my statement?
When there is a match the record is updates according plan...
MERGE bc_data2 AS Target
USING (SELECT ip, report_date, group, value FROM bc_data2 As b
WHERE b.ip = '1.1.1.2'
AND b.report_date = '2/29/2012'
AND b.group = 'EPO-Client-Update') AS Source
ON (Target.ip = Source.ip
AND Target.frequency = Source.frequency
AND Target.report_date = Source.report_date
AND Target.service = Source.service
AND Target.proxy_service = Source.proxy_service
AND Target.proxy = Source.proxy
AND Target.service_group = Source.service_group)
WHEN MATCHED THEN
UPDATE SET Target.value = Target.value + 1
WHEN NOT MATCHED BY Target THEN
INSERT (ip, report_date, group, value)
VALUES ('1.1.1.2', '2/29/2012', 'EPO-Client-Update', 119437142);
You don't add any rows because your using clause is not returning any rows. Put your constants in the using clause with column aliases and use the fields in the when not matched
Something like this with a couple of fields removed simplicity.
merge bc_data2 as T
using (select '1.1.1.2' as ip,
'2012-02-29' as report_date,
1194370142 as value) as S
on T.ip = S.ip and
T.report_date = S.report_date
when matched then
update set T.value = T.value + 1
when not matched then
insert (ip, report_date, value)
values(ip, report_date, value);
I've found that NULLs don't compare well in the ON clause of the MERGE statement. However, converting NULLs to blanks is a valid workaround. I would modify the the ON statement to look something like this.
ON (ISNULL(Target.ip, '') = ISNULL(Source.ip, '')
AND ISNULL(Target.frequency, '') = ISNULL(Source.frequency, '')
AND ISNULL(Target.report_date, '') = ISNULL(Source.report_date, '')
AND ...)