CASE STRUCTURE IN A DB2 SQL UPDATE - sql

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

Related

update value only when value changed

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

Use of CASE with criteria from multiple tables

I have to do a select query to create a view with specific criteria.
I have multiple tables which contains many many columns and lines.
However, I have extracted a value to use as my key (e.g.: id). I have 7000+ of those unique keys that I extracted from all my tables with the function UNION to avoid duplicates.
Now, I want to add a column INDICATOR_1 which will affect the value YES or NO based on criteria.
This is where I struggle.
I need to find the line in those tables that contain the id. After that, I'd like to check, always in that line, if the field XYZ contains the value 'N' (example). If yes, affect the value 'YES' to INDICATOR_1, else it's no.
In a matter of pseudo-code, what I want to do looks like this :
CASE
WHEN id = (id from table_1) AND (if table_1.xyz = 'N')
THEN 'YES'
ELSE 'NO'
END AS INDICATOR_1
I don't know if I'm clear enough, but your help will be greatly appreciated.
If I understand correctly, you want a separate indicator for each table. Something like this:
select i.*,
(case when exists (select 1
from table1 t1
where t1.id = i.id and t1.xyz = 'N'
)
then 'YES' else 'NO'
end) as indicator_1,
(case when exists (select 1
from table2 t2
where t2.id = i.id and t2.xyz = 'N'
)
then 'YES' else 'NO'
end) as indicator_2,
. . .
from (<your id list here>) i
I think you should fix this in the union, where you have all the data you need. You probably have something like:
SELECT Id
FROM table_1
UNION
SELECT Id
FROM table_2
How about selecting the information you want as well (I use distinct here to clarify):
SELECT DISTINCT Id
, CASE WHEN table_1.xyz = 'N' THEN 'N'
ELSE 'Y'
END INDICATOR_1
FROM table_1
This can lead to more records than you had, if id's can have records of both flavours exist. We can fix that with a row number in an outer query. You end up with something like:
SELECT Id
, INDICATOR_1
FROM (
SELECT Id
, INDICATOR_1
, ROW_NUMBER()OVER(PARTITION BY ID ORDER BY CASE WHEN INDICATOR_1 ='N' THEN 0 ELSE 1 END) RN
FROM (
SELECT Id
, CASE WHEN table_1.xyz = 'N' THEN 'N'
ELSE 'Y'
END INDICATOR_1
FROM table_1
UNION
...
) T
) S
WHERE S.RN = 1
You can in fact shorten that by using the inner most case expression in the ROW_NUMBER expression.

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 when statement in SQL

I am using the following query. In this query I want to apply the where clause based on passed parameter. But the issue is that where clause is like 'value = if parameterVal = 'I' than NULL else NOT NULL'
I've build a query like this
SELECT * FROM MASTER
WHERE
Column1 IS (CASE WHEN :Filter = 'I' THEN 'NULL' ELSE 'NOT NULL' END)
but it's not working. Help me solve this.
UPDATE
Updating question to elaborate question more clearly.
I've one table MASTER. Now I am passing one parameter in query that is Filter (indicated by :Filter in query).
Now when the Filter parameter's value is 'I' than it should return the following result.
SELECT * FROM MASTER WHERE Column1 IS NULL
but if the passed argument is not equal to 'I' than,
SELECT * FROM MASTER WHERE Column1 IS NOT NULL
SELECT * FROM MASTER
WHERE (Filter = 'I' AND Column1 IS NULL)
OR
(Filter <> 'I' AND Column1 IS NOT NULL)
If you really insist on using a CASE the SELECT could be rewritten as:
SELECT *
FROM MASTER
WHERE CASE
WHEN COLUMN1 IS NULL AND FILTER = 'I' THEN 1
WHEN COLUMN1 IS NOT NULL AND FILTER <> 'I' THEN 1
ELSE 0
END = 1
SQLFiddle here
Frankly, though, I think that this is very difficult to interpret, and I suggest that #MAli's version is better.
Your case has assignment not equality check

Sql statement for mutually exclusive events

I am trying to run an sql statement on an iSeries that will output retuls based on a type parameter I pass in.
Just say mytable has a field called field1. field1 contains Y,N and NULL values.
A type of 'Y' should return just 'Y' values.
A type of 'N' should return not 'Y' values. (ie. Null, N and any other junk in the field)
I tried this...
select *
from mytable
where field1 in case when :type = 'Y' then 'Y'
else (select field1 from mytable where field1 <> 'Y') end
However, this does not work.
I believe the logic you are looking for is this:
SELECT *
FROM myTable
WHERE (:type = 'Y' AND field1 IS NOT null AND field1 = 'Y')
OR (:type <> 'Y' AND (field1 IS null OR field1 <> 'Y'))
(keep in mind the fact that short-circuit logic is not garuanteed with SQL...)
Remember that null doesn't really compare to anything, and it's best to call out the fact that you actually want it (in the second case).