Lets assume, I have one column 'Rate' and rows have to be fetched on basis of below criteria.
Below CHARS are nothing but columns.
if X>Y then fetch Rate
if A>B then
if C>D then fetch Rate
if E>F then
if G>H then fetch Rate
if I>J then fetch Rate
if K>L then
if M>N THEN fetch Rate
I am stuck at, how to write multilevel conditions in WHERE clause.
I'm not at all sure I've gotten the right end of the stick, but it sounds like you just want to combine multiple conditions in the WHERE clause:
WHERE
X>Y OR
(
A>B AND
(
C>D OR
(
E>F AND
(
G>H OR
I>J OR
(K>L AND M>N)
)
)
)
)
Not all of the brackets are strictly necessary, but I usually prefer to use them rather than remember whether AND or OR has higher precedence.
To parse contditions into SQL one may use a simple set of two rules:
Change "then fetch Rate" to "OR (...)"
Change "then" to "AND (...)"
so the resulting SQL is something like that:
select Rate
from ...
where (X > Y) OR
(A > B) AND
((C > D) OR
(E > F) AND
((G > H) OR
(I > J) OR
(K > L) AND
(M > N)))
Related
I made a BigQuery query which involves generating an array of random numbers for each row. I use the random numbers to decide which elements to include from an array that exists in my source table.
I had a lot of trouble getting the arrays of random numbers to not repeat themselves over every single row. I found a workaround, but is this expected behavior? I'll post two "methods" (one with desired results, one with bad results) below. Note that both methods work fine if you don't use an array, but just generate a single random number.
Method 1 (BAD Results):
SELECT
(
SELECT
ARRAY(
SELECT AS STRUCT
RAND() AS random
FROM UNNEST(GENERATE_ARRAY(0, 10, 1)) AS _time
) AS random_for_times
)
FROM UNNEST(GENERATE_ARRAY(0, 10, 1))
Method 2 (GOOD results):
SELECT
(
SELECT
ARRAY(
SELECT AS STRUCT
RAND() AS random
FROM UNNEST(GENERATE_ARRAY(0, 10, 1)) AS _time
) AS random_for_times
FROM (SELECT NULL FROM UNNEST([0]))
)
FROM UNNEST(GENERATE_ARRAY(0, 10, 1))
Example Results - Method 1 (BAD):
Row 1
0.5431173080158003
0.5585452983410205
...
Row 2
0.5431173080158003
0.5585452983410205
...
Example Results - Method 2 (GOOD):
Row 1
0.49639706531271377
0.1604380522058521
...
Row 2
0.7971869432989377
0.9815667330115473
...
EDIT: See below for some alternative examples that are similar, after Yun Zhang's theory about subqueries. Your solution was useful for the problem I posted, but note that there are still some cases I am finding baffling. Also, although I agree that you are probably correct about the subqueries being tied to the problem: shouldn't a subquery (especially one without a FROM clause) be less likely to have its results re-used than selecting a "normal" value? People talk about performance issues with subqueries sometimes, because they are supposedly calculated one time for each row, even if the results may be the same.
Do you agree that this seems like it may be a bug?
The below examples show that it is not necessarily even creating an array of randoms that is the problem -- even performing a sub-select that just happens to have an unrelated array in it can cause problems with RAND(). The problem goes away by eliminating the sub-select, by choosing just the random value from the sub-select, or by including a value inside the array which varies by row. Weird !!!
BAD
SELECT
(SELECT AS STRUCT RAND() AS r, ARRAY(SELECT 1) AS a)
FROM UNNEST(GENERATE_ARRAY(0, 5, 1)) AS u
FIX #1 - No subselect
SELECT
STRUCT(RAND() AS r, ARRAY(SELECT 1) AS a)
FROM UNNEST(GENERATE_ARRAY(0, 5, 1)) AS u
FIX #2 - Select only r
SELECT
(SELECT AS STRUCT RAND() AS r, ARRAY(SELECT 1) AS a).r
FROM UNNEST(GENERATE_ARRAY(0, 5, 1)) AS u
Fix #3 - Array contains "u"
SELECT
(SELECT AS STRUCT RAND() AS r, ARRAY(SELECT u) AS a).r
FROM UNNEST(GENERATE_ARRAY(0, 5, 1)) AS u
Haven't understood why first query didn't work but I have a simpler version that works for you:
SELECT (
SELECT array_agg(RAND()) AS random
FROM UNNEST(GENERATE_ARRAY(0, 10, 1)) AS _time
) AS random_for_times
FROM UNNEST(GENERATE_ARRAY(0, 10, 1))
Update: I later realized that the problem is with ARRAY(subquery), as long as you can avoid using it for your case (like in my query above), you should be fine.
I'm fairly new to SQL and can't figure out how to combine several if .. then statements.
What is the right syntax for this?
I'm using SQL Server Management Studio 2017.
I've tried to combine if... else if..statements and I tried using case statements, but I always get lost in the nesting of the statements.
I have several condtions whom have to be met before I can execute some sort of calculation.
It should be something like this:
If CalculationMethod = x
and if (Price * coefficient) < Amount
then CalculatedAmount = Amount
else CalculatedAmount = (Price * coefficient)
Where Amount has it's own if statements:
Amount =
If Category = a and DistanceFrom <= Distance >= DistanceUntill then take amount from that particular cell
If Category = b and DistanceFrom <= Distance >= DistanceUntill then take amount from that particular cell
If Category = c and DistanceFrom <= Distance >= DistanceUntill then take amount from that particular cell
In this case, Amount is a cell in a table with columns DistanceFrom, DistanceUntill, a, b and c.
CalculationMethod and Coefficient are columns in another table.
Price is a column in third table.
In the end I want the CalculatedAmount based on the Amount, Price and Coefficient.
Does this make any sense? Does anyone has an idea on how to tackle this?
If you have an IF...THEN...ELSE type of scenario I think the right direction would be to use a CASE statement such as:
SELECT CASE WHEN CalculationMethod = x AND ((Price * coefficient) < Amount) THEN Amount
ELSE (Price * coefficient) END CalculatedAmount
You can read about it here: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/case-transact-sql?view=sql-server-2017
An IIF clause works very well when there is only one decision branch, but if you have multiple things to choose from the CASE statement is the way to go.
SELECT IIF(CalculationMethod = x and Price * coefficient < Amount, Amount, Price * coefficient) as CalculatedAmount
FROM aTable
I have certain values that are needed for validation in Forms, either they look like Value X >= 0 but it could also be X <= 0, it depends on what operator should be used. How can I store such a value?
(I use MS SQL Server + Access as Frontend)
I basicly wanna store the Value and if it needs to be bigger than or smaller than.
Store the value, as usual, in a field, and the operator in another field as Short Text.
The you can use Eval:
Result = Eval("" & [ValueField] & [OperatorField] & "0")
You can store your value as it is, and to check if this value is positive or not you have two ways
First one
Create a computed column to check the Value column as
CREATE TABLE YourTable(
YourValue INT,
IsPositive AS CASE WHEN YourValue < 0 THEN 0 ELSE 1 END
);
INSERT INTO YourTable (YourValue) VALUES
(1), (-1);
SELECT *
FROM YourTable;
Second one
Use a CASE expression (or even you can create a view) as
SELECT CASE WHEN YourValue < 0 THEN 0 ELSE 1 END IsPositive,
--...
FROM YourTable;
Let's say I have a table lines
b | a
-----------
17 7000
17 0
18 6000
18 0
19 5000
19 2500
I want to get positive values of a function: (a1 - a2) \ (b2 - b1) for all elements in cartesian product of lines with different b's. (If you are interested this will result in intersections of lines y1 = b1*x + a1 and y2 = b2*x + a2)
I wrote query1 for that cause
SELECT temp.point FROM
(SELECT DISTINCT ((l1.a - l2.a) / (l2.b - l1.b)) AS point
FROM lines AS l1
CROSS JOIN lines AS l2
WHERE l1.b != l2.b
) AS temp
WHERE temp.point > 0
It throws a "division by zero" error. I tried the same query without the WHERE clause (query2) and it works just fine
SELECT temp.point FROM
(SELECT DISTINCT ((l1.a - l2.a) / (l2.b - l1.b)) AS point
FROM lines AS l1
CROSS JOIN lines AS l2
WHERE l1.b != l2.b
) AS temp
as well as the variation with the defined SQL function (query3)
CREATE FUNCTION get_point(#a1 DECIMAL(18, 4), #a2 DECIMAL(18, 4), #b1 INT, #b2 INT)
RETURNS DECIMAL(18, 4)
WITH EXECUTE AS CALLER
AS
BEGIN
RETURN (SELECT (#a1 - #a2) / (#b2 - #b1))
END
GO
SELECT temp.point FROM
(SELECT DISTINCT dbo.get_point(l1.a, l2.a, l1.b, l2.b) AS point
FROM lines AS l1
CROSS JOIN lines AS l2
WHERE l1.b != l2.b
) AS temp
WHERE temp.point > 0
I have an intuitive assumption that the outer SELECT shouldn't affect the way nested SELECT is executed (or at least shouldn't break it). Even if it is not true that wouldn't explain why query3 works when query1 doesn't.
Could someone explain the principle behind this? That would be much appreciated.
If you want to guarantee that the query will always work, you'd need to wrap your calculation in something like a case statement
case when l2.b - l1.b = 0
then null
else (l1.a - l2.a) / (l2.b - l1.b)
end
Technically, the optimizer is perfectly free to evaluate conditions in whatever order it expects will be more efficient. The optimizer is free to evaluate the division before the where clause that filters out rows where the divisor would be 0. It is also free to evaluate the where clause first. Your different queries have different query plans which result in different behavior.
Realistically, though, even though a particular query might have a "good" query plan today, there is no guarantee that the optimizer won't decide in a day, a month, or a year to change the query plan to something that would throw a division by 0 error. I suppose you could decide to use a bunch of hints/ plan guides to force a particular plan with a particular behavior to be used. But that tends to be the sort of thing that bites you in the hind quarters later. Wrapping the calculation in a case (or otherwise preventing the division by 0 error) will be much safer and easier to explain to the next developer.
I have a text column and the data in the text columns are as below:
Rob goes to school,get punished
Rob goes to school
Rob does not goes to school,get punished
When trying to write a query using case statement like
CASE
WHEN (PATINDEX('%Rob goes to school%',value) > 0) OR
(PATINDEX('%Rob is ill%',value) > 0 ) AND
(PATINDEX(%get punished%',value) > 0) THEN
'DONE'
It should select only the 1st statement but instead it is picking both the 1st and 2nd statement with 'DONE'. Any suggestion how to do a pattern match in this case?
I am using SQL Sever 2005/2008
Operator precedence and not enough parenthesis probably
You have x OR y AND z which is actually x OR (y AND z). Do you want want (x OR y) AND z?
The 2nd statement give true OR (false AND false) which gives true
You want (true OR false) AND false to give false
So the SQL should be
CASE WHEN
(
PATINDEX('%Rob goes to school%', value) > 0
OR
PATINDEX('%Rob is ill%', value) > 0
)
AND
(PATINDEX(%get punished%', value) > 0) THEN 'DONE'
...
PATINDEX does not treat your strings as delimited lists (comma-separated values) -- it searches for a match against the entire string.
Rob goes to school,get punished
Rob goes to school
PATINDEX('%Rob goes to school%',value) > 0 evaluates to true for both of them because the wildcard % matches any string of 0 or more characters. Your second and third patterns never get evaluated.
If you want to test which pattern is returning true, try this:
CASE
WHEN (PATINDEX('%Rob goes to school%',value) > 0) THEN 'Pattern 1'
WHEN (PATINDEX('%Rob is ill%',value) > 0 ) THEN 'Pattern 2'
WHEN (PATINDEX('%get punished%',value) > 0) THEN 'Pattern 3'
ELSE 'No Match Found' END
If you want a pattern to match the first value, but not the second, then look for (PATINDEX('%Rob goes to school,%',value) > 0) with the comma instead.
Otherwise -- if you're wanting to treat the strings like comma-separated values, PATINDEX is not your best tool for that. Other options might include converting your strings to tables via table-value function, or what have you.