Writing CASE Statement in SQL - sql

I have a requirement to display two columns in a report,whose values will be determined using the same input expression as following:
SELECT
CASE WHEN id>10
THEN 'AAAA'
ELSE
'BBBB'
END as 'FirstColumn',
CASE WHEN id>10
THEN
'BBBB'
ELSE
'DDDD' END as 'SecondColumn'
Can I construct this expression without repeating input expression twice as they are same?

You will need to repeat the CASE statment for each field as while the condition might be the same teh results are differnt. The only other alternative is to use a UNion all statement whenre the first select takes the records WHERE id<=10 and the other one does the WHERE ID>10. This can bea viable alternative but it is a littel hareder to maintain, so if the performance is good enough, Iwoudl stick to repeating teh CASE condition.

If some sophisticated is supposed instead of id>10 then, to make it a bit shorter and a bit more readable:
select
IIF(p.b=1, 'AAA', 'BBB') [Col1],
IIF(p.b=1, 'BBB', 'DDD') [Col2]
from
TableName t
outer apply (select cast(case when t.id>10 then 1 else NULL end as bit) as b) p
However, this is only available in SqlServer 2012. In earlier versions, parhaps, you will have to write your own IIF-like scalar function:
create function dbo.IIF (#b bit, #ifValue varchar(50), #elseValue varchar(50))
returns varchar(50)
as begin
return (select case when #b = 1 then #ifValue else #elseValue end)
end
GO
select
dbo.IIF(p.b, 'AAA', 'BBB') [Col1],
dbo.IIF(p.b, 'BBB', 'DDD') [Col2]
from
TableName t
outer apply (select cast(case when t.id>10 then 1 else NULL end as bit) as b) p

If it's worth the trouble, you could created a User-defined function (UDF) that would do this transforamation logic:
CREATE FUNCTION dbo.fColTransform(#id Int)
RETURNS Varchar(20)
AS BEGIN
DECLARE #ret Varchar(20)
SET #ret =
CASE
WHEN #id IS NULL THEN 'Unknown'
WHEN #id > 10 THEN 'BBB'
ELSE 'DDD'
END
RETURN #ret
END
Then, in your SELECT, you could structure it like this:
SELECT dbo.fColTransform(id1) AS FirstColumn,
dbo.fColTransform(id2) AS SecondColumn
FROM MyTable

You can store it in a variable and use Execute to call it:
Declare #foo as nvarchar(max)
set #foo='MyTable'
execute ('Select * from ' + #foo)
The downside is that your entire query would be all red (since it's now a string).

Try with this query, maybe will be useful (in the case if you do not use a function):
WITH temp AS (
SELECT
CASE WHEN x.ID > 10
THEN 'AAAA'
ELSE 'BBBB'
END as Col
FROM (
SELECT 1 AS ID
UNION
SELECT 11 AS ID
UNION
SELECT 13 AS ID
UNION
SELECT 9 AS ID
UNION
SELECT 7 AS ID
) x
)
SELECT temp.Col AS FirstColumn, temp.Col AS SecondColumn
FROM temp
Basically:
With name As (
/*yourquery with one CASE-COLUMN*/
)
SELECT name.COL AS COLUMN1, name.COL AS COLUMN2 FROM name
You can try this here

Related

Conditionally modify query based on parameter

I have this query (something like a case statement which I can use and fix it)
select *
from mytable
where 1=1
and (isNull(ID, 0) = 0 OR UtilityID IN (9,40))
I also want to add another statement
select *
from mytable
where 1=1
and UtilityID NOT IN (9,40)
Everything is happening in a procedure, so want to use a variable like declare #something so if that is passed as 1, use the first statement and the if 0 is passed, use the latter one.
While I appreciate the genius in Dale's answer I find this more readable:
IF #something = 0
BEGIN
select *
from mytable
where ID IS NULL OR ID = 0 OR UtilityID IN (9,40);
END
IF #something = 1
BEGIN
select *
from mytable
where UtilityID NOT IN (9,40);
END
It's procedure code, so use IF to direct the control flow. Also expanded and simplified your where clauses
I think I understand your logic, ignoring the 1=1 (which does nothing) you want to only allow id = 0 when #something = 1. This should do it:
declare #something bit = 0;
declare #mytable table (ID int, UtilityID int);
insert into #mytable (ID, UtilityID)
select 0, 1 union all
select 1, 2 union all
select 2, 9 union all
select 3, 40;
select *
from #mytable
where (
(#something = 1 and (isnull(ID, 0) = 0 or UtilityID in (9,40)))
or (#something = 0 and (UtilityID not in (9,40)))
);
A more performant approach for a larger query could be:
select *
from #mytable
where (#something = 1 and (isnull(ID, 0) = 0 or UtilityID in (9,40)))
union all
select *
from #mytable
where (#something = 0 and (UtilityID not in (9,40)));
PS: Hopefully your ID cannot ever by null - it should have a constraint on it.

Removal of Cursor in SQL

Its always been discouraged to use cursor, which have been extensively used in our current stored procedures and replace them with set based queries. But this particular scenario is one, where I did not get solution to use set based query and forced to keep using the cursor. I am providing below sample code which represents the scenario:
DECLARE #temp varchar(10), #continuechar varchar(10)
DECLARE #table1 table (col1 varchar(10))
insert into #table1
select 'A' UNION
select 'B' UNION
select 'C' UNION
select 'D' UNION
select 'E' UNION
select 'F' UNION
select 'G'
DECLARE Cursor1 CURSOR for select Col1 from #table1
open Cursor1
FETCH NEXT from Cursor1 into #temp
WHILE ##FETCH_STATUS = 0
BEGIN
if #temp='A'
BEGIN
set #continuechar=#temp
END
if #temp='C'
BEGIN
set #continuechar=#temp
END
select #continuechar, #temp
FETCH NEXT from Cursor1 into #temp
END
CLOSE cursor1;
deallocate cursor1
Here in above sample code #continuechar variable is not getting set, every time cursor is getting executed. If #continuechar is getting set, then following select statement is providing result set with current value of #continuechar:
select #continuechar, #temp
if its not getting set, then its using the previously set value to provide result set.
Can we have set based queries to remove cursor from such scenario.
First I would add some id column to get stable sort. Then simply use windowed functions:
SUM() OVER() to calculate groups
FIRST_VALUE() OVER() to propagate first value across group
(present from SQL Server 2012, you could exchange it with MAX(continuechar) OVER(PARTITION BY grp) if necessary)
DECLARE #table1 table (id INT IDENTITY(1,1), col1 varchar(10))
insert into #table1
select 'A' UNION
select 'B' UNION
select 'C' UNION
select 'D' UNION
select 'E' UNION
select 'F' UNION
select 'G';
WITH cte AS (
SELECT id
,col1
,CASE WHEN col1 IN('A', 'C') THEN col1 END AS continuechar
,SUM(CASE WHEN col1 IN ('A', 'C') THEN 1 ELSE 0 END)
OVER(ORDER BY id) AS grp
FROM #table1
)
SELECT id, col1,
FIRST_VALUE(continuechar) OVER(PARTITION BY grp ORDER BY id) AS continuechar
FROM cte
ORDER BY id;
DBFiddle Demo
EDIT:
Quirky update This is for pure demo only. Do not use this method on production system:
DECLARE #table1 table (id INT IDENTITY(1,1) PRIMARY KEY, col1 varchar(10),
continue_char VARCHAR(10));
DECLARE #temp VARCHAR(10);
insert into #table1(col1)
select 'A' UNION
select 'B' UNION
select 'C' UNION
select 'D' UNION
select 'E' UNION
select 'F' UNION
select 'G';
UPDATE #table1
SET #temp = CASE WHEN col1 IN ('A','C') THEN col1 ELSE #temp END
,continue_char = #temp
OPTION(MAXDOP 1);
SELECT *
FROM #table1;
DBFiddle Demo2

Select with IN and Like

I have a very interesting problem. I have an SSRS report with a multiple select drop down.
The drop down allows to select more than one value, or all values.
All values is not the problem.
The problem is 1 or the combination of more than 1 option
When I select in the drop down 'AAA' it should return 3 values: 'AAA','AAA 1','AAA 2'
Right now is only returning 1 value.
QUESTION:
How can make the IN statement work like a LIKE?
The Drop down select
SELECT '(All)' AS team, '(All)' AS Descr
UNION ALL
SELECT 'AAA' , 'AAA'
UNION ALL
SELECT 'BBB' , 'BBB'
Table Mytable
ColumnA Varchar(5)
Values for ColumnA
'AAA'
'AAA 1'
'AAA 2'
'BBB'
'BBB 1'
'BBB 2'
SELECT * FROM Mytable
WHERE ColumnA IN (SELECT * FROM SplitListString(#Team, ',')))
Split function
CREATE FUNCTION [dbo].[SplitListString]
(#InputString NVARCHAR(max), #SplitChar CHAR(1))
RETURNS #ValuesList TABLE
(
param NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #ListValue NVARCHAR(max)
DECLARE #TmpString NVARCHAR(max)
DECLARE #PosSeparator INT
DECLARE #EndValues BIT
SET #TmpString = LTRIM(RTRIM(#InputString));
SET #EndValues = 0
WHILE (#EndValues = 0) BEGIN
SET #PosSeparator = CHARINDEX(#SplitChar, #TmpString)
IF (#PosSeparator) > 1 BEGIN
SELECT #ListValue = LTRIM(RTRIM(SUBSTRING(#TmpString, 1, #PosSeparator -1 )))
END
ELSE BEGIN
SELECT #ListValue = LTRIM(RTRIM(#TmpString))
SET #EndValues = 1
END
IF LEN(#ListValue) > 0 BEGIN
INSERT INTO #ValuesList
SELECT #ListValue
END
SET #TmpString = LTRIM(RTRIM(SUBSTRING(#TmpString, #PosSeparator + 1, LEN(#TmpString) - #PosSeparator)))
END
RETURN
END
You can't. But, you can make the like work like the like:
select *
from mytable t join
SplitListString(#Team, ',') s
on t.ColumnA like '%'+s.param+'%'
That is, move the split list to an explicit join. Replace with the actual column name returned by the function, and use the like function.
Or, if you prefer:
select *
from mytable t cross join
SplitListString(#Team, ',') s
where t.ColumnA like '%'+s.param+'%'
The two versions are equivalent and should produce the same execution plan.
Better approach would be to have a TeamsTable (teamID, teamName, ...) and teamMembersTable (teamMemberID, teamID, teamMemberDetails, ...).
Then you an build your dropdown list as
SELECT ... FROM TeamsTable ...;
and
SELECT ... FROM teamMembersTable WHERE teamID IN (valueFromYourDropDown);
Or you can just store your teamID or teamName (or both) in your (equivalent of) teamMembersTable
You're not going to get IN to work the same as LIKE without a lot of work. You could do something like this though (and it would be nice to see some of your actual data though so we could give better solutions):
SELECT *
FROM table
WHERE LEFT(field,3) IN #Parameter
If you'd like better performance, create a code field on your table and update it like this:
UPDATE table
SET codeField = LEFT(field,3)
Then just add an index on that field and run this query to get your results:
SELECT *
FROM table
WHERE codeField IN #Parameter

is it possible to select EXISTS directly as a bit?

I was wondering if it's possible to do something like this (which doesn't work):
select cast( (exists(select * from theTable where theColumn like 'theValue%') as bit)
Seems like it should be doable, but lots of things that should work in SQL don't ;) I've seen workarounds for this (SELECT 1 where... Exists...) but it seems like I should be able to just cast the result of the exists function as a bit and be done with it.
No, you'll have to use a workaround.
If you must return a conditional bit 0/1 another way is to:
SELECT CAST(
CASE WHEN EXISTS(SELECT * FROM theTable where theColumn like 'theValue%') THEN 1
ELSE 0
END
AS BIT)
Or without the cast:
SELECT
CASE
WHEN EXISTS( SELECT 1 FROM theTable WHERE theColumn LIKE 'theValue%' )
THEN 1
ELSE 0
END
SELECT CAST(COUNT(*) AS bit) FROM MyTable WHERE theColumn like 'theValue%'
When you cast to bit
0 -> 0
everything else -> 1
And NULL -> NULL of course, but you can't get NULL with COUNT(*) without a GROUP BY
bit maps directly to boolean in .net datatypes, even if it isn't really...
This looks similar but gives no row (not zero) if no matches, so it's not the same
SELECT TOP 1 CAST(NumberKeyCOlumn AS bit) FROM MyTable WHERE theColumn like 'theValue%'
You can use IIF and CAST
SELECT CAST(IIF(EXISTS(SELECT * FROM theTable
where theColumn like 'theValue%'), 1, 0) AS BIT)
I'm a bit late on the uptake for this; just stumbled across the post. However here's a solution which is more efficient & neat than the selected answer, but should give the same functionality:
declare #t table (name nvarchar(16))
declare #b bit
insert #t select N'Simon Byorg' union select N'Roe Bott'
select #b = isnull((select top 1 1 from #t where name = N'Simon Byorg'),0)
select #b whenTrue
select #b = isnull((select top 1 1 from #t where name = N'Anne Droid'),0)
select #b whenFalse
You can also do the following:
SELECT DISTINCT 1
FROM theTable
WHERE theColumn LIKE 'theValue%'
If there are no values starting with 'theValue' this will return null (no records) rather than a bit 0 though
SELECT IIF(EXISTS(SELECT * FROM theTable WHERE theColumn LIKE 'theValue%'), 1, 0)
No it isn't possible. The bit data type is not a boolean data type. It is an integer data type that can be 0,1, or NULL.
Another solution is to use ISNULL in tandem with SELECT TOP 1 1:
SELECT ISNULL((SELECT TOP 1 1 FROM theTable where theColumn like 'theValue%'), 0)
I believe exists can only be used in a where clause, so you'll have to do a workaround (or a subquery with exists as the where clause). I don't know if that counts as a workaround.
What about this:
create table table1 (col1 int null)
go
select 'no items',CONVERT(bit, (select COUNT(*) from table1) ) -- returns 'no items', 0
go
insert into table1 (col1) values (1)
go
select '1 item',CONVERT(bit, (select COUNT(*) from table1) ) --returns '1 item', 1
go
insert into table1 (col1) values (2)
go
select '2 items',CONVERT(bit, (select COUNT(*) from table1) ) --returns '2 items', 1
go
insert into table1 (col1) values (3)
go
drop table table1
go

Is there a Max function in SQL Server that takes two values like Math.Max in .NET?

I want to write a query like this:
SELECT o.OrderId, MAX(o.NegotiatedPrice, o.SuggestedPrice)
FROM Order o
But this isn't how the MAX function works, right? It is an aggregate function so it expects a single parameter and then returns the MAX of all rows.
Does anyone know how to do it my way?
If you're using SQL Server 2008 (or above), then this is the better solution:
SELECT o.OrderId,
(SELECT MAX(Price)
FROM (VALUES (o.NegotiatedPrice),(o.SuggestedPrice)) AS AllPrices(Price))
FROM Order o
All credit and votes should go to Sven's answer to a related question, "SQL MAX of multiple columns?"
I say it's the "best answer" because:
It doesn't require complicating your code with UNION's, PIVOT's,
UNPIVOT's, UDF's, and crazy-long CASE statments.
It isn't plagued with the problem of handling nulls, it handles them just fine.
It's easy to swap out the "MAX" with "MIN", "AVG", or "SUM". You can use any aggregate function to find the aggregate over many different columns.
You're not limited to the names I used (i.e. "AllPrices" and "Price"). You can pick your own names to make it easier to read and understand for the next guy.
You can find multiple aggregates using SQL Server 2008's derived_tables like so: SELECT MAX(a), MAX(b) FROM (VALUES (1, 2), (3, 4), (5, 6), (7, 8), (9, 10) ) AS MyTable(a, b)
Can be done in one line:
-- the following expression calculates ==> max(#val1, #val2)
SELECT 0.5 * ((#val1 + #val2) + ABS(#val1 - #val2))
Edit: If you're dealing with very large numbers you'll have to convert the value variables into bigint in order to avoid an integer overflow.
You'd need to make a User-Defined Function if you wanted to have syntax similar to your example, but could you do what you want to do, inline, fairly easily with a CASE statement, as the others have said.
The UDF could be something like this:
create function dbo.InlineMax(#val1 int, #val2 int)
returns int
as
begin
if #val1 > #val2
return #val1
return isnull(#val2,#val1)
end
... and you would call it like so ...
SELECT o.OrderId, dbo.InlineMax(o.NegotiatedPrice, o.SuggestedPrice)
FROM Order o
I don't think so. I wanted this the other day. The closest I got was:
SELECT
o.OrderId,
CASE WHEN o.NegotiatedPrice > o.SuggestedPrice THEN o.NegotiatedPrice
ELSE o.SuggestedPrice
END
FROM Order o
Why not try IIF function (requires SQL Server 2012 and later)
IIF(a>b, a, b)
That's it.
(Extra hint: be careful about either a or b is null, as in this case the result of a>b will be false. So b will be the return result if either is null) (Also by system design, column null is not a good practice)
DECLARE #MAX INT
#MAX = (SELECT MAX(VALUE)
FROM (SELECT 1 AS VALUE UNION
SELECT 2 AS VALUE) AS T1)
In SQL Server 2012 or higher, you can use a combination of IIF and ISNULL (or COALESCE) to get the maximum of 2 values.
Even when 1 of them is NULL.
IIF(col1 >= col2, col1, ISNULL(col2, col1))
Or if you want it to return 0 when both are NULL
IIF(col1 >= col2, col1, COALESCE(col2, col1, 0))
Example snippet:
-- use table variable for testing purposes
declare #Order table
(
OrderId int primary key identity(1,1),
NegotiatedPrice decimal(10,2),
SuggestedPrice decimal(10,2)
);
-- Sample data
insert into #Order (NegotiatedPrice, SuggestedPrice) values
(0, 1),
(2, 1),
(3, null),
(null, 4);
-- Query
SELECT
o.OrderId, o.NegotiatedPrice, o.SuggestedPrice,
IIF(o.NegotiatedPrice >= o.SuggestedPrice, o.NegotiatedPrice, ISNULL(o.SuggestedPrice, o.NegotiatedPrice)) AS MaxPrice
FROM #Order o
Result:
OrderId NegotiatedPrice SuggestedPrice MaxPrice
1 0,00 1,00 1,00
2 2,00 1,00 2,00
3 3,00 NULL 3,00
4 NULL 4,00 4,00
But if one needs the maximum of multiple columns?
Then I suggest a CROSS APPLY on an aggregation of the VALUES.
Example:
SELECT t.*
, ca.[Maximum]
, ca.[Minimum], ca.[Total], ca.[Average]
FROM SomeTable t
CROSS APPLY (
SELECT
MAX(v.col) AS [Maximum],
MIN(v.col) AS [Minimum],
SUM(v.col) AS [Total],
AVG(v.col) AS [Average]
FROM (VALUES (t.Col1), (t.Col2), (t.Col3), (t.Col4)) v(col)
) ca
This has the extra benefit that this can calculate other things at the same time.
Try this. It can handle more than 2 values
SELECT Max(v) FROM (VALUES (1), (2), (3)) AS value(v)
The other answers are good, but if you have to worry about having NULL values, you may want this variant:
SELECT o.OrderId,
CASE WHEN ISNULL(o.NegotiatedPrice, o.SuggestedPrice) > ISNULL(o.SuggestedPrice, o.NegotiatedPrice)
THEN ISNULL(o.NegotiatedPrice, o.SuggestedPrice)
ELSE ISNULL(o.SuggestedPrice, o.NegotiatedPrice)
END
FROM Order o
Sub Queries can access the columns from the Outer query so you can use this approach to use aggregates such as MAX across columns. (Probably more useful when there is a greater number of columns involved though)
;WITH [Order] AS
(
SELECT 1 AS OrderId, 100 AS NegotiatedPrice, 110 AS SuggestedPrice UNION ALL
SELECT 2 AS OrderId, 1000 AS NegotiatedPrice, 50 AS SuggestedPrice
)
SELECT
o.OrderId,
(SELECT MAX(price)FROM
(SELECT o.NegotiatedPrice AS price
UNION ALL SELECT o.SuggestedPrice) d)
AS MaxPrice
FROM [Order] o
YES, THERE IS.
T-SQL (SQL Server 2022 (16.x)) now supports GREATEST/LEAST functions:
MAX/MIN as NON-aggregate function
This is now live for Azure SQL Database and SQL Managed Instance. It will roll into the next version of SQL Server.
Logical Functions - GREATEST (Transact-SQL)
This function returns the maximum value from a list of one or more expressions.
GREATEST ( expression1 [ ,...expressionN ] )
So in this case:
SELECT o.OrderId, GREATEST(o.NegotiatedPrice, o.SuggestedPrice)
FROM [Order] o;
db<>fiddle demo
SELECT o.OrderId,
--MAX(o.NegotiatedPrice, o.SuggestedPrice)
(SELECT MAX(v) FROM (VALUES (o.NegotiatedPrice), (o.SuggestedPrice)) AS value(v)) as ChoosenPrice
FROM Order o
I would go with the solution provided by kcrumley
Just modify it slightly to handle NULLs
create function dbo.HigherArgumentOrNull(#val1 int, #val2 int)
returns int
as
begin
if #val1 >= #val2
return #val1
if #val1 < #val2
return #val2
return NULL
end
EDIT
Modified after comment from Mark. As he correctly pointed out in 3 valued logic x > NULL or x < NULL should always return NULL. In other words unknown result.
SQL Server 2012 introduced IIF:
SELECT
o.OrderId,
IIF( ISNULL( o.NegotiatedPrice, 0 ) > ISNULL( o.SuggestedPrice, 0 ),
o.NegotiatedPrice,
o.SuggestedPrice
)
FROM
Order o
Handling NULLs is recommended when using IIF, because a NULL on either side of your boolean_expression will cause IIF to return the false_value (as opposed to NULL).
I probably wouldn't do it this way, as it's less efficient than the already mentioned CASE constructs - unless, perhaps, you had covering indexes for both queries. Either way, it's a useful technique for similar problems:
SELECT OrderId, MAX(Price) as Price FROM (
SELECT o.OrderId, o.NegotiatedPrice as Price FROM Order o
UNION ALL
SELECT o.OrderId, o.SuggestedPrice as Price FROM Order o
) as A
GROUP BY OrderId
Oops, I just posted a dupe of this question...
The answer is, there is no built in function like Oracle's Greatest, but you can achieve a similar result for 2 columns with a UDF, note, the use of sql_variant is quite important here.
create table #t (a int, b int)
insert #t
select 1,2 union all
select 3,4 union all
select 5,2
-- option 1 - A case statement
select case when a > b then a else b end
from #t
-- option 2 - A union statement
select a from #t where a >= b
union all
select b from #t where b > a
-- option 3 - A udf
create function dbo.GREATEST
(
#a as sql_variant,
#b as sql_variant
)
returns sql_variant
begin
declare #max sql_variant
if #a is null or #b is null return null
if #b > #a return #b
return #a
end
select dbo.GREATEST(a,b)
from #t
kristof
Posted this answer:
create table #t (id int IDENTITY(1,1), a int, b int)
insert #t
select 1,2 union all
select 3,4 union all
select 5,2
select id, max(val)
from #t
unpivot (val for col in (a, b)) as unpvt
group by id
Its as simple as this:
CREATE FUNCTION InlineMax
(
#p1 sql_variant,
#p2 sql_variant
) RETURNS sql_variant
AS
BEGIN
RETURN CASE
WHEN #p1 IS NULL AND #p2 IS NOT NULL THEN #p2
WHEN #p2 IS NULL AND #p1 IS NOT NULL THEN #p1
WHEN #p1 > #p2 THEN #p1
ELSE #p2 END
END;
You can do something like this:
select case when o.NegotiatedPrice > o.SuggestedPrice
then o.NegotiatedPrice
else o.SuggestedPrice
end
SELECT o.OrderID
CASE WHEN o.NegotiatedPrice > o.SuggestedPrice THEN
o.NegotiatedPrice
ELSE
o.SuggestedPrice
END AS Price
For the answer above regarding large numbers, you could do the multiplication before the addition/subtraction. It's a bit bulkier but requires no cast. (I can't speak for speed but I assume it's still pretty quick)
SELECT 0.5 * ((#val1 + #val2) +
ABS(#val1 - #val2))
Changes to
SELECT #val1*0.5+#val2*0.5 +
ABS(#val1*0.5 - #val2*0.5)
at least an alternative if you want to avoid casting.
Here's a case example that should handle nulls and will work with older versions of MSSQL. This is based on the inline function in one one of the popular examples:
case
when a >= b then a
else isnull(b,a)
end
-- Simple way without "functions" or "IF" or "CASE"
-- Query to select maximum value
SELECT o.OrderId
,(SELECT MAX(v)
FROM (VALUES (o.NegotiatedPrice), (o.SuggestedPrice)) AS value(v)) AS MaxValue
FROM Order o;
CREATE FUNCTION [dbo].[fnMax] (#p1 INT, #p2 INT)
RETURNS INT
AS BEGIN
DECLARE #Result INT
SET #p2 = COALESCE(#p2, #p1)
SELECT
#Result = (
SELECT
CASE WHEN #p1 > #p2 THEN #p1
ELSE #p2
END
)
RETURN #Result
END
Here is #Scott Langham's answer with simple NULL handling:
SELECT
o.OrderId,
CASE WHEN (o.NegotiatedPrice > o.SuggestedPrice OR o.SuggestedPrice IS NULL)
THEN o.NegotiatedPrice
ELSE o.SuggestedPrice
END As MaxPrice
FROM Order o
Here is an IIF version with NULL handling (based on of Xin's answer):
IIF(a IS NULL OR b IS NULL, ISNULL(a,b), IIF(a > b, a, b))
The logic is as follows, if either of the values is NULL, return the one that isn't NULL (if both are NULL, a NULL is returned). Otherwise return the greater one.
Same can be done for MIN.
IIF(a IS NULL OR b IS NULL, ISNULL(a,b), IIF(a < b, a, b))
select OrderId, (
select max([Price]) from (
select NegotiatedPrice [Price]
union all
select SuggestedPrice
) p
) from [Order]
In its simplest form...
CREATE FUNCTION fnGreatestInt (#Int1 int, #Int2 int )
RETURNS int
AS
BEGIN
IF #Int1 >= ISNULL(#Int2,#Int1)
RETURN #Int1
ELSE
RETURN #Int2
RETURN NULL --Never Hit
END
For SQL Server 2012:
SELECT
o.OrderId,
IIF( o.NegotiatedPrice >= o.SuggestedPrice,
o.NegotiatedPrice,
ISNULL(o.SuggestedPrice, o.NegiatedPrice)
)
FROM
Order o
Expanding on Xin's answer and assuming the comparison value type is INT, this approach works too:
SELECT IIF(ISNULL(#A, -2147483648) > ISNULL(#B, -2147483648), #A, #B)
This is a full test with example values:
DECLARE #A AS INT
DECLARE #B AS INT
SELECT #A = 2, #B = 1
SELECT IIF(ISNULL(#A, -2147483648) > ISNULL(#B, -2147483648), #A, #B)
-- 2
SELECT #A = 2, #B = 3
SELECT IIF(ISNULL(#A, -2147483648) > ISNULL(#B, -2147483648), #A, #B)
-- 3
SELECT #A = 2, #B = NULL
SELECT IIF(ISNULL(#A, -2147483648) > ISNULL(#B, -2147483648), #A, #B)
-- 2
SELECT #A = NULL, #B = 1
SELECT IIF(ISNULL(#A, -2147483648) > ISNULL(#B, -2147483648), #A, #B)
-- 1
In MemSQL do the following:
-- DROP FUNCTION IF EXISTS InlineMax;
DELIMITER //
CREATE FUNCTION InlineMax(val1 INT, val2 INT) RETURNS INT AS
DECLARE
val3 INT = 0;
BEGIN
IF val1 > val2 THEN
RETURN val1;
ELSE
RETURN val2;
END IF;
END //
DELIMITER ;
SELECT InlineMax(1,2) as test;