I have a very large case in my select statement, that ends as either 1 or 0, and has an alias name "x". I want to check if "x" = 1 in my WHERE statement, but I know that aliases cannot be used in the where statement. Is my only way of checking for this condition to include the original case statement in the WHERE clause?
You could use CROSS/OUTER APPLY:
SELECT *
FROM tab t
CROSS APPLY (SELECT CASE WHEN t.col ... THEN
-- very complex conditions
END
) sub(c)
WHERE sub.c = ?;
This approach allows you to avoid nested subqueries.
You can put your statement in a cte:
; with CTE as (Select .... as X from ...)
Select *
from CTE
where X = 1
How about even simpler? Your case expression is returning a bit. Seems to me that if you need a where clause there is no need to run the case expression more than once.
select MyReturn = 1
from SomeTable
where case with a whole bunch of logic end = 1
Or if you need it to be parameterized something like this.
select MyReturn = #MyBit
from SomeTable
where case with a whole bunch of logic end = #MyBit
Doesn't a subquery work just fine?
SELECT ST.*
FROM (SELECT TBL.*,
CASE WHEN ComplexCondition THEN 'Something'
ELSE 'SomethingElse'
END AS aliasedColumn
FROM SomeTable
) ST
WHERE ST.aliasedColumn = 'Something';
Related
I am having some trouble with the syntax when I am trying to use the below query in SQL Server. I wanted to show WHERE clause based on condition.
This is my code:
DECLARE #isActual = 0
SELECT
SolverRunId, PointId, TimeStampUtc, Value,
UnitOfMeasure, Reliability
FROM
(SELECT
bt.SolverRunId, bt.PointId, bt.TimeStampUtc,
bt.Value, bt.UnitOfMeasure, bt.Reliability
FROM
cte bt
WHERE
bt.TimeStampUtc = bt.TargetTimeUtc
UNION ALL
SELECT
a.SolverRunId, a.PointId, a.TimeStampUtc, a.Value,
a.UnitOfMeasure, a.Reliability
FROM
cte a
WHERE
-- I tried using this case but it is syntactically incorrect
CASE
WHEN #isActual = 0 THEN a.TimeStamUtc > #endDateUtc
ELSE a.TimestampUtc <= #endDateUtc
END
-- instead of this. I wanted to have conditional where based on #isActual value from 0 to 1
a.TimeStampUtc > #endDateUtc
AND a.SolverRunId = #maxRun) x
ORDER BY
SolverRunId, PointId, TimeStampUtc;
I wanted to have the where condition to be evaluated based on #isActual set to true or false
As mentioned in the comments, don't use a CASE in the WHERE just use proper boolean logic with AND and OR clauses. In your question your variable #isActual is also missing a data type, so I have assumed it is a bit:
DECLARE #isActual bit = 0;
SELECT SolverRunId,
PointId,
TimeStampUtc,
Value,
UnitOfMeasure,
Reliability
FROM (SELECT bt.SolverRunId,
bt.PointId,
bt.TimeStampUtc,
bt.Value,
bt.UnitOfMeasure,
bt.Reliability
FROM cte bt
WHERE bt.TimeStampUtc = bt.TargetTimeUtc
UNION ALL
SELECT a.SolverRunId,
a.PointId,
a.TimeStampUtc,
a.Value,
a.UnitOfMeasure,
a.Reliability
FROM cte a
WHERE a.TimeStampUtc > #endDateUtc
AND a.SolverRunId = #maxRun
AND ((#isActual = 0 AND a.TimeStamUtc > #endDateUtc)
OR (#isActual = 1 AND a.TimestampUtc <= #endDateUtc))) x
ORDER BY SolverRunId,
PointId,
TimeStampUtc;
You may also want experiment with adding RECOMPILE to the OPTION clause of the above, as the query plan requirements for when #isActual has a value of 1 or 0 could be quite different.
I have the following query. I simplified it for demo purpose. I am using SQL Server - t-sql
Select tm.LocID = (select LocID from tblLoc tl
where tl.LocID = tm.LodID )
from tblMain tm
if the subquery returns multiple records, I like to assign tm.LocID to null else if there is only 1 record returned then assign it to tm.LocID. I am looking for a simple way to do this. Any help would be appreciated.
One way I can see is to have a CASE statement and check if (Count * > 1 ) then assign null else return the value but that would require a select statement within a select statement.
You have the right idea about using a case expression for count(*), but it will not require another subquery:
SELECT tm.LocID = (SELECT CASE COUNT(*) WHEN 1 THEN MAX(LocID) END
FROM tblLoc tl
WHERE tl.LocID = tm.LodID )
FROM tblMain tm
or just use a HAVING clause, like
Select tm.LocID = (select LocID from tblLoc tl
where tl.LocID = tm.LodID
group by locID
having count(*) = 1)
)
from tblMain tm
Your query above (and many of the other answers here) is a correlated subquery which will be very slow since it performs a separate aggregation query on each record. This following will address both your problem and potentially perform a bit better since the count happens in a single pass.
SELECT
CASE
WHEN x.locid IS NOT NULL THEN x.locid
ELSE NULL
END
FROM tblMain m
LEFT JOIN (
SELECT
locid
FROM tblLoc
GROUP BY locid
HAVING COUNT(1) = 1
) x
ON x.locid = m.locid
;
The above is in Postgres syntax (what I'm familiar with) so you would have to make it TSQL compatible.
I have a sql server query statement like this:
WITH A
AS (
SELECT (
SUM(CASE
WHEN (t1.price) > 0
THEN (t1.price)
ELSE 0
END)
) AS pr1
,(
ABS(SUM(CASE
WHEN (t1.price) < 0
THEN (t1.price)
ELSE 0
END))
) AS pr2
FROM dbo.price_table AS t1
)
,B
AS (
SELECT (WHEN(pr1 - pr2) < 0 THEN ABS(pr1 - pr2) ELSE 0 END) AS res
FROM A
)
SELECT res
FROM B
in my query, i use 2 select statement to achieve "res" column, but i want achieve to "res" column in 1 select statement.
what is best way for merge 2 select statement to 1 select statement query?
Your calculation seems way to complicated. You are taking the sum of the positive values. Then the sum of the negative values, using ABS() to make that value positive, and subtracting this result. Guess what? That is the same as taking the SUM() of all the values in the first place.
So, I think this statement is equivalent:
SELECT (CASE WHEN SUM(t1.price) < 0
THEN ABS(SUM(t1.price))
ELSE 0
END)
FROM dbo.price_table t1;
Common Table Expressions (CTEs) are a way of writing reusable sub-queries. There's not one method that works with all CTEs (CTEs can be self-referencing which is not possible with subqueries_. In your example, the query
WITH A AS
(
{A query}
)
,B AS
(
SELECT ... res FROM A
)
SELECT res FROM B
could be rearranged to
SELECT res FROM
(
SELECT {expression} res FROM
(
{A query}
)
)
which is just
SELECT {expression} res FROM
(
{A query}
)
your solution is fine.
Even thou you use multipple "Select" statements, they will compile in one query.
So your query is equivalent to the query proposed by Gordon Linoff.
If you have more complicated logic you can create SINGLE query: take your last "Select ..." and substituse "pr1" and "pr2" with subqueries (or expressions). You may receive very complex statement with repeatable parts.
When you use "WITH" you split logical parts with good readability, while performance is not decreased. I always prefere "WITH" in complicate queries.
Its possible to do something like this:
and zmt.mediatypeid in nvl(:P21_MEDIA, select mediatypeid from zbx.media_type)
:P21_MEDIA is a variable
Yes, it is called a scalar subquery expression:
select x, (select y from another_table where foo = x) from the_table
select x from the_table where foo = (select y from another_table where bar = x)
It can only return a single column and a single row, anything else is an error.
I cannot test it right now, but I would assume you can also use it inside a function such as NVL. The documentation only mentions that it cannot be used in GROUP BY.
yes, you can use CASE statement to check the condition:
and 1 = CASE
WHEN :P21_MEDIA IS NOT NULL THEN
CASE
WHEN zmt.mediatypeid = :P21_MEDIA THEN 1
ELSE 0
END
WHEN zmt.mediatypeid IN (select mediatypeid from zbx.media_type) THEN 1
ELSE 0
END
I'm trying to create the following WHERE clause:
AND CASE #SomePRarmeter
WHEN 'this' THEN
user_id IN (SELECT * FROM dbo.func_Id1(#User))
WHEN 'that' THEN
user_id IN (SELECT user_id from dbo.func_Ids2(#OrgsForReporter)
END
But I'm getting an error: Incorrect syntax near the keyword 'IN' (in the first condition) , although separately both of those conditions work. What would be the correct way to make such a statement work?
Thanks!
Try
AND (
(#SomePRarmeter = 'this' AND user_id IN (SELECT * FROM dbo.func_Id1(#User)))
OR
(#SomePRarmeter = 'that' AND user_id IN user_id IN (SELECT user_id from dbo.func_Ids2(#OrgsForReporter)))
)
You are doing select * in a subquery. You need to return only one column:
(SELECT * FROM dbo.func_Id1(#User))
to this:
(SELECT YOUR_USER_ID_COLUMN FROM dbo.func_Id1(#User))
A case statement must result in a value, not an expression. So this won't work:
select case when 1=1 then 1 in (1,2,3) end
But this will work;
select case when 1=1 then 1 end
The value can be the result of a subquery. So one solution would be to rewrite the where clause like:
CASE #SomePRarmeter
WHEN 'this' THEN
(SELECT count() FROM dbo.func_Id1(#User) f where f.user_id = t.user_id))
WHEN 'that' THEN
(SELECT count() from dbo.func_Ids2(#OrgsForReporter) f where f.user_id = t.user_id))
END > 1
Now it returns the number of matching rows. You can then filter with case ... end > 1.
I'd break this out:
IF 'this'
SELECT
...
WHERE user_id IN (SELECT * FROM dbo.func_Id1(#User))
ELSE IF 'that'
SELECT
...
WHERE user_id IN (SELECT user_id from dbo.func_Ids2(#OrgsForReporter))
CASE ... END returns an expression, not a piece of literal SQL code. Rather than:
AND CASE foo WHEN bar THEN bla=ble END -- Wrong
... you have to use this:
AND bla = CASE foo WHEN bar THEN ble END -- Right
In your case, you can do something on this line:
-- Untested
AND (
(#SomePRarmeter='this' AND user_id IN (SELECT * FROM dbo.func_Id1(#User)))
OR (#SomePRarmeter='that' AND user_id IN (SELECT user_id from bo.func_Ids2(#OrgsForReporter))
)