Using a subquery in a dynamic where clause - sql-server

I'm trying to do the following:
SELECT *
FROM myTable
WHERE workplace IN
(CASE #param
WHEN 'a' THEN (SELECT workplace
FROM workPlaceTable
WHERE condition1)
WHEN 'b' THEN (SELECT workplace
FROM workPlaceTable
WHERE condition2)
END)
This will always return:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Has anybody an idea how I can realize this without an if and repeating the query?

I don't think you need the subquery at all, just use a JOIN with a WHERE clause:
SELECT T1.*
FROM myTable T1
INNER JOIN workPlaceTable T2 ON T1.workplace = T2.workplace
WHERE (#param = 'a' AND condition1) OR (#param = 'b' AND condition2)
If #param = 'a' then condition1 will be evaluated, otherwise condition2 will be evaluated.

Has anybody an idea how I can realize this without an if and repeating
the query?
Something like this could work for you.
select *
from myTable
where #Param = 'a' and workplace in (
select workplace
from workPlaceTable
where condition1
) or
#Param = 'b' and workplace in (
select workplace
from workPlaceTable
where condition2
);

To elaborate on #Allan's answer, a join would look something like this:
SELECT myTable.*
FROM myTable
INNER JOIN workPlaceTable ON myTable.workplace = workPlaceTable.workplace AND
(
(#param = 'a' AND <<condition1>>) OR (#param = 'b' AND <<condition2>>)
)
By the way, this question has nothing to do with dynamic-sql.

You can try this;
SELECT *
FROM myTable
WHERE (#param = 'a' and
workplace in
(SELECT workplace FROM workPlaceTable WHERE condition1))
OR (#param = 'b' and
workplace in
(SELECT workplace FROM workPlaceTable WHERE condition2))

Related

Update statement in SQL Server using two tables

This my query
UPDATE #Student_tbl
SET MStudentId = (SELECT StudentId
FROM #StudentHistory_tbl
WHERE UserId NOT IN (SELECT UserId
FROM #Student_tbl)
)
it returns an error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The error is fairly explicit, when you write:
UPDATE #Student_tbl
SET MStudentId = (
Select StudentId
from #StudentHistory_tbl
where UserId not in (select UserId from #Student_tbl)
)
You are updating all rows in the #Student_tbl. You are trying to set MSStudentId to equal multiple rows, as your select does not return just one row.
You would have to look at either your subquery, to get it to return 1 row, or join the subquery to the 'StudnetHistory_tbl so you are updating the column with a singular row value.
You can use JOINS in update statements. Try something like this:
UPDATE t1
SET t1.MStudentId = t2.Studentid
FROM #Student_tbl AS t1
INNER JOIN #StudentHistory_tbl AS t2 ON t2.UserId = t1.UserId
WHERE t1.MStudentId <> t2.Studentid;
I think you need something like this
UPDATE #Student_tbl
SET MStudentId= a.StudentId
from (Select StudentId,UserId from #StudentHistory_tbl ,#Student_tbl
where #StudentHistory_tbl.UserId <> #Student_tbl.UserId ) a
where a.UserId <> #Student_tbl.UserId
OR
UPDATE #Student_tbl
SET MStudentId= a.StudentId
from (Select StudentId,UserId from #StudentHistory_tbl
where UserId not in (select UserId from #Student_tbl) ) a
where a.UserId not in (select UserId from #Student_tbl)

Not allowing Multiple CASE Subquery Results when using IN

I'm working with the following query:
SELECT *
FROM TableA a
WHERE a.FieldA IN (
CASE
--select subquery returns a single value
WHEN a.FieldB = 'Value1'
THEN (select b.ID from TableB b where b.FK_Field = '123')
--select subquery returns multiple values
WHEN a.FieldB = 'Value2'
THEN (select c.ID from TableC c where c.FK_Field = '123')
END
)
The first case select statement returns only a single b.ID. If I just have that statement, my code works.
The second case statement, however, returns multiple c.IDs. When I add that check, I get the following error:
Subquery returned more than 1 value. This is not permitted when the
subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression.
If I would have WHERE a.FieldA =, then I understand that the subquery can only return 1 value. I however have WHERE a.FieldA IN, so why is it complaining if there are multiple values returned?
How can I implement this kind of check?
As #marc_s explained in a comment:
CASE in T-SQL is an expression (like a+b) which returns a single,
atomic value - you cannot use it to selectively run SQL snippets that
return entire result sets
In order to resolve this error, I removed the CASE statement and instead used a bunch of AND and OR statements to accomplish the same kind of check.
SELECT *
FROM TableA a
WHERE
(a.FieldB = 'Value1'
AND a.FieldA IN (select b.ID from TableB b where b.FK_Field = '123'))
OR (a.FieldB = 'Value2'
AND a.FieldA IN (select c.ID from TableC c where c.FK_Field = '123'))
This code is a bit messier than a CASE statement, but it works.
Lots of ways of doing this, here is one way using union all and a correlated EXISTS statement
;WITH cte AS (
SELECT 'Value1' as FieldB, b.Id
FROM
TableB
WHERE
b.FK_FieldId = '123'
UNION ALL
SELECT 'Value1' as FieldB, c.Id
FROM
TableB
WHERE
c.FK_FieldId = '123'
)
SELECT *
FROM
TableA a
WHERE
EXISTS (SELECT 1
FROM
cte c
WHERE
a.FieldB = c.FieldB
AND a.FieldA = c.Id)
The problem with the way you have written it is that you are getting a non-scalar value (meaning more than 1 row) where sql is expecting a scalar value. In the case expression only scalar values can be used in the THEN part as well as some rules in WHEN as well. To solve you need to break apart your case expression to multiple where statements and/or use some other technique such as the one above.
Or you could write your case expression like this:
SELECT *
FROM
TableA a
WHERE
(CASE
WHEN a.FieldB = 'Value1' AND a.FieldA IN (select b.ID from TableB b where b.FK_Field = '123') THEN 1
WHEN a.FieldB = 'Value2' AND a.FieldA IN (select c.ID from TableC c where c.FK_Field = '123') THEN 1
ELSE 0
END) = 1
Don't use case in predicates if it is not necessary - using case make your argument non-SARG (you qry will not use index).
SELECT *
FROM TableA a
WHERE EXISTS(
SELECT NULL
FROM TableB b
WHERE a.FieldB = 'Value1'
AND b.FK_Field = '123'
AND a.FieldA = b.ID))
OR EXISTS(
SELECT NULL
FROM TableC c
WHERE a.FieldB = 'Value2'
AND c.FK_Field = '123'
AND a.FieldB = c.ID))
Use indexes. And try to make your qry readable.
Or if you would like use semi-join:
SELECT *
FROM TableA a
WHERE a.FieldA IN (
SELECT b.ID
FROM TableB b
WHERE a.FieldB = 'Value1'
AND b.FK_Field = '123'))
OR a.FieldB IN (
SELECT c.ID
FROM TableC c
WHERE a.FieldB = 'Value2'
AND c.FK_Field = '123'))
Both of this solutions are with SARG.

Using BETWEEN operator in CASE (THEN) statement

I am trying the following but its throwing an error, can someone tell me what I am doing wrong?
SELECT custType, count(*)
FROM tbl1 a
LEFT JOIN tbl2 b ON a.tbID = b.tbID
AND ((CustType IN ('Apple','IPAD') and date BETWEEN #StartDate AND #EndDate)
or (CustType IN ('Samsung','LCD') and date BETWEEN #StartDateLL AND #EndDateLL))
GROUP BY CustType
Why don't use AND/OR
SELECT CustType , count(*)
FROM tbl1 a
LEFT JOIN tbl2 b ON a.tbID = b.tbID
AND ((CustType IN ('Apple','IPAD') and date BETWEEN #StartDate AND #EndDate)
or (CustType IN ('Samsung','LCD') and date BETWEEN #StartDateLL AND #EndDateLL))
group by CustType ;
EDIT You can't group by CustType and Select *
You cannot use a BETWEEN operator for the THEN part of a case statement or IIF. BETWEEN is the condition that translates to true and false. So it is VALUE BETWEEN A AND B and would be apart of the WHEN section of a case statement.
Also the case statement returns a value not a formula to be further executed.
I agree with #Vercelli's solution but to show you how a case statement works in the method you are trying you could do the following:
SELECT *
FROM tbl1 a
LEFT JOIN tbl2 b ON a.tbID = b.tbID
AND (CASE
WHEN CustType IN ('Apple','IPAD') AND [date] ETWEEN #StartDate AND #EndDate THEN 1
WHEN CustType IN ('Samsung','LCD') AND [date] BETWEEN #StartDateLL AND #EndDateLL THEN 1
ELSE 0 END) = 1
Again go with #vercelli's answer as it is the more appropriate way of writing the SQL query.

How to write where condition on the basis of selected value

I have a multi select drop down containing 20 values.
User can select all or few.
I want to write a query
if user has selected all the values then I don't want to pass all the selected values. (I will pass 0 to identify that all values are selected)
If use has selected few(or not all) then I want to pass only those selected values. (I will be passing comma separated values ex 101,102,103,104)
On the basis of the values I want write a join table or use it in a where condition
I have done something like as follows but it seem not working.
WHERE: Following throwing error (Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.)
DECLARE #EntitiyIds AS varchar(2500) = '0'
SELECT *
FROM Tb_CompanyType
WHERE Id IN (
CASE WHEN #EntitiyIds <> '0' THEN (SELECT Item FROM dbo.Split(#EntitiyIds, ','))
WHEN #EntitiyIds = '0' THEN (SELECT Id FROM Tb_CompanyType) END
)
INNER JOIN: (Following is not giving proper output)
DECLARE #EntitiyIds AS VARCHAR(2500) = '101'
SELECT * FROM Tb_CompanyType CT
INNER JOIN Tb_Company TC ON
CASE WHEN #EntitiyIds = '0' AND TC.CompanyId = CT.Id THEN 1
WHEN #EntitiyIds <> '0' AND TC.CompanyId = (SELECT Item FROM dbo.Split(#EntitiyIds, ',')) THEN 1 END = 1
dbo.Split returns table containing comma separated values
Kindly help
You can do it this way.
SELECT *
FROM Tb_CompanyType
WHERE #EntitiyIds = '0'
OR Id IN (SELECT Item
FROM dbo.Split(#EntitiyIds, ','))
Something like this perhaps.
SELECT Id FROM Tb_CompanyType where #EntitiyIds = '0'
UNION ALL
SELECT Item FROM dbo.Split(#EntitiyIds, ',') s where #EntitiyIds <> '0'
From the error message your provided, it seems that your sub-query is returning more than one rows. You last query segment's last line seems problematic:
DECLARE #EntitiyIds AS VARCHAR(2500) = '101'
SELECT * FROM Tb_CompanyType CT
INNER JOIN Tb_Company TC ON
CASE WHEN #EntitiyIds = '0' AND TC.CompanyId = CT.Id THEN 1
WHEN #EntitiyIds <> '0' AND TC.CompanyId = (SELECT Item FROM dbo.Split(#EntitiyIds, ',')) THEN 1 END = 1
Change the last
SELECT Item FROM dbo.Split(#EntitiyIds, ',')
to
SELECT top 1 Item FROM dbo.Split(#EntitiyIds, ',')

MSSQL subquery result to show and calculate

I need show a subquery result and use this same result to calculate other value, is possible set this value in a variable in MS SQL 2008 or something like this?
exemple:
SELECT
#test = (SELECT COUNT(*) FROM [tableTest] WHERE [tableTest].[columnA] = [tableA].[columnA]) as 'Counter'
, (#test * 50) as 'Calc'
, [tableA].[columnA]
FROM tableA
you may use a cte and join on it.
with cte as (select count(*) cnt, columnA from [tableTest] group by columnA)
select
c.cnt as 'Counter',
c.cnt * 50 as 'Calc',
a.columnA
from tableA a
join cte c on c.columnA = a.columnA
It could also be done with a subquery, of course
select
a.columnA,
c.cnt as 'Counter',
c.cnt * 50 as 'Calc'
from tableA a
join (select columnA, count(*) as cnt
from tableTest
group by columnA) c
on c.columnA = a.columnA
Why can't you do this. Move the subquery outside of select statement and store the result in a variable
Then use that variable for calculations.
declare #test int = (SELECT COUNT(*) FROM [tableTest])
SELECT
#test as 'Counter'
, (#test * 50) as 'Calc'
, [tableA].[columnA]
FROM tableA
Update :
SELECT [Counter],
( [Counter] * 50 ) AS 'Calc',
columnA
FROM (SELECT (SELECT Count(*)
FROM [tableTest]
WHERE [tableTest].[columnA] = [tableA].[columnA]) AS 'Counter',
[tableA].[columnA]
FROM tableA) A
You can also use correlated sub-queries:
SELECT
Counter = (SELECT COUNT(*) FROM tableTest t WHERE t.columnA = a.columnA),
Calc = (SELECT COUNT(*) FROM tableTest t WHERE t.columnA = a.columnA) * 50,
a.columnA
FROM tableA a
It'll be optimized to be only evaluated once.
Try:
SELECT t2.[Count]
,t2.[Count] * 50 As [Calc]
,[tableA].[columnA]
FROM TableA
CROSS APPLY
(
SELECT COUNT(*) AS [Count]
FROM TableTest
WHERE [tableTest].[columnA] = [tableA].[columnA]
) t2

Resources