Complex Conditional Query in Where Clause - sql

In my company, each department has their own statuses for purchase orders. I'm trying to load only the pertinent POs for a specific user's department. I keep getting errors. It seems like it should be correct, though. I initially attempted to use a Case statement, but it appears that SQL can only do a simple return from a case, so I'm now attempting If statements. Current error is Incorrect Syntax near the keyword IF. It looks right to me. It's as if the incorrect syntax is that it is in the IN parentheses.
declare #Dept nvarchar(12);
set #Dept = 'IT'
SELECT *
FROM TBL_ORDERS
WHERE ORD_STATUS IN
(
IF #Dept = 'PURCH'
BEGIN
SELECT distinct * FROM (VALUES ('PurchStat1'), ('PurchStat2'), ('PurchStat3'), ('PurchStat4')) AS X(a)
END
ELSE
IF #Dept = 'ADMIN'
BEGIN
SELECT distinct * FROM (VALUES ('ADStat1')) AS X(a)
END
ELSE
IF #Dept = 'IT'
BEGIN
SELECT distinct * FROM (VALUES ('ADStat1'), ('PurchStat1'), ('PurchStat2'), ('PurchStat3'), ('PurchStat4'), ('ITStat1'), ('ITStat2')) AS X(a)
END
ELSE END
)

There's a conceptual problem to address here first, and then we can look at how to actually do what you want.
select is starting a statement here. Statements end with a semicolon. You can't put statements inside other statements. What you are doing is the same as trying to do something like this in c#:
int i = if (true) 1; else 2;;
You can of course use expressions inside statements:
int i = true ? 1 : 2;
Moreover, select is a declarative statement. You can't do imperative flow of control inside a declarative language construct. You're mixing metaphors, as it were. To understand the declarative/imperative distinction see this question and in particular (in my opinion) this answer.
So the first thing to do is wrap your head around the declarative nature of SQL statements like select. Yes, T-SQL also includes imperative constructs like if and while, but you can't do imperative inside declarative.
You can use conditional expressions (and other expressions) inside a declarative statement:
select name,
case
when name = 'date' then 'this is the date row'
else 'this is not the date row'
end
from sys.types;
In this example the declarative select says what to do with all of the rows returned by the from clause. I don't write a while loop or a for loop in order to instruct the computer to loop over each row and provide instructions inside the loop. The from returns all the rows, and the select declares what I want to do with each of them. The case expression will be evaluated against every row in sys.types.
OK, so what about your specific question? There's many ways to write the code. Here is one way that is very similar to your current structure. First I conditionally (imperatively!) populate a temp table with the statuses I want. Then I declaratively use that temp table as my filter:
create table #statuses
(
statusname varchar(32)
);
declare #dept nvarchar(12) = 'IT';
if (#dept = 'IT')
begin
insert #statuses (statusname) values
('ADStat1'), ('PurchStat1'), ('PurchStat2'), ('PurchStat3'), ('PurchStat4'), ('ITStat1'), ('ITStat2');
end
else if (#dept = 'PURCH')
begin
insert #statuses (statusname) values
('PurchStat1'), ('PurchStat2'), ('PurchStat3'), ('PurchStat4');
end
else if (#dept = 'ADMIN')
begin
insert #statuses (statusname) values
('ADStat1');
end
select *
from tbl_orders
where ord_status in (select statusName from #statuses);
Can I do it without the temp table? Sure. Here's one way:
declare #dept nvarchar(12) = 'IT';
select *
from tbl_orders
where (#dept = 'ADMIN' and ord_status = 'ADStat1')
or (#dept = 'PURCH' and ord_status in ('PurchStat1', 'PurchStat2', 'PurchStat3', 'PurchStat4'))
or (#dept = 'IT' and ord_status in ('ADStat1', 'PurchStat1', 'PurchStat2', 'PurchStat3', 'PurchStat4', 'ITStat1', 'ITStat2'));
Here we evaluate a different in depending on the value of #dept. Clearly only one of them actually needs to be evaluated, and the other two don't really need to be there, depending on which value of #dept is provided. Adding an option (recompile) can be beneficial in cases like this. For more information about option (recompile) look here and here.

If for a reason storing those status/department data in tables is not possible you can use union
declare #Dept nvarchar(12);
set #Dept = 'IT'
SELECT *
FROM TBL_ORDERS
WHERE ORD_STATUS IN
(
SELECT distinct *
FROM (VALUES ('PurchStat1'), ('PurchStat2'), ('PurchStat3'), ('PurchStat4')) AS X(a)
WHERE #Dept ='PURCH'
union all
SELECT distinct *
FROM (VALUES ('ADStat1')) AS X(a)
WHERE #Dept ='ADMIN'
union all
SELECT distinct *
FROM (VALUES ('ADStat1'), ('PurchStat1'), ('PurchStat2'), ('PurchStat3'), ('PurchStat4'), ('ITStat1'), ('ITStat2')) AS X(a)
WHERE #Dept ='IT'
)

Related

replacing TSQL NOT EXISTS in SQL-92

I am have following code which works well in TSQL:
BEGIN
IF NOT EXISTS (select * from tblDCUSTOM where id = 'All Customers')
BEGIN
INSERT INTO tblDCUSTOM
(ID
,Name
,English
)
SELECT 'All Customers','All Customers','All Customers'
END
END
Now, I need to have this functionality in an custom environment, where SQL-92 is used - so no EXISTS (edit: not true, EXISTS works in SQL-92) or BEGIN-END is possible. Any Ideas?
As per very first comment;
INSERT INTO tblDCUSTOM
(ID
,Name
,English
)
SELECT 'All Customers','All Customers','All Customers'
WHERE (SELECT COUNT(*) FROM tblDCUSTOM where id = 'All Customers') >= 1
If TOP is supported this might be better
INSERT INTO tblDCUSTOM
(ID
,Name
,English
)
SELECT 'All Customers','All Customers','All Customers'
WHERE (SELECT TOP 1 1 as F FROM tblDCUSTOM where id = 'All Customers') IS NOT NULL
I must warn you, many have tried to make a 'database agnostic' system. It's not worth it.
This is the correct answer, the EXISTS statement IS actually supported:
Put the condition in the WHERE: INSERT ... SELECT ... WHERE NOT EXISTS
(...). This is arguably better practice even in T-SQL, to make the
operation atomic.

Adding dynamic condition in where clause

I want to add condition in 'where' clause depends upon 'if' condition like below
Declare #strSQLClause varchar(50)
If (#QMasterCompanyId='09' and #State='FL' and (#LOB='HO' or #LOB='HP'))
Begin
Declare #strMonthsOccupied char(1)
select Distinct #strMonthsOccupied=sm.MonthsOccupiedDesc from HOStructureRating sr
join HOSeleMonthsOccupied sm on sr.MonthsOccupied=sm.MonthsOccupiedCd
where AppId=#AppId
If(CONVERT(int,LTRIM(RTrim(#strMonthsOccupied))) > 9)
Begin
set #strSQLClause ='AND QuestionCd!=Q8'
End
Else
set #strSQLClause =''
End
so that in my Query will work as
select * from SHSeleQuestions where MasterCompanyId='09' + #strSQLClause
But this approach is not working fine, can anyone please help me on this.
There are two ways to do this one is use dynamic sql or other one is below mention :
select *
from SHSeleQuestions
where MasterCompanyId='09' AND
1 = CASE WHEN LEN(#strSQLClause) > 0 AND QuestionCd != 'Q8' THEN 1
WHEN LEN(#strSQLClause) = 0 THEN 1 END
Using Dynamic SQL
EXEC('select * from SHSeleQuestions where MasterCompanyId=''09''' + #strSQLClause ')
You would need to use dynamic SQL, but why not just have two statements that execute SQL, so rather than set #strSQLClause = 'AND ...', simply have a select statement here with the condition(s) you need
IF (#QMasterCompanyId='09' AND #State='FL' AND (#LOB='HO' OR #LOB='HP'))
BEGIN
DECLARE #strMonthsOccupied CHAR(1)
SELECT DISTINCT #strMonthsOccupied = sm.MonthsOccupiedDesc
FROM HOStructureRating sr
INNER JOIN HOSeleMonthsOccupied sm
ON sr.MonthsOccupied=sm.MonthsOccupiedCd
WHERE AppId=#AppId;
IF(CONVERT(INT,LTRIM(RTRIM(#strMonthsOccupied))) > 9)
BEGIN
SELECT *
FROM SHSeleQuestions
WHERE MasterCompanyId='09'
AND QuestionCd!='Q8';
RETURN;
END
END
SELECT *
FROM SHSeleQuestions
WHERE MasterCompanyId='09';
That being said, there are so many issues with the above I don't really know where to begin. You declare your variable then assign it an indeterminate value:
DECLARE #strMonthsOccupied CHAR(1)
SELECT DISTINCT #strMonthsOccupied = sm.MonthsOccupiedDesc
FROM HOStructureRating sr
INNER JOIN HOSeleMonthsOccupied sm
ON sr.MonthsOccupied=sm.MonthsOccupiedCd
WHERE AppId=#AppId;
If the query returns multiple rows then there is no clear logic for which value the variable should be assigned. The nex issue is that this CHAR(1) variable is clearly a number based on your attempted conversion:
IF(CONVERT(INT,LTRIM(RTRIM(#strMonthsOccupied))) > 9)
Why not just cut out the middle man and declare an INT to begin with. The next point is that it is a CHAR(1) so isn't actually big enough to store anything greater than 9, so your above condition will never be true.
Even if sm.MonthsOccupiedDesc, was 10, the variable would simply be truncated to 1, which is smaller than 9, so fails the condition, e.g.
DECLARE #strMonthsOccupied CHAR(1) = '10';
IF(CONVERT(INT,LTRIM(RTRIM(#strMonthsOccupied))) > 9)
PRINT 'TRUE';
ELSE
PRINT 'FALSE';

Querying different table based on a parameter

I have a stored procedure that I would like to query either the production or the "work in progress" table, based on the parameter I am passing in. I could write two separate stored procedures, but I thought this was worth a try.
something along the lines of:
create procedure getUserDetails
#userID int,
#prod varchar(5)
as
begin
select * from
if (#prod = 'true')
Begin
userprod_table
else
userwip_table
end
where something = 'something'
END
Is this at all possible? I wouldn't want to write 2 SP that are almost identical :-/
Why not use a simple if(#prod = 'true') statement like below:
if (#prod = 'true')
begin
select * from userprod_table where something = 'something'
end else
begin
select * from userwip_table where something = 'something'
end
You could use a CTE so that your main query isn't repeated
with usertable as
(
select * from userprod_table where 1 = #flag
union
select * from userwip_table where 0 = #flag
)
select ... from usertable ...

TSQL: if statement inside a select insert

I have this structure inside a function:
INSERT INTO #TheTable
SELECT DISTINCT
#Number AS Number,
#Name AS Name
FROM MYTABLE
WHERE id = #id
I need to add to this structure the result of executing another function and I need your help to do so.
Below the #Name AS Name, line I should add something like this
IF (dbo.anotherFunction(#id)==1)
#NewVisitor AS NewVisitor
ELSE
#NewVisitor AS NoNewVisitor
How can I translate this into TSQL??
Thanks a million!
Guessing this is what you want...
INSERT INTO #TheTable
SELECT DISTINCT
#Number AS Number,
#Name AS Name,
case when (dbo.anotherFunction(#id)=1) then #NewVisitor else null end as NewVisitor,
case when (dbo.anotherFunction(#id)<>1) then #NewVisitor else null end AS NoNewVisitor
FROM MYTABLE
WHERE id = #id
IF statements aren't available inside of SELECT statements. Instead, you'll want to utilize a CASE.
MSDN Documentation on CASE
This question makes little sense to me (why all the variables, what do the columns mean?), but you can do it this way:
IF (dbo.anotherFunction(#id)==1)
INSERT INTO #TheTable
SELECT DISTINCT
#Number AS Number,
#Name AS Name,
#NewVisitor AS NewVisitor
FROM MYTABLE
WHERE id = #id
ELSE
INSERT INTO #TheTable
SELECT DISTINCT
#Number AS Number,
#Name AS Name,
#NewVisitor AS NoNewVisitor
FROM MYTABLE
WHERE id = #id
Alternatively use two CASE statements to selected NewVisitor and NoNewVisitor.

IF condition in view in SQL Server

Is it possible to have a if condition in VIEWS
eg
CREATE VIEW
as
DECLARE #Count int
SET #Count=-1
select #Count=EmpID from EmployeeDetails where ID=200
IF #Count=-1
BEGIN
SELECT * FROM TEAM1
END
ELSE
BEGIN
SELECT * FROM TEAM1
END
You could try something sneaky with a UNION :
SELECT {fieldlist}
FROM Table1
WHERE EXISTS(SELECT EmpID FROM EmployeeDetails WHERE ID = 200)
UNION ALL
SELECT {fieldlist}
FROM Table2
WHERE NOT EXISTS(SELECT EmpID FROM EmployeeDetails WHERE ID = 200)
This method would require both SELECT statements to return the same set of fields, although their sources might be different.
Views only allow select statements as stated in here
if you need to do if on column values you can use a
SELECT
CASE WHEN COLUMN1 = 1 THEN COLUMNX ELSE COLUMNY END
FROM TABLE1
if your need exceeds this you should create a select from a table valued function instead of a view.
What you need is a simple Procedure
CREATE PROCEDURE DOSOMETHING
(
#ID INT
)
AS
BEGIN
IF #ID > 100
SELECT 1 AS ID,'ME' AS NAME, GETDATE() AS VARIABLEDATECOL, NEWID() AS VARIABLEGUID
ELSE
SELECT 2 AS ID, 'YOU' AS NAME
END
No I don't believe this is possible.
You could use a stored procedure instead to achieve this functionality.
simply use a udf (User defined Function)
Here you can use IF, ELSE, WHILE etc.
But when you are manipulating data (INSERT, UPDATE, DELETE) then you have to use Stored Procedures because udf's aren't able to do that