select inside case statement in where clause tsql - sql

I have written a case condition inside where clause which is working fine without any sub queries, but it is not working with sub queries
for example
declare #isadmin varchar(5) = 'M'
select * from Aging_calc_all a where a.AccountNumber in
(case #isadmin when 'M' then 1 else 0 end)
which is working fine.
However this does not seem to work -
select * from Aging_calc_all a where a.AccountNumber in
(case #isadmin when 'M' then (select AccountNumber from ACE_AccsLevelMaster where AssignedUser=7) else 0 end)
Any suggestions or this is a t-sql bug in 2008.

The error you're receiving is "Subquery returned more than 1 value", I believe. So you can't return multiple values after THEN.
You should rewrite your query to something like this:
select
*
from Aging_calc_all a
where
(#isadmin='M' and a.AccountNumber in (select AccountNumber from ACE_AccsLevelMaster where AssignedUser=7))
or
(#isadmin<>'M' and a.AccountNumber=0)

select * from Aging_calc_all a where a.AccountNumber in (
SELECT AccountNumber
from ACE_AccsLevelMaster
where AssignedUser=7 AND #isadmin = 'M'
UNION ALL select 0 where #isadmin <> 'M'
)
EDITED To show how use multiple criteria
select * from Aging_calc_all a where a.AccountNumber in (
SELECT AccountNumber
from ACE_AccsLevelMaster
where AssignedUser=7 AND #isadmin = 'M'
-- case to select from another table:
UNION ALL select * from ANOTHER_TABLE where #isadmin = 'A'
-- case to select from const set (1, 2, 3):
UNION ALL select * from (
select 1 union all select 2 union all select 3) x where #isadmin = 'B'
-- case to show how 'else' work
UNION ALL select 0 where #isadmin not in( 'M', 'A', 'B')
)

This should work(although it's not a direct answer of the question):
SELECT *
FROM aging_calc_all a
WHERE ( #isadmin <> 'M' AND a.accountnumber = 0 )
OR ( #isadmin = 'M' AND a.accountnumber IN (SELECT accountnumber
FROM ace_accslevelmaster
WHERE assigneduser = 7) )

Related

Want to Return Empty Rows With a Case When Statement

Lets say I'm using 2 case when statements to group my data, like in the below example:
select case
when group1 = 'A' then 'Large'
when group1 = 'B' then 'Medium'
else 'Small'
end as 'Order Size'
,case
when method = 'Delivery' then 'Delivery'
else 'Pick-up'
end as 'Distribution Method'
,count(distinct(OrderIDs))
from OrderTable
GROUP BY
select case
when group1 = 'A' then 'Large'
when group1 = 'B' then 'Medium'
else 'Small'
end
,case
when method = 'Delivery' then 'Delivery'
else 'Pick-up'
end
Lets also say that there were no "Large" deliveries that were "Pick-Up'. Currently, this query will not return a row with Large,PickUp category.
Is there a way to have a row returned with 0’s if there is nothing that meets the multiple case when criteria?
Use a cross join to generate the rows and left join to bring in the data:
select os.OrderSize, coalesce(d.DistributionMethod, 'Pick-Up') as
count(*)
from (select 'Large' as OrderSize union all
select 'Medium' as OrderSize union all
select 'Small' as OrderSize
) os cross join
(select 'Delivery' as DistributionMethod union all
select 'Pick-Up' as DistributionMethod
) d left join
OrderTable ot
on ( (ot.group1 = 'A' and os.OrderSize = 'Large') or
(ot.group1 = 'B' and os.OrderSize = 'Medium') or
(ot.group1 not in ('A', 'B') and os.OrderSize = 'Small')
) and
ot.method = d.DistributionMethod
group by os.OrderSize, coalesce(d.DistributionMethod, 'Pick-Up');
Not all databases support the creation of a table of constants using this syntax, but there is generally some syntax that does this.
You could select a recordset that contains the required values and then left join your grouped recordset from there. Following is an example for SQL Server where you would join your results to [Groupings].[OrderSize] and [Groupings].[DistributionMethod]:
SELECT *
FROM (
SELECT *
FROM (
SELECT 'Large' AS [OrderSize]
UNION
SELECT 'Medium' AS [OrderSize]
UNION
SELECT 'Small' AS [OrderSize]
) AS [OrderSizes]
CROSS JOIN (
SELECT 'Delivery' AS [DistributionMethod]
UNION
SELECT 'Pick-up' AS [DistributionMethod]
) AS [DistributionMethods]
) AS [Groupings]
LEFT JOIN ...

How to use case statement inside where clause of sql 2000

I have a query that contains a WHERE clause with a CASE statement in it (See code below), somehow it doesn't seem to work.
select * FROM
details
where orgcode in
(case when orgtype='P' then
(SELECT distinct [PCode]
FROM [GPOS_Extract].[dbo].[GP8288List])
else
0 end )
How about
select * FROM details
where (orgtype <> 'P' AND orgcode = 0)
or orgcode in
(
SELECT distinct [PCode]
FROM [GPOS_Extract].[dbo].[GP8288List]
)
Or try this:
SELECT * FROM details
WHERE details.orgcode IN
( SELECT DISTINCT
(CASE WHEN details.orgtype='P'
THEN [GPOS_Extract].[dbo].[GP8288List].PCode
ELSE 0 END)
FROM [GPOS_Extract].[dbo].[GP8288List] )
I think the following is the logic that is equivalent to your attempt:
select *
FROM details
where (orgtype = 'P' and
orgcode in (SELECT distinct [PCode]
FROM [GPOS_Extract].[dbo].[GP8288List]
)
) or
((orgtype <> 'P' or orgtype is NULL) and orgcode = 0);
what about this,
select a.*,case when orgtype='P' then PCode else '0' end FROM
details a
left join [GPOS_Extract].[dbo].[GP8288List] b on a.orgcode=b.PCode
case returns a single value. You are trying to use it as though it returns a result set. What you want is:
select * FROM details d
where (orgtype = 'p'
And exists (Select *
From GPOS_Extract.dbo.GP8288List
Where PCode = d.orgcode))
or (orgtype <> 'p' And orgcode= 0)

counting records on the same table with different values possibly none sql server 2008

I have a inventory table with a condition i.e. new, used, other, and i am query a small set of this data, and there is a possibility that all the record set contains only 1 or all the conditions. I tried using a case statement, but if one of the conditions isn't found nothing for that condition returned, and I need it to return 0
This is what I've tried so far:
select(
case
when new_used = 'N' then 'new'
when new_used = 'U' then 'used'
when new_used = 'O' then 'other'
end
)as conditions,
count(*) as count
from myDB
where something = something
group by(
case
when New_Used = 'N' then 'new'
when New_Used = 'U' then 'used'
when New_Used = 'O' then 'other'
end
)
This returns the data like:
conditions | count
------------------
new 10
used 45
I am trying to get the data to return like the following:
conditions | count
------------------
new | 10
used | 45
other | 0
Thanks in advance
;WITH constants(letter,word) AS
(
SELECT l,w FROM (VALUES('N','new'),('U','used'),('O','other')) AS x(l,w)
)
SELECT
conditions = c.word,
[count] = COUNT(x.new_used)
FROM constants AS c
LEFT OUTER JOIN dbo.myDB AS x
ON c.letter = x.new_used
AND something = something
GROUP BY c.word;
try this -
DECLARE #t TABLE (new_used CHAR(1))
INSERT INTO #t (new_used)
SELECT t = 'N'
UNION ALL
SELECT 'N'
UNION ALL
SELECT 'U'
SELECT conditions, ISNULL(r.cnt, 0) AS [count]
FROM (
VALUES('U', 'used'), ('N', 'new'), ('O', 'other')
) t(c, conditions)
LEFT JOIN (
SELECT new_used, COUNT(1) AS cnt
FROM #t
--WHERE something = something
GROUP BY new_used
) r ON r.new_used = t.c
in output -
new 2
used 1
other 0
You can do it as a cross-tab:
select
sum(case when new_used = 'N' then 1 else 0 end) as N,
sum(case when new_used = 'U' then 1 else 0 end) as U,
sum(case when new_used = 'O' then 1 else 0 end) as Other
from myDB
where something = something

How to use Select Exists in Oracle?

What is the equivalent of the below SQL Query in Oracle?
SELECT CAST(
CASE WHEN EXISTS(SELECT * FROM theTable where theColumn like 'theValue%') THEN 1
ELSE 0
END
AS BIT)
I just want an oracle query where exists is used and it returns 0 or 1 like above.
The equivalent would be:
select count(*)
from dual
where exists (SELECT * FROM theTable where theColumn like 'theValue%')
This would show the same output. Just removed the CAST and added a FROM dual as Oracle doesn't allow queries with SELECT and without FROM:
SELECT
CASE WHEN EXISTS(SELECT * FROM theTable where theColumn like 'theValue%')
THEN 1
ELSE 0
END
FROM dual ;
Tested at SQL-Fiddle
You could write it:
SELECT COUNT(*) FROM theTable WHERE theColumn LIKE 'theValue%' AND ROWNUM = 1
This will return 0-1 and the optimizer gets that the query is to be optimized for first-row access.
You could also use MAX with CASE:
SELECT MAX(
CASE
WHEN theColumn like 'theValue%' THEN 1
ELSE
0
END)
AS BIT
FROM theTable
You can use one of the following queries: (the first ones is more performant)
SELECT H.TABLE_ID, H.OTHER_FIELD,
(SELECT 'YES' FROM DUAL WHERE EXISTS (SELECT 'X' FROM TABLE_DETAIL DT
WHERE DT.TABLE_ID = H.TABLE_ID) ) WITH_DETAIL FROM TABLE_HEADER H;
SELECT H.TABLE_ID, H.OTHER_FIELD,
CASE WHEN EXISTS(SELECT * FROM IMTS.DETAIL_TABLE DT WHERE DT.TABLE_ID=H.TABLE_ID)
THEN 'Y' ELSE 'N' END WITH_DETAIL FROM HEADER_TABLE H;
SELECT H.TABLE_ID, H.OTHER_FIELD, NVL2(DT.SOME_NOTNULL_FIELD, 'YES','NO') WITH_DETAIL
FROM TABLE_HEADER H
LEFT JOIN TABLE_DETAIL DT ON DT.TABLE_ID=H.TABLE_ID AND ROWNUM<2;

Query Optimization in oracle

I need to fetch result from one tables and one view joined on 2 columns.
View: 8million records
Table: 5K Records
I went through query plan and observe that query will take very long to run and infact try to run this query but not getting any result.
Please help me in optimize the query.I am not using any hint.
SELECT coupon_upc
, sum ( loyalty_a ) a
, sum ( loyalty_b ) b
, sum ( loyalty_c ) c
, sum ( loyalty_x )
FROM ( SELECT ccd.coupon_upc AS coupon_upc
, ( CASE WHEN a.loyalty_cell = 'A' then 1 else 0 end ) AS loyalty_a
, ( CASE WHEN a.loyalty_cell = 'B' then 1 else 0 end ) AS loyalty_b
, ( CASE WHEN a.loyalty_cell = 'C1' then 1 else 0 end ) AS loyalty_c
, ( CASE WHEN a.loyalty_cell = 'X' then 1 else 0 end ) AS loyalty_x
FROM view1 a
, ( select distinct coupon_upc
, coupon_id
, division
from table2
where schedule_key = 'XXX' ) ccd
WHERE a.campaign_code = 'XXX'
AND a.coupon_id = ccd.coupon_id
AND a.division = ccd.division ) a
GROUP BY coupon_upc
Without the explain plan, or the schema/DDL, there is a limitted amount of optimisation that can be done.
Here is an alternative, but you'd need to test it to see if it makes any difference. (Replace a join with a correlated sub-query.)
SELECT
coupon_upc, sum(loyalty_a) a, sum(loyalty_b) b, sum(loyalty_c) c, sum(loyalty_x) x
FROM
(
SELECT
(
SELECT
coupon_upc
FROM
table2
WHERE
schedule_key = 'XXX'
AND coupon_id = a.coupon_id
AND division = a.division
GROUP BY
coupon_upc
) as coupon_upc,
(case when a.loyalty_cell = 'A' then 1 else 0 end) as loyalty_a,
(case when a.loyalty_cell = 'B' then 1 else 0 end) as loyalty_b,
(case when a.loyalty_cell = 'C1' then 1 else 0 end) as loyalty_c,
(case when a.loyalty_cell = 'X' then 1 else 0 end) as loyalty_x
FROM
view1 a
WHERE
a.campaign_code = 'XXX'
) a
GROUP BY
coupon_upc
Other than that, the kind of optimisations are:
- persisting the view
- indexes
- refactoring data structures
EDIT
Another possible refactor of the query... I don't know how well Oracle would optimise the 4 instances of correlated sub-queries.
SELECT
coupon_upc,
SUM((SELECT COUNT(*) FROM view1 WHERE campaign_code = 'XXX' AND loyalty_cell = 'A' AND coupon_id = map.coupon_id AND division = map.division)) AS loyalty_a,
SUM((SELECT COUNT(*) FROM view1 WHERE campaign_code = 'XXX' AND loyalty_cell = 'B' AND coupon_id = map.coupon_id AND division = map.division)) AS loyalty_b,
SUM((SELECT COUNT(*) FROM view1 WHERE campaign_code = 'XXX' AND loyalty_cell = 'C1' AND coupon_id = map.coupon_id AND division = map.division)) AS loyalty_c,
SUM((SELECT COUNT(*) FROM view1 WHERE campaign_code = 'XXX' AND loyalty_cell = 'X' AND coupon_id = map.coupon_id AND division = map.division)) AS loyalty_x
FROM
(
SELECT coupon_upc, coupon_id, division
FROM table2 WHERE schedule_key = 'xxx'
GROUP BY coupon_upc, coupon_id, division
)
AS map
GROUP BY
coupon_upc
Or maybe...
SELECT
map.coupon_upc, SUM(data.loyalty_a) AS a, SUM(data.loyalty_b) AS b, SUM(data.loyalty_c) AS c, SUM(data.loyalty_x) AS X
FROM
(
SELECT coupon_upc, coupon_id, division
FROM table2 WHERE schedule_key = 'xxx'
GROUP BY coupon_upc, coupon_id, division
)
AS map
INNER JOIN
(
SELECT
coupon_id,
division,
SUM(CASE WHEN loyalty_cell = 'A' THEN 1 ELSE 0 END) AS loyalty_a,
SUM(CASE WHEN loyalty_cell = 'B' THEN 1 ELSE 0 END) AS loyalty_b,
SUM(CASE WHEN loyalty_cell = 'C1' THEN 1 ELSE 0 END) AS loyalty_c,
SUM(CASE WHEN loyalty_cell = 'X' THEN 1 ELSE 0 END) AS loyalty_x
FROM
view1
WHERE
campaign_code = 'XXX'
)
AS data
ON data.coupon_id = map.coupon_id
AND data.division = map.division
GROUP BY
map.coupon_upc
Another possible rewrite would be:
SELECT
map.coupon_upc
, COUNT(CASE WHEN a.loyalty_cell = 'A' THEN 1 ELSE NULL END) AS loyalty_a
, COUNT(CASE WHEN a.loyalty_cell = 'B' THEN 1 ELSE NULL END) AS loyalty_b
, COUNT(CASE WHEN a.loyalty_cell = 'C1' THEN 1 ELSE NULL END) AS loyalty_c
, COUNT(CASE WHEN a.loyalty_cell = 'X' THEN 1 ELSE NULL END) AS loyalty_x
FROM
( SELECT coupon_upc, coupon_id, division
FROM table2 WHERE schedule_key = 'xxx'
GROUP BY coupon_upc, coupon_id, division
) AS map
JOIN view1 a
ON a.coupon_id = map.coupon_id
AND a.division = map.division
WHERE
a.campaign_code = 'xxx'
GROUP BY
map.coupon_upc
Do you have indexes on the fields that are used in the JOIN, WHERE and the GROUP BY clauses?