T-SQL Case Condition in Where Clause - sql

i trying to do this query where i have a where clause. The problem is that i need to use inside the where condition the operator IN but i canĀ“t figured out
what i missing.
someone can give a hand pls?
here is my query
DECLARE #OP INT = 1
SELECT * FROM Table
WHERE
Table.[status] IN (CASE WHEN #OP = 1 THEN (5,6) ELSE (12) END)

There's no need for a case statement.
DECLARE #OP INT = 1;
SELECT * FROM Table
WHERE (#OP = 1 AND Table.[status] IN (5,6))
OR (#OP !=1 AND Table.[status] IN (12))

Try:
DECLARE #OP INT = 1
SELECT * FROM TABLE
WHERE
((#OP = 1 AND TABLE.[status] IN (5,6)) OR (#OP <> 1 AND TABLE.[status] = 12))

Another option:
DECLARE #OP INT = 1
SELECT * FROM Table
WHERE
1 = (CASE WHEN #OP = 1 CASE WHEN Table.[status] IN (5,6) THEN 1 END
ELSE CASE WHEN Table.[status] = 12 THEN 1 END
END)
Having nested case statements may help the query plan take advantage of case statement short circuiting. You could also do it like this without the nested cases:
DECLARE #OP INT = 1
SELECT * FROM Table
WHERE
1 = (CASE WHEN #OP = 1 AND Table.[status] IN (5,6) THEN 1
WHEN #OP <> 1 AND Table.[status] = 12 THEN 1
END)

Related

Order by calculated column with alias inside case expression

I've got a problem with my order by clause when using a calculated column with an alias as below:
This order by works without any problem
declare #Mode int = 1
declare #Sort nvarchar(max) = 'engname'
select top 10 School.Id as EntityId,
School.EnglishName as EntityEnglishName,
School.Name as EntityNativeName,
case
when #Mode = 0 then 0
when #Mode = 1 then 1
when #Mode = 2 then 2
end as ActiveStudents
from V_SchoolMinimized as School
Order By ActiveStudents
The following query has an error:
Invalid column name 'ActiveStudents'
declare #Mode int = 1
declare #Sort nvarchar(max) = 'engname'
select top 10 School.Id as EntityId,
School.EnglishName as EntityEnglishName,
School.Name as EntityNativeName,
case
when #Mode = 0 then 0
when #Mode = 1 then 1
when #Mode = 2 then 2
end as ActiveStudents
from V_SchoolMinimized as School
Order By
case when #Sort is null then School.Id end,
case when #Sort = 'engname' then ActiveStudents end
How can I use ActiveStudents within the conditional order by clause as shown?
So while you can use a calculated column in your ORDER BY clause (but not in other clauses such as GROUP BY), you cannot then apply further calculations or conditions - it must be used exactly as created.
There are a whole bunch of ways to solve this problem. Which approach you use will come down to some combination of:
Which option is clearer to you as the developer
Which option performs better
Which option fits into your existing query better
Option 1: Repeat the logic
I don't recommend this option because it violates the DRY principle thereby making it harder to maintain and easier to make mistakes.
select top 10
S.Id as EntityId
, S.EnglishName as EntityEnglishName
, S.[Name] as EntityNativeName
, case
when #Mode = 0 then 0
when #Mode = 1 then 1
when #Mode = 2 then 2
end as ActiveStudents
from V_SchoolMinimized as S
order by
case when #Sort is null then S.Id end
, case when #Sort = 'engname' then
case
when #Mode = 0 then 0
when #Mode = 1 then 1
when #Mode = 2 then 2
end
end;
The rest of the options are sub-query variations the choice of which comes down to the comments provided as the start.
Option 2: Use a derived table sub-query
select top 10
S.Id as EntityId
, S.EnglishName as EntityEnglishName
, S.[Name] as EntityNativeName
, S.ActiveStudents
from (
select *
, case
when #Mode = 0 then 0
when #Mode = 1 then 1
when #Mode = 2 then 2
end as ActiveStudents
from V_SchoolMinimized
) as S
order by
case when #Sort is null then S.Id end
, case when #Sort = 'engname' then S.ActiveStudents end;
Option 3: Use a CTE (Common Table Expression)
with cte as (
select *
, case
when #Mode = 0 then 0
when #Mode = 1 then 1
when #Mode = 2 then 2
end as ActiveStudents
from V_SchoolMinimized
)
select top 10
S.Id as EntityId
, S.EnglishName as EntityEnglishName
, S.[Name] as EntityNativeName
, S.ActiveStudents
from cte
order by
case when #Sort is null then S.Id end
, case when #Sort = 'engname' then S.ActiveStudents end;
Option 4: Use CROSS APPLY
select top 10
S.Id as EntityId
, S.EnglishName as EntityEnglishName
, S.[Name] as EntityNativeName
, A.Students
from V_SchoolMinimized as S
cross apply (
values (
case
when #Mode = 0 then 0
when #Mode = 1 then 1
when #Mode = 2 then 2
end
)
) as A (Students)
order by
case when #Sort is null then S.Id end
, case when #Sort = 'engname' then A.Students end;
Note: I suggest keeping your table aliases nice and short, 1-2 characters where possible, occasionally 3.

How to use like clause over string separated with comma inside case statement

DECLARE #IsSearch_ BIT;
SET #IsSearch_ ='True'
DECLARE #Organization_ VARCHAR(100);
SET #Organization_ ='111,111A'
select *
from VYC20 C20
where (#Organization_ IS NULL)
OR (1 = CASE #IsSearch_
WHEN 0 THEN (CASE WHEN #Organization_ IS NOT NULL
AND C20.ORG IN
(select value
from STRING_SPLIT(#Organization_, ','))
THEN 1 ELSE 0 END)
WHEN 1 THEN (CASE WHEN C20.ORG like #Organization_ + '%'
THEN 1 ELSE 0 END)
ELSE 0 END)
In the above query the 1st case WHEN 0 statement with IN clause gives the correct record, but the WHEN 1 THEN gives me 0 records, can someone please help me how to apply like clause with string separated with comma.
It looks like what you actually want is to use LIKE after splitting
DECLARE #IsSearch_ BIT = 1;
DECLARE #Organization_ VARCHAR(100) = '111,111A';
SELECT *
FROM VYC20 C20
WHERE #Organization_ IS NULL
OR (#IsSearch_ = 0 AND
C20.ORG IN
(SELECT s.value
FROM STRING_SPLIT(#Organization_, ',') s))
OR (#IsSearch_ = 1 AND
EXISTS (SELECT 1
FROM STRING_SPLIT(#Organization_, ',') s
WHERE C20.ORG LIKE s.value + '%'));
I strongly advise you to do this in a set-based fashion and use a table variable or Table Parameter
DECLARE #IsSearch_ BIT = 1;
DECLARE #Organization_ TABLE (value VARCHAR(100));
INSERT #Organization_ VALUES('111'),('111A');
SELECT *
FROM VYC20 C20
WHERE #IsSearch_ = -1 -- include all
OR (#IsSearch_ = 0 AND
C20.ORG IN
(SELECT s.value
FROM #Organization_ s))
OR (#IsSearch_ = 1 AND
EXISTS (SELECT 1
FROM #Organization_ s
WHERE C20.ORG LIKE s.value + '%'));

How to update table based on the values in other columns

Here is a sample below, I want to update AvailableAmt column based on the amount entered on UI.
Requirement
Update the value from the last row to the first row,
If entered 500 on UI, then the table will be like
If entered 1000 on UI, then the table will be like
Thank you for your help in advance !
Can't test it on a Sybase somewhere.
But in theory something like this might work:
DECLARE #Group VARCHAR(8) = 'a';
DECLARE #Amount INT = 1100;
UPDATE t
SET t.AvailableAmt =
CASE
WHEN q.PrevRemain > 0 AND t.AvailableAmt <= q.PrevRemain THEN 0
WHEN q.PrevRemain > 0 THEN t.AvailableAmt - q.PrevRemain
ELSE t.AvailableAmt
END
FROM YourTable t
JOIN
(
select [Group], [Row],
#Amount-(SUM(AvailableAmt) OVER (PARTITION BY [Group] ORDER BY AvailableAmt, [Row] desc) - AvailableAmt) as PrevRemain
from YourTable
where AvailableAmt > 0
and [Group] = #Group
) AS q
ON (q.[Group] = t.[Group] and q.[Row] = t.[Row]);
For a Sybase flavor that doesn't support the window function of SUM, something like this might work.
DECLARE #Group VARCHAR(8) = 'a';
DECLARE #Amount INT = 1200;
UPDATE t
SET t.AvailableAmt =
CASE
WHEN q.PrevRemain > 0 AND t.AvailableAmt <= q.PrevRemain THEN 0
WHEN q.PrevRemain > 0 THEN t.AvailableAmt - q.PrevRemain
ELSE t.AvailableAmt
END
FROM YourTable t
JOIN
(
select t1.[Group], t1.[Row],
#Amount - (SUM(t2.AvailableAmt)-t1.AvailableAmt) as PrevRemain
from YourTable t1
left join YourTable t2 on (t2.[Group] = t1.[Group] and t2.AvailableAmt <= t1.AvailableAmt and t2.[Row] >= t1.[Row])
where t1.AvailableAmt > 0
and t1.[Group] = #Group
group by t1.[Group], t1.[Row], t1.AvailableAmt
) AS q
ON (q.[Group] = t.[Group] and q.[Row] = t.[Row]);

How do I check whether data is there or not in more than one sql table in one script?

I have more than 3 sql tables.
now i'm trying to select count(*) from all tables but how can i do this?.
I want to know whether data is present in all tables or not
I need to check the row count from previous business day ~15% for any table and it sends an email alert
I tried like following please help me to complete
PROCEDURE [dbo].[SendEmail_WSOTableDataAlert]
AS
BEGIN
declare #err int
IF NOT EXISTS (select 1 from T1) OR
NOT EXISTS (select 1 from T2)
BEGIN
set #error=1
END
//here i need to show which table is having empty data how can i do this please help
SET #tableHTML = #tableHTML + +
'</TABLE>' + #EmailFooter;
#error =1
then
send mail
END
Select
case when count(*) = 0 then
'No rows'
else
'Has rows'
end
FROM
(
Select * from #table1
UNION ALL
Select * from #table2
UNION ALL
Select * from #table3
) t
UPDATE
This makes sure all of then have at least one row and fail if any of them does not have record
Select
case when count(*) = 0 then
'No rows'
else
'Has rows'
end
FROM
(
Select top 1 1 found from #table1
intersect
Select top 1 1 found from #table2
intersect
Select top 1 1 found from #table3
) t
You can try multiplying the flags indicating zero counts together. If any of them is zero, the result will be zero.
select (case when (select count(*) from table1)=0 then 0 else 1 end
*case when (select count(*) from table2)=0 then 0 else 1 end
*case when (select count(*) from table3)=0 then 0 else 1 end) as no_zeros
If you would like to know which table has all zeros, you could transform the query as follows:
select (case when (select count(*) from table1)=0 then 1 else 0 end
+case when (select count(*) from table2)=0 then 2 else 0 end
+case when (select count(*) from table3)=0 then 4 else 0 end
+case when (select count(*) from table4)=0 then 8 else 0 end) as no_zeros
Use powers of two (1, 2, 4, 8, 16, 32, and so on) as your flags. Ones 1 in the binary representation of the result will tell you which tables have no records.
(select count() from table1 )
union all
(select count() from table2 )
union all
(select count(*) from table3 )
And then loop through the rows of the result
declare #count1 int
select #count1 = count(*)
from table1
declare #count2 int
select #count2 = count(*)
from table2
declare #count3 int
select #count3 = count(*)
from table3
if (#count1 + #count2 + #count3 = 0)
--do something
else
--do something else
You can use the EXISTS keyword to efficiently check if there is any data in a table.
IF NOT EXISTS (SELECT 1 FROM Table1) OR NOT EXISTS (SELECT 1 FROM Table2) OR NOT EXISTS (SELECT 1 FROM Table3)
BEGIN
/* do something */
END

Getting average from 3 columns in SQL Server

I have a table with 3 columns (smallint) in SQL Server 2005.
Table Ratings
ratin1 smallint,
ratin2 smallint
ratin3 smallint
These columns can have values from 0 to 5.
How can I select the average value of these fields, but only compare fields where the value is greater then 0.
So if the column values are 1, 3 ,5 - the average has to be 3.
if the values are 0, 3, 5 - The average has to be 4.
This is kind of quick and dirty, but it will work...
SELECT (ratin1 + ratin2 + ratin3) /
((CASE WHEN ratin1 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin2 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin3 = 0 THEN 0 ELSE 1 END) +
(CASE WHEN ratin1 = 0 AND ratin2 = 0 AND ratin3 = 0 THEN 1 ELSE 0 END) AS Average
#mwigdahl - this breaks if any of the values are NULL. Use the NVL (value, default) to avoid this:
Sum columns with null values in oracle
Edit: This only works in Oracle. In TSQL, try encapsulating each field with an ISNULL() statement.
There should be an aggregate average function for sql server.
http://msdn.microsoft.com/en-us/library/ms177677.aspx
This is trickier than it looks, but you can do this:
SELECT dbo.MyAvg(ratin1, ratin2, ratin3) from TableRatings
If you create this function first:
CREATE FUNCTION [dbo].[MyAvg]
(
#a int,
#b int,
#c int
)
RETURNS int
AS
BEGIN
DECLARE #result int
DECLARE #divisor int
SELECT #divisor = 3
IF #a = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #b = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #c = 0 BEGIN SELECT #divisor = #divisor - 1 END
IF #divisor = 0
SELECT #result = 0
ELSE
SELECT #result = (#a + #b + #c) / #divisor
RETURN #Result
END
select
(
select avg(v)
from (values (Ratin1), (Ratin2), (Ratin3)) as value(v)
) as average
You can use the AVG() function. This will get the average for a column. So, you could nest a SELECT statement with the AVG() methods and then SELECT these values.
Pseudo:
SELECT col1, col2, col3
FROM (
SELECT AVG(col1) AS col1, AVG(col2) AS col2, AVG(col3) AS col3
FROM table
) as tbl
WHERE col1 IN (0, 3, 5)
etc.