Using CASE Statements In A SQL UPDATE Query

Posted August 30, 2007 at 7:35 AM by Ben Nadel

Tags: SQL

Nothing revolutionary here, I just recently tried to use a SQL CASE statement as part of an SQL UPDATE statement. I had never tried this before and I am quite happy that it works. I am working on a transactional system that allows for the voiding of account credits. As part of the Void process, I need to make sure that I never let an account balance drop below zero dollars (business requirement - these aren't "real" dollars). In something like ColdFusion, I would use the Max() function:

  • Max( 0, (balance - void_credit) )

But, in SQL, the MAX() function performs aggregate calculations on groups, not "max of two numbers" calculations. As such, I tried the following CASE statement:

  • <cfquery name="qUpdate" datasource="xxx">
  • UPDATE
  • [account]
  • SET
  • balance =
  • (
  • CASE
  • WHEN
  • ((balance - 10.00) < 0)
  • THEN
  • 0
  • ELSE
  • (balance - 10.00)
  • END
  • )
  • WHERE
  • id = 1
  • </cfquery>

I am hard coding the variables here, but you get the point. I think it's kind of cool that this works. I guess there's nothing about it that should have made me think that it wouldn't work; I just never tried this technique before. I love the fact that you can reference values in the database row that you are going to update. Very cool!



Reader Comments

Aug 30, 2007 at 8:00 AM // reply »
54 Comments

Nice work Ben,

I always like to see people leaning on thier database a little to do this kind of work for them, many people would simply break that in to two queries, the first being a SELECT and then use CF conditionals to determine the update type.

I'm a big fan of having SQL do this stuff for you, as CF developers its very easy to forget that SQL is an entire language all of its own and is MASSIVLY powerfull for this data manipulation stuff, it took me quite a while to earn full respect for SQL as its own language when I first started developing, but once I did you start thinking about things in a very different way.

Rob


Aug 30, 2007 at 8:25 AM // reply »
211 Comments

MySQL has a Greatest() function:
http://dev.mysql.com/doc/refman/5.1/en/comparison-operators.html#function_greatest

I'm sure the other servers have something equivalent. Otherwise, you'd probably want to use tsql or a stored proc.


Aug 30, 2007 at 9:15 AM // reply »
11,238 Comments

@Rob,

I know what you mean. SQL is awesomely powerful. I am still learning new things all the time. I still have yet to fully understand the whole CONVERT() function and I would love to learn more about Cursors and that sort of stuff.

@Todd,

It looks like MS SQL Server doesn't have the Greatest() equivalent, at least nothing that I could find on Google.


Aug 30, 2007 at 9:23 AM // reply »
211 Comments

@Ben: Yup, tsql it is. Anyway, your case statement works.


Aug 30, 2007 at 9:33 AM // reply »
5 Comments

Another nice technique that I have found invaluable on occasion is using a join in an update statement. I don't know if something like this works on MySQL but on SQL Server you can do something like the following

UPDATE p
SET
p.col1 = p.col1 + q.otherCol
FROM table1 AS p join table2 AS q
ON p.fk_table2_pk = q.pk
WHERE
q.filterColumn = 'something'


Aug 30, 2007 at 9:39 AM // reply »
11,238 Comments

@John,

That is awesome! I knew you could do something like that on a View, but I had no idea you could just update with join usage in a standard statement. Crazy!


Aug 30, 2007 at 10:46 AM // reply »
92 Comments

@Ben/John,

I had just learned you could do joins in an UPDATE SQL statement early last month. I should have guessed you could since the FROM statement is there and joining would making sense if you need to bring data from other locations. I learned how to use it because I needed to move data from one database to another (QA to DEV). It's easy to get lost in SQL but it can really do some neat stuff.


Aug 30, 2007 at 3:08 PM // reply »
17 Comments

It's amazing what SQL can do once you start digging into it.

A little gotcha about CASE statements is that you can only return simple values in your THEN clauses. It's a little less flexible than using dynamic CF in your queries, but as long as you are only looking to return simple values the CASE statement is very powerful.

Also check out IF and IFNULL. IFNULL is nice if for example, your column value is null but you want to return 0:

IFNULL(mycol,0) as col

This will return the column value if it is not null, or 0 if it is null. This can save a lot of extra CF coding if you happen to be in that situation.


Aug 30, 2007 at 3:11 PM // reply »
17 Comments

When I say simple values I mean you can't do something like this:

CASE WHEN 1
THEN 'Where colval = 1'
ELSE
'Where colval = 2'
END

In other words you can't change the structure of the query the way you can using dynamic CF.


Aug 30, 2007 at 7:26 PM // reply »
11 Comments

SQL can do many magical stuff.
I use SQL for all the business logic. I use ColdFusion for presentation.

CASE statement can be effectively used in ORDER BY :
http://blog.sqlauthority.com/2007/07/17/sql-server-case-statement-in-order-by-clause-order-by-using-variable/

People do create whole another SP (as another comment earlier) instead of using CASE.

I use other technologies with SQL but ColdFusion has been my favorite since day one.

Regards,
Pinal


Sep 4, 2007 at 10:38 AM // reply »
11,238 Comments

@John Eric,

I just tried your technique (UPDATE + JOIN) and it works like a charm! Very cool stuff:

http://www.bennadel.com/index.cfm?dax=blog:938.view

Thanks for the hot tip.


Oct 23, 2008 at 11:54 AM // reply »
2 Comments

Hi,

What if I want to "switch" the field name? Your example is
Update xxx
Set balance = (Case
.....
End)
Where ......

But I want to do like this:
Update xxx
Set (Case
.....
End) = 1000
Where ......

I have tried this syntax, but it doesn't work. What should I do?
Thanks.


Nov 3, 2008 at 9:39 AM // reply »
11,238 Comments

@Kenny,

Are you trying to dynamically select the actual column to update? Could this be something that would be done more effectively using the parent language (ColdFusion, ASP, etc):

UPDATE xxx
<cfif A>
SET a = 1000
<cfelse>
SET b = 1000
</cfif>
WHERE....

Is that what you're trying to do?


Nov 3, 2008 at 11:21 AM // reply »
2 Comments

Yes, that's what I am trying to do. I have many stored procedures update the same table but different fields, and I am thinking to merge them into one sproc. By doing so, the front end developer can just call the same sproc and do several update processes by varying the keywords.


Nov 3, 2008 at 1:29 PM // reply »
11,238 Comments

@Kenny,

You can probably build a dynamic SQL statement and then EXEC() it or something. I've never done this personally, but I think this is how a lot of people handle pagination.


Mar 25, 2009 at 4:33 PM // reply »
2 Comments

I have the following SQL statement that meets all the requirements of what I am trying to do?

SELECT Diff_Adj,
Case
When Diff_Adj < 0
Then '-'+right ('00000000'+convert (varchar(10), convert (int, -Diff_Adj*100)), 9)
Else right ('000000000'+convert (varchar(10), convert (int, Diff_Adj*100)), 10)
End
FROM dbo.Tran405_CC1

?and these are the results:

Diff_Adj
0000000100
0000000000
0000000000
-000000100
-008638200

Now, instead of running a SELECT statement, I need to run an UPDATE statement to modify data in my table, here is the Update statement I am using to do so?

UPDATE dbo.Tran405_CC1
SET Diff_Adj =
CASE
WHEN Diff_Adj < 0
THEN '-'+right ('00000000'+convert (varchar(10), convert (int, -Diff_Adj*100)), 9)
ELSE right ('000000000'+convert (varchar(10), convert (int, Diff_Adj*100)), 10)
END

?and these are my results:

Diff_Adj
1.00
.00
.00
-1.00
-86382.00

FYI?the data type for Diff_Adj column is Decimal (10, 2)

Thanks in advance


Mar 25, 2009 at 4:35 PM // reply »
2 Comments

CORRECTED CODE

I have the following SQL statement that meets all the requirements of what I am trying to do?

SELECT Diff_Adj,
Case
When Diff_Adj < 0
Then '-'+right ('00000000'+convert (varchar(10), convert (int, -Diff_Adj*100)), 9)
Else right ('000000000'+convert (varchar(10), convert (int, Diff_Adj*100)), 10)
End
FROM dbo.Tran405_CC1

?and these are the results:

Diff_Adj
0000000001
0000000000
0000000000
-000000001
-000086382

Now, instead of running a SELECT statement, I need to run an UPDATE statement to modify data in my table, here is the Update statement I am using to do so?

UPDATE dbo.Tran405_CC1
SET Diff_Adj =
CASE
WHEN Diff_Adj < 0
THEN '-'+right ('00000000'+convert (varchar(10), convert (int, -Diff_Adj*100)), 9)
ELSE right ('000000000'+convert (varchar(10), convert (int, Diff_Adj*100)), 10)
END

?and these are my results:

Diff_Adj
1.00
.00
.00
-1.00
-86382.00

FYI?the data type for Diff_Adj column is Decimal (10, 2)

Thanks in advance


Ric
Feb 4, 2010 at 7:22 AM // reply »
1 Comments

Brilliant, I was getting some syntax errors when using case during an update statement, but having seen this, the error is now corrected.

Good job.


Feb 4, 2010 at 9:17 PM // reply »
11,238 Comments

@Ric,

Awesome! Glad you got it working.


Apr 3, 2010 at 1:57 AM // reply »
1 Comments

thank u very much...It s very use full


May 21, 2010 at 10:28 PM // reply »
1 Comments

Hi Ben

please show me how to update a single row
of a particular table by joining with another
table in MySql.Is it really possible?


May 22, 2010 at 9:32 AM // reply »
11,238 Comments

@Goutam,

Take a look at this post:

http://www.bennadel.com/blog/938-Using-A-SQL-JOIN-In-A-SQL-UPDATE-Statement-Thanks-John-Eric-.htm

That post is for MS SQL, I am pretty sure, but MySQL supports the same thing with a slightly different syntax. You might just have to Google the MySQL specific syntax - the difference is just where the table join is actually performed.


Jun 26, 2010 at 8:08 AM // reply »
3 Comments

hello ben,
i want to use the case statment in the Storeprocedure like this can u tell me it is a urgent

example

create proc Testing
(
@jobnumber nvarchar (100),
@flag int
)
begin

update tablename
jobnumber=(case when @flag = 0 than @jobnumber )
end

I have used like this but it showing syntex error so please tell the actual case statment for the storeprocedure ! it is the urgent


Jun 26, 2010 at 8:14 AM // reply »
4 Comments

CREATE PROC Testing
(
@jobnumber NVARCHAR (100),
@flag INT
)
AS
BEGIN
UPDATE tablename
SET jobnumber=CASE WHEN @flag = 0 THEN @jobnumber ELSE jobnumber END
END


Jun 26, 2010 at 8:26 AM // reply »
3 Comments

hi Pinal,

I have already use like this but when i use END of case statment than it take the END for the BEGIN not for the case statment.

but anyways maybe i have use some another line there. i will try to use this.

please make comment on my this comment


Jun 29, 2010 at 10:07 AM // reply »
11,238 Comments

@Ben,

I will defer to @Pinal in this case. I know nothing about stored procedures.


Jul 16, 2010 at 2:34 PM // reply »
2 Comments

What about if i only want to update on true ignoring the else?

CASE WHEN 1>0 THEN
UPDATE table field='true' WHERE field='false'
END;


Jul 18, 2010 at 11:37 AM // reply »
11,238 Comments

@Alqin,

I am not sure I understand. If you know the value before the UPDATE statement, then you can always just choose not to run the query at all?


Jul 18, 2010 at 4:04 PM // reply »
2 Comments

@Ben,
I'm playing around with a custom autoincrement.
For example if I delete all entries from mammal category, and there were like 5, when I insert new row the increment has to start from 6.
So...
To remember the last incremented value, I thought that something like DELETE only if there is at least one more entry in the mammal category otherwise update by making null the output data, then on new entry start with an update instead of insert.

Now the real question here is:
I'm using php. If i use the function mysql_query($query) one time to count() if there is one more entry then another time for deleting or updating depending on count(), if other users are updating the database doesn't the incremented value gets messed up?

I used $mysqli->multi_query($query) to execute multiple mysql queries in one $query then i discover that i cant use if() or case() to chose to update or delete the database. So this force my to use at least two php $query calls.

Now I'm curios about the answer on the real question...

Sorry for the long message...


Jul 20, 2010 at 9:43 PM // reply »
11,238 Comments

@Alqin,

I don't quite follow your auto-increment stuff, but I think I understand your primary question. When I moved from SQL Server to MySQL, this is the first big thing that stumped me. I used to have conditions around queries all the time. Now that I know what you mean, I can totally relate. I used to do things like this:

SET @id = SELECT....

IF (@id IS NOT NULL) THEN
.... UPDATE
ELSE
.... INESRT
END IF

When I switched to MySQL, I had to break apart all kinds of queries. You can still run multiple queries in a MySQL environment; but, you can't conditionally execute individual queries.

I don't have great advice on this. The best thing I can say is that you might want to wrap the multiple queries in an exclusive-lock. This way, you won't get any dirty reads.

Sorry I couldn't be more helpful.


Aug 11, 2010 at 10:27 AM // reply »
3 Comments

What you wrote is fine.
Tell me how will you write a sql where you have to update one field only if its null where as other fields should be updated at the same time with no condition.

UPDATE TABLE
SET

A (only if its null)= B.a
,X= B.x
,Y = B.y


Aug 11, 2010 at 10:38 AM // reply »
3 Comments

@ABHAY,

NEVERMIND I GOT IT, btw i am using MERGE i hope the same logic can be applied for simple update aswell.

UPDATE SET

RTRMNT_FG =
CASE
WHEN TARGET.RTRMNT_FG IS NULL THEN SOURCE.RTRMNT_FG
--UPDATE ONLY WHEN ITS NULL
END
,
DT_OF_RTRMNT = SOURCE.DT_OF_RTRMNT
,
UPD_TS = GETDATE()

thanks guys.


Aug 11, 2010 at 10:43 AM // reply »
11,238 Comments

@Abhay,

Are you sure that will always work? Since your CASE statement only has one condition, I am afraid that it will return NULL when that condition is not met.


Aug 11, 2010 at 10:51 AM // reply »
1 Comments

@ABHAY,

Ben is absolutely correct. You should use following code.

UPDATE SET

RTRMNT_FG =
ISNULL(TARGET.RTRMNT_FG,SOURCE.RTRMNT_FG)
--UPDATE ONLY WHEN ITS NULL
,
DT_OF_RTRMNT = SOURCE.DT_OF_RTRMNT
,
UPD_TS = GETDATE()


Aug 11, 2010 at 10:56 AM // reply »
11,238 Comments

@Pinal,

Ah, awesome stuff. I was thinking about using a WHEN/ELSE suggestiong; but the ISNULL() is much more to the point!


Sep 11, 2010 at 2:15 PM // reply »
3 Comments

@Ben, @pinal

Thanks Guys, I didnt realize this earlier.
COALESCE should do the same.
Cheerz


May 3, 2011 at 3:48 PM // reply »
1 Comments

awesome blog. :)


Nov 16, 2011 at 9:27 AM // reply »
1 Comments

@Ben- Thanks for posting this..

@Pinal,

I am wondering whether it has any performance issues or whether IF is a better choice here.

This is what i did....

UPDATE MyTable
SET MyColumn=CASE WHEN @MyVariable IS NOT NULL THEN @MyVariable ELSE MyColumn END
WHERE
<some condition>

Will be good if i get a (CASE Vs IF) interms of Performance. Any Idea??.

Thanks,
Ananthan



Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 21, 2013 at 7:46 PM
Using Plupload For Drag & Drop File Uploads In ColdFusion
No luck. At least I have uncovered the cause, URLScan 3.1. Here is what I see in the IIS log when a file is over 30mb. 2013-05-21 23:29:05 10.105.45.128 GET /plupload/assets/jquery/jquery-1.8. ... read »
May 21, 2013 at 6:12 PM
Using Plupload For Drag & Drop File Uploads In ColdFusion
Ben, I did not see you after Pete Freitag's Lockdown session at cfObjective but he said that IIS sets file size limits at 30MB by default which just happened to be the threshold for file size when ... read »
May 21, 2013 at 11:51 AM
Ask Ben: Parsing Very Large XML Documents In ColdFusion
Looking at my first ever XML document that I have to parse and put into MS SQL 2000 with CF8. I get it to list the desired Field name, many times over, and have a long list of this field name displa ... read »
May 21, 2013 at 9:25 AM
Turning Off and On Identity Column in SQL Server
you are awesome..i am lucky to get this blog between such a garbage one....Thanks, Prashant ... read »
May 20, 2013 at 4:38 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Dana, Your confusion is well founded, since this is a very confusing features. In fact, it ONLY works if you use array notation. Meaning, that this: arrayToList( query[ "columnName" ] ) ... read »
May 20, 2013 at 4:34 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
I was thinking chicken and the egg, I wouldn't have expected it to work in the valuelist going in I guess. Maybe I just need a beer, long day :) ... read »
May 20, 2013 at 4:29 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
@Dana, That's if you're trying to reference a specific row. In this case, we're trying to reference the entire query column as one cohesive value. So, you are correct that if you wanted to output a ... read »
May 20, 2013 at 4:24 PM
Using A Dynamic Column Name With ValueList() In ColdFusion
I thought when you used array notation to reference queries you always had to have the row or it would throw a similar error as well? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools