update value only when value changed - sql

I have to update main_table data from another table. Below is statement
We will have value in any one column among value_string,value_date in a row or for a key, based on type_key. (like if type_key is string then value_string will have value and value_date is null). There is a trigger which ensure this constraint.
update main_table t set
value_string=value_string,
value_date=value_date,
value_int=value_int,
updated_on=now(),
status=status
from import_table imp where t.key=imp.key
even if there was no change in values of value_string or value_date, then updated_on will change. I want updated_on to be updated only if there is change in values. so i'm changing update query to below
update main_table t set
value_string=value_string,
value_date=value_date,
updated_on= (case when type_key='string' and t.value_string!=imp.value_string
then now()
when type_key='date' and t.value_date!=imp.value_date
then now()
when type_key='int' and t.value_int!=imp.value_int
then now()
else updated_on end),
status=status
from import_table imp where t.key=imp.key
Is there a better approach to rewrite above query to improve the performance of query ?

I would add a WHERE condition that only changes the row if at least one of the values is different.
update main_table t
set
value_string = imp.value_string,
value_date = imp.value_date,
value_int = imp.value_int,
updated_on = now(),
status = imp.status
from import_table imp
where t.key = imp.key
and ( t.value_string is distinct from imp.value_string
or t.value_date is distinct from imp.value_date
or t.value_int is distinct from imp.value_int
or t.status is distinct from imp.status);
alternatively you can write it as
where t.key = imp.key
and (t.value_string, t.value_date, t.value_int, t.status)
is distinct from (imp.value_string, imp.value_date, imp.value_int, imp.status);

Related

updating for several columns with distinct conditions

I have INSERT statement where values are provided through SELECT from other table. ON CONFLICT I'm updating several columns. I'm just wondering if is possible to SET each column matching unique condition
Now I have solution which work, however it isn't ideal.
Basically something like this would match my desired result..
WITH table_a (
--joining two tables
)
INSERT INTO table_b
SELECT * FROM table_a
ON CONFLICT
ON CONSTRAINT table_b_pkey DO UPDATE
SET column_a = EXCLUDED.column_a
WHERE table_b.column_a < EXCLUDED.column_a
OR
SET column_b = EXCLUDED.column_b
WHERE table_b.column_b < EXCLUDED.column_b
Use CASE, e.g.:
INSERT INTO table_b
SELECT * FROM table_a
ON CONFLICT
ON CONSTRAINT table_b_pkey DO UPDATE
SET
column_a = CASE
WHEN table_b.column_a < EXCLUDED.column_a
THEN EXCLUDED.column_a
ELSE table_b.column_a END,
column_b = CASE
WHEN table_b.column_b < EXCLUDED.column_b
THEN EXCLUDED.column_b
ELSE table_b.column_b END

Case, Select and Update in a single query

I am trying to do the following, but I get an error on DELETE FROM, any idea why? So how can I check if the createts=endts in person_net_contacts is true for certain persondID then Delete... else Update...
SELECT
createts, endts,
CASE WHEN createts = endts
THEN DELETE FROM person_net_contacts where personid='276178';
ELSE UPDATE person_net_contacts SET endts = current_timestamp
WHERE personid='276178';
FROM person_net_contacts
You can do this with a single query if you use writeable CTEs:
with to_check as (
SELECT personid, createts, endts, createts = endts as delete_this
WHERE personid = '276178'
FROM person_net_contacts
), deleted as (
delete from person_net_contacts
where personid = (select personid
from to_check
where delete_this)
)
update person_net_contacts pnc
SET endts = current_timestamp
from to_check tc
where tc.personid = pnc.personid
and not tc.delete_this;
The first CTE selects the row from the table and creates a flag that tells us if the row should be deleted or not. The second CTE then deletes rows based on that flag and the final statement updates the row if needed.
This also works for multiple rows assuming that personid is unique.
You should also compare numbers to numbers '276178' is a string value, not a number. If personid is defined as a number data type (integer, bigint or something similar) you should use where personid = 276178. Never put single quotes around numbers.
You can delete or update a row in one query using with statement, e.g.:
with delete_person as (
delete from person_net_contacts
where personid = '276178'
and createts = endts
returning 'deleted'::text, createts, endts
),
update_person as (
update person_net_contacts
set endts = current_timestamp
where personid = '276178'
and createts is distinct from endts
returning 'updated'::text, createts, endts
)
select *
from delete_person
union all
select *
from update_person;
You can't mix SELECT, DELETE, UPDATE, CASE like this. I assume you want to do this:
DELETE FROM person_net_contacts
WHERE personid='276178' AND createts=endts;
UPDATE person_net_contacts
SET endts = current_timestamp
WHERE personid='276178' AND createts!=endts;
-- the last condition only kicks in if you run the two statements in a different order
This mix of operations (SELECT and DELETE, UPDATE) are not part of a SQL expression. However you can run a SELECT, DELETE and UPDATE operations independently using the right conditions:
All the select fields must be an expression that returns some value: UPDATE or DELETE operations are not intended for that.
SELECT createts, endts
FROM person_net_contacts
WHERE personid='276178';
Delete the record when createts = endts
DELETE FROM person_net_contacts where personid='276178' and createts = endts;
Update the record when createts <> endts (the last condition can be ommited , the update only affects records that are not deleted in the previous command)
UPDATE person_net_contacts SET endts = current_timestamp where personid='276178' and createts <> endts;
IF EXISTS(SELECT 1 FROM person_net_contacts WHERE personid='276178' AND DATEDIFF(DAY,createts,endts) = 0)
BEGIN
DELETE FROM person_net_contacts where personid='276178'
END
ELSE
BEGIN
UPDATE person_net_contacts SET endts = current_timestamp where personid='276178'
END

Effective update statement

I have table Value_table and column value
I need to update value column as 'Hi' if any value column exists with 'bye'
I wrote query
UPDATE Value_table
SET value = 'Hi'
WHERE value_id
IN
(
SELECT
value_id
FROM
Value_table
WHERE
value = 'bye'
)
It works fine.Now since I'm using the same table in sub query can we make any other way to make this statement in more efficient.?
If value_id is unique you can do
UPDATE Value_table
SET value = 'Hi'
WHERE value = 'bye'
i guess if the table is really large, EXISTS might perform better.
UPDATE vt
SET vt.value = 'Hi'
FROM Value_table vt
WHERE EXISTS ( SELECT 1
FROM Value_table vt2
WHERE vt.value_id = vt2.value_id AND vt2.value = 'bye')
if any value column exists with 'bye' if that means the id does not have to match, you can remove the vt.value_id = vt2.value_id portion

CASE STRUCTURE IN A DB2 SQL UPDATE

I need to update a value (FIELD_E) in one table based on the value in a field of a second table as long as certain conditions are met. Here is what I am working with thus far.
TABLE_1.FIELD_C is a primary key
TABLE_2.FIELD_D is a foreign key
There is a ONE TO MANY relationship between TABLE_1.FIELD_C AND TABLE_2.FIELD_D
UPDATE TABLE_1
SET TABLE_1.FIELD_E = (
CASE
WHEN
(
SELECT MAX(FIELD_A)
FROM TABLE_1,
TABLE_2
WHERE TABLE_1.FIELD_C = TABLE_2.FIELD_D
AND TABLE_1.FIELD_A IS NOT NULL
AND TABLE_2.FIELD_B IS NULL) = 'Y' THEN '1'
WHEN
(
SELECT MAX(FIELD_A)
FROM TABLE_1,
TABLE_2
WHERE TABLE_1.FIELD_C = TABLE_2.FIELD_D
AND TABLE_1.FIELD_A IS NOT NULL
AND TABLE_2.FIELD_B IS NULL) = 'N' THEN '10'
WHEN
(
SELECT MAX(FIELD_A)
FROM TABLE_1,
TABLE_2
WHERE TABLE_1.FIELD_C = TABLE_2.FIELD_D
AND TABLE_1.FIELD_A IS NOT NULL
AND TABLE_2.FIELD_B IS NULL) = 'U' THEN '9'
ELSE NULL
END
WHERE TABLE_1.FIELD_C = TABLE_2.FIELD_D
AND TABLE_1.FIELD_A IS NOT NULL
AND TABLE_2.FIELD_B IS NULL
I know I could probably do this more simply by just taking a snapshot and writing multiple statements using primary keys but if the values were to change after the snapshot and before the update, an incorrect update could be made.
Can I get some suggestions on how to do this using a similar case structures statement?
Your query is doing several things you likely do not intend, besides the blatant syntax and construction errors. Making some assumptions about reasonable behavior, the query can be a lot simpler than your current effort:
UPDATE Table_1 SET field_e = CASE field_a WHEN 'Y' THEN '1'
WHEN 'N' THEN '10'
WHEN 'U' THEN '9'
ELSE null END
WHERE field_a IS NOT NULL
AND EXISTS (SELECT '1'
FROM Table_2
WHERE Table_2.field_d = Table_1.field_c
AND Table_2.field_b IS NULL)
SQL Fiddle Example
Your original query had the following behavior or other problems:
Your subqueries were certainly returning more than one row (error)
Your outermost SELECT mentions Table_2 without providing a reference (error)
There was no correlation between the outer UPDATE and any of the subqueries, meaning everything would be updated to have 1, regardless of the actual contents of a particular row's field.
I'm also not sure if you want the ELSE NULL in your CASE - this has the effect of erasing whatever value is currently in field_e if field_a doesn't have a matching value. Or rather, not restricting the update to those rows containing the values interested in (because ELSE null is implied if excluded).

Set of values such that no rows match condition

I have a table with column uuid and type. I want all the uuid's 'xxxxx' such that no rows have uuid = 'xxxxx' AND type = 'buy'.
This ends up the same as if you took all uuid's in the table, and then removed all uuid's that match SELECT uuid FROM table WHERE type = 'buy'.
I approach these problems using aggregation and a having clause:
select a_uuid
from table t
group by a_uuid
having sum(case when type = 'Purchase' then 1 else 0 end) = 0;
EDIT:
If you have a table with one row per a_uuid, then the fastest is likely to be:
select a_uuid
from adtbs a
where not exists (select 1 from table t where t.a_uuid = a.a_uuid and t.type = 'Purchase');
For this query, you want an index on table(a_uuid, type).
select a_uuid
from t
group by a_uuid
having not bool_or(type = 'Purchase')
http://www.postgresql.org/docs/current/static/functions-aggregate.html#FUNCTIONS-AGGREGATE-TABLE