i have an sql2008 server in which i need to calculate a discount.
the fields at play are SuggestedRetailPrice, BasePrice, and AdPrice
The logic is like this:
if AdPrice is null
return baseprice/suggestedretailprice
else
if AdEndDate < GETDATE() // Ad is still valid
return adprice/SuggestedRetailPrice
endif
endif
if the above is confusing, here's what I need to do:
Determine whether to use AdPrice or BasePrice depending on if the Ad has expired by comparing AdEndDate to GETDATE(). Then use that field and divide by SuggestedRetailPrice and compare with a value. i.e. (AdPrice/SuggestedRetailPrice) >= 0.6
is something like this even possible to do with one query? i know there are case statements but i am not sure if they are applicable to this situation.
You can also embed a CASE statement within ISNULL() to simplify it still further:
SELECT
ISNULL
(
CASE
WHEN AdEndDate < GETDATE()
THEN AdPrice
END,
BasePrice
) / SuggestedRetailPrice
Yes indeed - CASE allows you to project a derived value, even through multiple branches or switch statements. The general format is as follows:
SELECT ...
CASE WHEN AdPrice is null THEN baseprice/suggestedretailprice
WHEN AdEndDate < GETDATE() -- Ad is still valid
THEN adprice/SuggestedRetailPrice
ELSE 42 -- Project a value for the column if the other branches aren't matched
END AS MyValue
Related
I wasn't really sure of the best wording for the question but here is my dilemma:
I am passing a value to a sql query as #district. This value may be the exact district but it also has the possibility of being a value that should create a set of multiple districts. So if I pass 002 I want the WHERE clause to say I.Offense_Tract = #district. If I pass Other I want the WHERE clause to say I.Offense_Tract in (). What I am trying to do is something like:
AND
CASE
WHEN #district = "Other" THEN I.Offense_Tract in ('BAR','COL','GER','MEM','MIL','JAIL','JAILEAST','SCCC','1USD','2USD')
ELSE I.Offense_Tract = #district
END
But this doesn't work. The problem, restated, is if the value passed is anything other than Other, I just want it to be =. If Other is passed, I want it to be IN.
You don't need the CASE expression.
You can apply this logic with operators AND and OR:
AND (
(#district = 'Other' AND I.Offense_Tract IN ('BAR','COL','GER','MEM','MIL','JAIL','JAILEAST','SCCC','1USD','2USD'))
OR
(#district <> 'Other' AND I.Offense_Tract = #district)
)
Note that, in databases like MySql, Postgresql and SQLite, your code would work as it is.
I have a query that I am pulling in a department field, however, after a certain date I want this field to be populated as null.
For example, here is the code
Select T6.Segment2 as 'Old Department Code'
I do want this field to pull in the appropriate values, however after a certain date ( 04/01/2019 ) I want this field to show a NULL value.
Is this possible?
Not sure which DBMS you are using but it is basically the same for all of them when it comes to this... You want to use a CASE statement.
What this essentially does is it acts as an IF ELSE in your SELECT.
So in your case (ha, pun) (T-SQL Syntax):
SELECT
CASE
WHEN (YourDateFieldHere) < '04/01/2019' THEN (YourOutputFieldHere)
ELSE NULL
END (AS Alias)
FROM ...
CASE statements can check for multiple criteria, it doesn't have to just be one or the other, in this case just include more lines of WHEN (something) THEN (display this)
You can use case..when
( considering YYYYMMDD is the default format used in SAP at the internal level )
Select case when myDate >'20190104' then
null
else
T6.Segment2
end
as 'Old Department Code'
From yourTable
insert into time_test(Difference) values
(select ((select actual from time_test where id = :p13_id) -
(select
(monday+tuesday+wednesday+thursday+friday) from time_test where id=
:p13_id
)) from time_test where id= :p13_id)
Difference is a column in time_test which is null, and :p13_id is a page item for Oracle Apex.
I know I need to wrap it in nvl or some function like that but I don't know how.
It looks like you're actually trying to do an update, not an insert:
update time_test
set difference = actual - (monday+tuesday+wednesday+thursday+friday)
where id = :p13_id
If any of the 'day' columns might be null then you can use nvl() or coalesce() to default them to zero so they don't break the calculation:
update time_test
set difference = actual - coalesce(monday, 0) - coalesce(tuesday, 0)
- coalesce(wednesday, 0) - coalesce(thursday, 0) - coalesce(friday, 0)
where id = :p13_id
You could also do coalesce(actual, 0) but it might make more sense to leave the difference null if that is not set. It depends what you want to see in that case.
In this case the nvl() and coalesce() functions are equivalent. If the first argument - e.g. monday - is null then the second argument is substituted. So nvl(monday, 0) will give you the actual value of monday if it is not null, but will give you zero if it is null. You will get the same effect from coalesce(), but that allows a list of multiple expressions to be evaluated and will return the first non-null value from the list.
Another approach to this is to make difference a virtual column that is calculated on the fly, or calculate it in a view over the table; either would remove the duplicate data storage and the need to maintain the value yourself. And if you did definitely want a physical column you could set it from a trigger so automate the maintenance in case any of the other columns are updated outside your Apex application. But a virtual column is probably simpler and neater.
I've got a query that uses several subqueries. It's about 100 lines, so I'll leave it out. The issue is that I have several rows returned as part of one subquery that need to be joined to an integer value from the main query. Like so:
Select
... columns ...
from
... tables ...
(
select
... column ...
from
... tables ...
INNER JOIN core.Type mt
on m.TypeID = mt.TypeID
where dpt.[DataPointTypeName] = 'TheDataPointType'
and m.TypeID in (100008, 100009, 100738, 100739)
and datediff(d, m.MeasureEntered, GETDATE()) < 365 -- only care about measures from past year
and dp.DataPointValue <> ''
) as subMdp
) as subMeas
on (subMeas.DataPointValue NOT LIKE '%[^0-9]%'
and subMeas.DataPointValue = cast(vcert.IDNumber as varchar(50))) -- THIS LINE
... more tables etc ...
The issue is that if I take out the cast(vcert.IDNumber as varchar(50))) it will attempt to compare a value like 'daffodil' to a number like 3245. Even though the datapoint that contains 'daffodil' is an orphan record that should be filtered out by the INNER JOIN 4 lines above it. It works fine if I try to compare a string to a string but blows up if I try to compare a string to an int -- even though I have a clause in there to only look at things that can be converted to integers: NOT LIKE '%[^0-9]%'. If I specifically filter out the record containing 'daffodil' then it's fine. If I move the NOT LIKE line into the subquery it will still fail. It's like the NOT LIKE is evaluated last no matter what I do.
So the real question is why SQL would be evaluating a JOIN clause before evaluating a WHERE clause contained in a subquery. Also how I can force it to only evaluate the JOIN clause if the value being evaluated is convertible to an INT. Also why it would be evaluating a record that will definitely not be present after an INNER JOIN is applied.
I understand that there's a strong element of query optimizer voodoo going on here. On the other hand I'm telling it to do an INNER JOIN and the optimizer is specifically ignoring it. I'd like to know why.
The problem you are having is discussed in this item of feedback on the connect site.
Whilst logically you might expect the filter to exclude any DataPointValue values that contain any non numeric characters SQL Server appears to be ordering the CAST operation in the execution plan before this filter happens. Hence the error.
Until Denali comes along with its TRY_CONVERT function the way around this is to wrap the usage of the column in a case expression that repeats the same logic as the filter.
So the real question is why SQL would be evaluating a JOIN clause
before evaluating a WHERE clause contained in a subquery.
Because SQL engines are required to behave as if that's what they do. They're required to act like they build a working table from all of the table constructors in the FROM clause; expressions in the WHERE clause are applied to that working table.
Joe Celko wrote about this many times on Usenet. Here's an old version with more details.
First of all,
NOT LIKE '%[^0-9]%'
isn`t work well. Example:
DECLARE #Int nvarchar(20)= ' 454 54'
SELECT CASE WHEN #INT LIKE '%[^0-9]%' THEN 1 ELSE 0 END AS Is_Number
Result: 1
But it is not a number!
To check if it is real int value , you should use ISNUMERIC function. Let`s check this:
DECLARE #Int nvarchar(20)= ' 454 54'
SELECT ISNUMERIC(#int) Is_Int
Result:0
Result is correct.
So, instead of
NOT LIKE '%[^0-9]%'
try to change this to
ISNUMERIC(subMeas.DataPointValue)=0
UPDATE
How check if value is integer?
First here:
WHERE ISNUMERIC(str) AND str NOT LIKE '%.%' AND str NOT LIKE '%e%' AND str NOT LIKE '%-%'
Second:
CREATE Function dbo.IsInteger(#Value VarChar(18))
Returns Bit
As
Begin
Return IsNull(
(Select Case When CharIndex('.', #Value) > 0
Then Case When Convert(int, ParseName(#Value, 1)) <> 0
Then 0
Else 1
End
Else 1
End
Where IsNumeric(#Value + 'e0') = 1), 0)
End
Filter out the non-numeric records in a subquery or CTE
I'm attempting to create a T-SQL case statement to filter a query based on whether a field is NULL or if it contains a value. It would be simple if you could assign NULL or NOT NULL as the result of a case but that doesn't appear possible.
Here's the psuedocode:
WHERE DateColumn = CASE #BitInput
WHEN 0 THEN (all null dates)
WHEN 1 THEN (any non-null date)
WHEN NULL THEN (return all rows)
From my understanding, the WHEN 0 condition can be achieved by not providing a WHEN condition at all (to return a NULL value).
The WHEN 1 condition seems like it could use a wildcard character but I'm getting an error regarding type conversion. Assigning the column to itself fixes this.
I have no idea what to do for the WHEN NULL condition. My internal logic seems to think assigning the column to itself should solve this but it does not as stated above.
I have recreated this using dynamic SQL but for various reasons I'd prefer to have it created in the native code.
I'd appreciate any input. Thanks.
The CASE expression (as OMG Ponies said) is mixing and matching datatypes (as you spotted), in addition you can not compare to NULL using = or WHEN.
WHERE
(#BitInput = 0 AND DateColumn IS NULL)
OR
(#BitInput = 1 AND DateColumn IS NOT NULL)
OR
#BitInput IS NULL
You could probably write it using CASE but what you want is an OR really.
You can also use IF..ELSE or UNION ALL to separate the 3 cases