SQL - WHERE clause on each SET command in UPDATE? - sql

I'm trying to create an SQL query in PHP to update a table.
Is it possible to have a different WHERE clause for each affected row?
eg something like:
UPDATE table
SET val=X WHERE someproperty = 1,
SET val=Y WHERE someproperty = 2
etc?
Any help appreciated. Thanks

Yes, you can with a CASE statement.
UPDATE table
SET val = CASE someproperty
WHEN 1 THEN x
WHEN 2 THEN y
....
ELSE
val
END
Now, there is concern that one CASE statement is less readable when compared to several UPDATE statements. There is a valid argument here. For example, when 1000 rows are being updated, it just feels and looks better to use several UPDATE statements rather than 1000 different conditions to a single CASE.
However, sometimes a CASE statement is more appropriate. If, for example, you are updating rows based on some trait, say the even or odd nature of a field's value the table, then a CASE statement is a wonderfully concise and maintainable way to update rows in the table without having to resort to a huge number of UPDATE statements that all share a specific type of logic. Take this for example:
UPDATE table
SET val = CASE MOD(someproperty, 2)
WHEN 0 THEN x
WHEN 1 THEN y
END
This expression takes the modulus of someproperty and, when 0 (even), assigns value x to val and, when 1 (odd), assigns value y to val. The greater the volume of data being updated by this statement, the cleaner it is compared to doing so by multiple UPDATE statements.
In short, CASE statements are sometimes just as readable/maintainable as UPDATE statements. It all depends on what you are trying to do with them.
EDIT: Added the ELSE clause to be extra safe. The OP may be interested in updating only specific rows so the rest should remain as they prior to the UPDATE.
EDIT: Added a scenario where the CASE statement is a more effective approach than multiple UPDATE statements.

You cannot have multiple WHERE clauses for any SQL statement, however you can use a CASE statement to accomplish what you are trying to do. Another option that you have is to execute multiple UPDATE statements.
Here is a sample using the CASE statement:
UPDATE table
SET val = (
CASE someproperty
WHEN 1 THEN X
WHEN 2 THEN Y
ELSE val
END
);
Here is a sample using multiple UPDATE statements:
UPDATE table SET val=X WHERE someproperty = 1;
UPDATE table SET val=Y WHERE someproperty = 2;

Nope. Make it two updates:
UPDATE table SET val=X WHERE someproperty = 1;
UPDATE table SET val=Y WHERE someproperty = 2;
On second thought, you could use sub-queries or the case statement...
UPDATE table SET val= ( case when someproperty = 1 then X when someproperty = 2 then Y else val END )
You may need to make that a sub query like this:
UPDATE table t1 SET val = ( select CASE when someproperty = 1 then X when someproperty = 2 then Y ELSE val END from table t2 where t1.primarykey = t2.primary key )

UPDATE TABLE
SET VAL CASE SOMEPROPERTY WHEN 1 THEN X WHEN 2 THEN Y END

A compact and easily scaleable way:
UPDATE table1 SET val=ELT(FIND_IN_SET(someproperty, '1, 2'), X, Y);
make the query this way:
$condition = array(1, 2);
$newvals = array('X', 'Y');
$query = "UPDATE table1 SET val=ELT(FIND_IN_SET(someproperty, '". implode(',', $condition). "', ". implode(', ', $newvals). ")";
Use prepare_query to avoid SQL syntax errors if you deal with string values.

Related

Multiple IF clause within WHERE clause in T-SQL

I have a table #t1 containing three columns, one of them named root_symbol.
I want to select all elements from table #t1 meeting the condition of variable #root.
However, variable #root can have four different values, thus I would like to dynamically select the filter applied by the where statement depending on the declared variable value.
declare #root integer
set #root = 1
select * from #t1
where root_symbol in
case when #root = 1 then ('ES')
when #root = 2 then ('EW', 'EW1', 'EW2', 'EW3', 'EW4')
when #root = 3 then ('E1C', 'E2C', 'E3C', 'E4C', 'E5C')
when #root = 4 then ('E1A', 'E2A', 'E3A', 'E4A', 'E5A')
end
I have seen this done using case when, however not with the value matching condition in.
An alternative would be to create an if statement with four different select statements inside, however also here I am not sure how to handle multi clause if statements in sql.
Any idea how to solve this?
Don't use case. Use simple comparisons:
where (#root = 1 and root_symbol in ('ES')) or
(#root = 2 and root_symbol in ('EW', 'EW1', 'EW2', 'EW3', 'EW4')) or
(#root = 3 and root_symbol in ('E1C', 'E2C', 'E3C', 'E4C', 'E5C')) or
(#root = 4 and root_symbol in ('E1A', 'E2A', 'E3A', 'E4A', 'E5A'))
In general, use case expressions in the where clause is a bad idea (there are some exceptions). Because the case forces the order of evaluation of its clauses, it prevents the optimizer from doing anything.

group function is not allowed here (sql-oracle)

I want to update saboloo with the following table
update sagani
set saboloo=sum(sagani.qula + sagani.shualeduri + sagani.finaluri)
where sagnis_id='9';
Aggregation is not allowed in an update, because update changes values in the rows in the table; once the data is aggregated, the connection to the original rows is lost.
I can imagine that you mean one of two things. The first would be a simple sum within the row:
update sagani
set saboloo = (sagani.qula + sagani.shualeduri + sagani.finaluri)
where sagnis_id = 9; -- looks like a number so I assume it is a number
Alternatively, you may want to update multiple rows with the same value added up from all those rows:
update sagani s
set saboloo = (select sum(s2.qula + s2.shualeduri + s2.finaluri)
from sagani s2
where s2.sagnis_id = s.sagnis_id
)
where s.sagnis_id = 9;
Your question doesn't have enough information to infer your intention, although the use of sagnis_id suggests that there is only one row and you don't want aggregation at all.
SUM is not applicable here as your requirement is very straight forward. You can try this following script for your purpose-
UPDATE sagani
SET saboloo=(sagani.qula + sagani.shualeduri + sagani.finaluri)
WHERE sagnis_id='9';

Merge query inserting but not updating on Oracle

I've got this query in a #SQLInsert annotation in Spring against an Oracle 11g database and, although it's inserting properly it is not updating the values but raising no error.
Any ideas? If not, any alternative ways of obtaining same behaviour?
merge INTO ngram sn
USING(SELECT ? AS frequency,
? AS occurrences,
? AS ngram
FROM dual) src
ON (sn.ngram = src.ngram)
WHEN matched THEN
UPDATE SET sn.occurrences = sn.occurrences + src.occurrences,
sn.frequency = sn.frequency + 1
WHEN NOT matched THEN
INSERT (ngram,
frequency,
occurrences)
VALUES (src.ngram,
src.frequency,
src.occurrences)
Update1: I'm adding the Entity def in Java for clarification.
#Entity(name = "NGRAM")
public class Ngram
{
#Id
#JsonProperty("ngram")
private String ngram;
#JsonProperty("frequency")
#Column(name = "frequency")
private int frequency;
#JsonProperty("occurrences")
#Column(name = "occurrences")
private int occurrences;
Update2: Adding a run of the SQL query on an existing Ngram
sql> merge INTO NGRAM sn
USING(SELECT 1 AS frequency,
200 AS occurrences,
'year' AS ngram
FROM dual) src
ON (sn.ngram = src.ngram)
WHEN matched THEN
UPDATE SET sn.occurrences = sn.occurrences + src.occurrences,
sn.frequency = sn.frequency + 1
WHEN NOT matched THEN
INSERT (ngram,
frequency,
occurrences)
VALUES (src.ngram,
src.frequency,
src.occurrences)
[2019-01-29 12:09:10] 1 row affected in 19 ms
This actually modifies the row, but not when I go over them in the code, so it seems the SQL is right...
Update3: So, this is the piece of code that should be inserting or updating but it's only doing the insert:
List<Ngram> lNgrams = new ArrayList<>(lCollocationsMap.size());
lCollocationsMap.forEach((pKey, pValue) -> lNgrams.add( new Ngram(pKey, 1, pValue)));
mNgramRepo.saveAll(lNgrams);
So I'll be investigating the saveAll behaviour.
Update4: I tried using save one by one which is much slower instead of using saveAll but got same behaviour. Changing ON (sn.ngram = src.ngram) to ON (sn.ngram LIKE src.ngram) makes some of the frequencies to become 2 (so they seem to be updated) but not all of them: 'year' for example appears more than 10 times but it's frequency remains 1 and it's occurrences is just updated with last value found.
So, I'm now completely lost on why this is failing, specially in this way.

Use CASE statement to test values in table 1, set values in table 2

I am new to SQL, so if you could include in answer correct syntax (for PostgreSQL) that will be great.
I have two tables
table 1 "geo_temp", with columns [geo_type1] [geo_type2]....[geo_type6] [geo_typeR];
table 2 "geo_summary", with column [geo].
Here what I want to do,
CASE
WHEN (geo_type1 NOTNULL) THEN (geo = geo_type1)
WHEN (geo_type2 NOTNULL) THEN (geo = geo_type2)
...
ELSE (geo = geo_typeR)
Your help is much appreciated. Thanks.
Use the coalesce() function:
geo = coalesce(geo1, geo2, ...,geoR)
coalesce() returns the first non-null value found in the list, which is what your intention is.
As an update statement:
update geo_summary set
geo = (select coalesce(geo1, geo2, ...,geoR)
from geo_temp
where geo_temp.id = geo_summary.id)
I am assuming you want to use this in a WHERE or ON Clause.
You could do something like...
geo =
CASE
WHEN (geo_type1 is NOT NULL) THEN geo_type1
WHEN (geo_type2 is NOT NULL) THEN geo_type2
...
ELSE geo_typeR
END

sqlite3 UPDATE generating nulls

I'm trying to transition from MySQL to SQLIte3 and running into an update problem. I'm using SQLite 3.6.20 on redhat.
My first line of code behaves normally
update atv_covar set noncomp= 2;
All values for noncomp (in the rightmost column) are appropriately set to 2.
select * from atv_covar;
A5202|S182|2
A5202|S183|2
A5202|S184|2
It is the second line of code that gives me problems:
update atv_covar
set noncomp= (select 1 from f4003 where
atv_covar.study = f4003.study and
atv_covar.rpid = f4003.rpid and
(rsoffrx="81" or rsoffrx="77"));
It runs without generating errors and appropriately sets atv_covar.noncomp to 1 where it matches the SELECT statement. The problem is that it changes atv_covar.noncomp for the non-matching rows to null, where I want it to keep them as 2.
select * from atv_covar;
A5202|S182|
A5202|S183|1
A5202|S184|
Any help would be welcome.
#Dan, the problem with your query is not specific to SQLite; you are updating all rows of atv_covar, but not all of them have correspondence in f4003, so these default to NULL. You should filter the update or provide a default value.
The following statement sets 1 only to the rows that macth the filtering condition:
UPDATE atv_covar
SET noncomp = 1
WHERE EXISTS (
SELECT 'x'
FROM f4003
WHERE atv_covar.study = f4003.study
AND atv_covar.rpid = f4003.rpid
AND (rsoffrx="81" or rsoffrx="77")
);
The following statement sets 1 or 2 for all rows of noncomp, depending on the filtering match (use this instead of two updates):
UPDATE atv_covar
SET noncomp = COALESCE((
SELECT 1
FROM f4003
WHERE atv_covar.study = f4003.study
AND atv_covar.rpid = f4003.rpid
AND (rsoffrx="81" or rsoffrx="77")
), 2);