IF or CASE in WHERE clause of SQL query - sql

My problem is I have a query like this:
SELECT * FROM TABLE1 WHERE STATUSID = #PARAMETER
Then I want to create that query into dynamic one like this:
IF #PARAMETER IS 1 THEN STATUSID = 1
IF #PARAMETER IS 2 THEN STATUSID = 2
IF #PARAMETER IS 3 THEN STATUSID = 1 OR STATUSID = 2
Can anyone give me a sample query how to do this? I just want to do this inside WHERE clause so that I can minimize my line of code.

Use OR
SELECT *
FROM TABLE1
WHERE (STATUSID = 1 and #PARAMETER = 1)
OR (STATUSID = 2 and #PARAMETER = 2)
or (STATUSID in (1,2) and #PARAMETER = 3)

Use CASE Clause to get result :
SELECT * FROM TABLE1 WHERE STATUSID = CASE
WHEN #PARAMETER = 1 THEN 1
WHEN #PARAMETER = 2 THEN 2
WHEN #PARAMETER = 3 THEN STATUSID END

Another solution would be to first check the parameter and then execute the corresponding query.
IF #PARAMETER = 1 THEN SELECT * FROM TABLE1 WHERE STATUSID = 1
IF #PARAMETER = 2 THEN SELECT * FROM TABLE1 WHERE STATUSID = 2
IF #PARAMETER = 3 THEN SELECT * FROM TABLE1 WHERE STATUSID = 1 OR STATUSID = 2
Is the easiest for the engine to optimize.

Related

How can I change the type of output?

My sample query is:
SELECT
SUM(CASE WHEN tb.[CashStatus] = 0 AND tb.[CashPayMethod] = 0 THEN tb.[CashPayPrice] ELSE 0 END) AS [TotalCashPrice],
SUM(CASE WHEN tb.[CashStatus] = 0 AND tb.[CashPayMethod] = 10 THEN tb.[CashPayPrice] ELSE 0 END) AS [TotalPOSPrice]
FROM
mytable tb
The result is:
TotalCashPrice | TotalPOsPrice
---------------+----------------
41,000,000 | 12,000,000
I want to change it to:
Value | Name
-----------------+--------------------
41,000,000 | TotalCashPrice
12,000,000 | TotalPOSPrice
Thanks everyone.
I would use GROUP BY and move the CASE expression to the column being aggregated:
SELECT (CASE WHEN tb.CashPayMethod = 0 THEN 'TotalCashPrice'
WHEN tb.CashPayMethod = 10 THEN 'TotalPOSPrice'
END) as name,
SUM(tb.CashPayPrice) as value
FROM mytable tb
WHERE tb.CashStatus = 0 AND tb.CashPayMethod IN (0, 10)
GROUP BY tb.[CashPayMethod]
One simple option here is to just take a union:
SELECT SUM(CashPayPrice) AS Value, 'TotalCashPrice' AS Name
FROM mytable
WHERE CashStatus = 0 AND CashPayMethod = 0
UNION ALL
SELECT SUM(CashPayPrice), 'TotalPOSPrice'
FROM mytable
WHERE CashStatus = 0 AND CashPayMethod = 10;
Another option would be to look into using SQL Server's UNPIVOT operator. But that might be overkill if your actual problem isn't much bigger than this.
You can use UNPIVOT to achieve desired output:
declare #tmp table(TotalCashPrice int, TotalPOsPrice int)
insert into #tmp values (41000000, 12000000)
select u.[Value], u.[Name]
from #tmp s
unpivot
(
[Value]
for [Name] in ([TotalCashPrice], [TotalPOsPrice])
) u
Result:
If the original column names (TotalCashPrice, TotalPOsPrice) can change in number you will need dynamic TSQL.

How to provide dynamic usage of select query in SQL Server 2008 based on a variable value

I have a table myTable with these columns and sample data:
Id Name Period0Status Period1Status Period2Status
-------------------------------------------------------
1 Mark 1 2 3
2 John 2 3 3
3 Brad 1 1 1
4 John 3 3 3
5 Mark 1 3 2
etc...
What I want is to use those data but the column names should be taken according to a SQL variable. What I need is something like that:
declare #0_period varchar(50) = 'Period_0'
declare #1_period varchar(50) = 'Period_1'
declare #2_period varchar(50) = 'Period_2'
declare #counter = 0;
while #counter < 3
begin
insert into #Results(Period, JohnVolume, MarkVolume, JoshVolume)
select
'#' + #counter + '_period',
sum(case when(name = 'John' and (Period+#counter+Status = 3) then 1 else 0)),
sum(case when(name = 'Mark' and (Period+#counter+Status = 3) then 1 else 0)),
sum(case when(name = 'Brad' and (Period+#counter+Status = 3) then 1 else 0))
from
myTable
set #counter = #counter + 1
end
I could not find the correct syntax to provide the dynamic query here. Any help would be appreciated. SQL Server 2008 is used.
Output table I want to produce be like:
Period JohnVolume MarkVolume BradVolume
Period_0 1 0 0
Period_1 2 1 0
Period_2 2 1 0
It should basically counts the amount of 3s for each (John,Mark and Brad) for each period. I gotta find out how to correct the syntax for the parts that #counter is used inside the select query.
Here's an example of how you could do this, with a cross apply to derive the values for each period and a count(case...) for the end values:
SELECT period
, COUNT(CASE WHEN Name = 'John' AND val = 3 THEN 1 END) [JohnVolume]
, COUNT(CASE WHEN Name = 'Mark' AND val = 3 THEN 1 END) [MarkVolume]
, COUNT(CASE WHEN Name = 'Brad' AND val = 3 THEN 1 END) [BradVolume]
FROM myTable
CROSS APPLY (VALUES ('Period_0', Period0Status), ('Period_1', Period1Status), ('Period_2', Period2Status)) unp(period, val)
GROUP BY period
The cross apply works as an unpivot to get each period status and the values within them, and the conditional case statements work as a pivot.
Alternatively, you could use an actual UNPIVOT/PIVOT if you want. An example would be:
SELECT Period, John JohnVolume, Mark MarkVolume, Brad BradVolume
FROM (
SELECT *
FROM myTable
UNPIVOT (val FOR period IN (Period0Status, Period1Status, Period2Status)) U
WHERE val = 3) T
PIVOT (COUNT(val) FOR Name IN ([John], [Mark], [Brad])) P

Union different tables with differents columns and data

My problem is that I have 4 differents SELECT with
SELECT COUNT (*) AS regular
WHERE experience = 1 AND bl = 1
SELECT COUNT (*) AS rptmm
WHERE experience = 1 AND bl = 0
SELECT COUNT (*) AS new
WHERE experience = 0 AND bl = 0
SELECT COUNT (*) AS rptss
WHERE experience = 0 AND bl = 1
I want that the results appear together whith the respective names like:
regular rptmm new rptss
10 5 2 6
Firstly, I'd suggest not to use Count()*. There are many answers on this site explaining why so I am not going to repeat it.
Instead, I'd suggest you to use a query like this:
SELECT (SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 1 AND tab.bl = 1) AS 'Regular',
(SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 1 AND tab.bl = 0) AS 'rptmm',
(SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 0 AND tab.bl = 0) AS 'New',
(SELECT COUNT (tab.someColumnName)
FROM TableName tab
WHERE tab.experience = 0 AND tab.bl = 1) AS 'rptss'
Hope this helps!!!
Just put UNION ALL between your four statements you will get four rows with each count on its own row. However, you will lose the column name. You could also use join to get one row with four columnes. Just put the keyword join between each sql statement.
SELECT COUNT (*) AS regular
WHERE experience = 1 AND bl = 1
JOIN
SELECT COUNT (*) AS rptmm
WHERE experience = 1 AND bl = 0
JOIN
SELECT COUNT (*) AS new
WHERE experience = 0 AND bl = 0
JOIN
SELECT COUNT (*) AS rptss
WHERE experience = 0 AND bl = 1
You could create a temp table to hold all of this data for you: Replace Name1, Name2, Name3,Name4 with whatever you want to call them. These will be the column headers.
CREATE TABLE #Temp(
NAME1 INT
,NAME2 INT
,NAME3 INT
,NAME4 INT
)
INSERT INTO #Temp
(NAME1)
SELECT COUNT(*) AS regular
WHERE experience = 1 AND bl = 1
INSERT INTO #Temp
(NAME2)
SELECT COUNT(*) AS regular
WHERE experience = 1 AND bl = 0
INSERT INTO #Temp
(NAME3)
SELECT COUNT(*) AS regular
WHERE experience = 0 AND bl = 0
INSERT INTO #Temp
(NAME4)
SELECT COUNT(*) AS regular
WHERE experience = 0 AND bl = 1*
SELECT * FROM #Temp

SQL Implementing 'IN' operator with 'OR'

I am working on a legacy system which has a custom java implementation for generating SQL queries. That doesn't support 'IN' operation.
To implement 'IN' I have written something like
SELECT * from Q
WHERE IS_HIDDEN = 0 AND ID = 1
OR ID = 2 OR ID = 3 AND IS_DELETED = 0;
I know that the one like below would have been fine.
SELECT * from Q
WHERE IS_HIDDEN = 0 AND (ID = 1
OR ID = 2 OR ID = 3) AND IS_DELETED = 0 ;
Both these return the same result but I'm not too confident about SQL operator priorities. I had read that AND takes precedence
Is it safe to assume that both the SQL statemets are equivalent.
The actual query that I wanted to write is
SELECT * from Q
WHERE IS_HIDDEN = 0 AND ID IN(1, 2, 3) AND IS_DELETED = 0;
The DB in question is oracle 10g.
Update: The reason that this was working is because the oracle CBO rearranges the subclauses in the where clause.
No your queries are not the same
SELECT * from Q
WHERE IS_HIDDEN = 0 AND ID = 1
OR ID = 2 OR ID = 3 AND IS_DELETED = 0;
is like
SELECT * FROM Q WHERE IS_HIDDEN = 0 AND ID = 1
UNION
SELECT * FROM Q WHERE ID = 2
UNION
SELECT * FROM Q WHERE ID = 3 AND IS_DELETED = 0
when you use the parentheses for your ORs then you have the same like the IN-Clause
You can try it: SQLFiddle
You first query is equal to the IN. You should use that:
Your second query is like this:
SELECT * from Q
WHERE (IS_HIDDEN = 0 AND ID = 1) OR ID = 2 OR (ID = 3 AND IS_DELETED = 0);
If IS_HIDDEN is 1 or DELETED Is 1, but ID is 2, your query will still give you records. Try it..

Sql query confusion

I'm stumped on a sql query.
In a query, I have a result set like so:
FooId Name Value SourceLevel SourceId RecordId
-----------------------------------------------------------------
1 'Foo' 10 1 0 1
1 'Foo' 25 3 1 2
2 'Bar' 33 1 0 3
To that query, I pass parameters #Level1Level, #Level2Level, #Level3Level, and #Level1Id, #Level2Id, #Level3Id
(no, these aren't real names, but they illustrate my point).
My query is trying to do a filter like this:
WHERE ((SourceLevel = #Level1Level AND SourceId = #Level1Id)
OR (SourceLevel = #Level2Level AND SourceId = #Level2Id)
OR (SourceLevel = #Level3Level AND SourceId = #Level3Id))
If I pass in parameters like so:
#Level1Level = 1, #Level2Level = 2, #Level3Level = 3
#Level1Id = 0, #Level2Id = 3, #Level3Id = 2
I would want recordIds 1 and 3 back.
But, if I pass in parameters like so:
#Level1Level = 1, #Level2Level = 2, #Level3Level = 3
#Level1Id = 0, #Level2Id = 3, #Level3Id = 1
I would want recordIds 2 and 3 back. Unfortunately, in the second case, I'm getting all 3 records back, which makes sense, because of the OR in my where clause. I can't figure out how to limit my result set to say "only choose SourceLevel 1 if I haven't already matched on SourceLevel 2 or 3".
Anyone have any thoughts, assuming this makes any sense?
To clarify: I want each FooId from my result set, but only the most specific FooId available, based on the SourceLevel parameters passed in.
Here's what I ended up doing:
WHERE
(
(SourceLevel = #Level3Level AND SourceId = #Level3Id)
OR
(
SourceLevel = #Level2Level AND SourceId = #Level2Id
AND NOT EXISTS (SELECT 'X' FROM SourceTable WHERE SourceLevel = #Level3Level And SourceId = #Level3Id AND FooId = SourceTable.FooId)
)
OR
(
SourceLevel = #Level1Level AND SourceId = #Level1Id
AND NOT EXISTS (SELECT 'X' FROM SourceTable WHERE SourceLevel = #Level3Level And SourceId = #Level3Id AND FooId = SourceTable.FooId)
AND NOT EXISTS (SELECT 'X' FROM SourceTable WHERE SourceLevel = #Level2Level And SourceId = #Level2Id AND FooId = SourceTable.FooId)
)
)
This seems to do the filtering I was after...sorry that the question was so confusing. :)
(SourceLevel = 1 AND SourceId = 0) match 1 and 3 records
(SourceLevel = 2 AND SourceId = 3) no matches
(SourceLevel = 3 AND SourceId = 1) match 2 record
All three records will be returned by the query.
I don't see exactly the point and why it's a sql question. But if you want SourceLevel 1 (i call it SL1 now) only if you haven't yet SL2 or SL3...
why don't you try with this:
SL2 OR SL3 OR (NOT SL2 AND NOT SL3 AND SL1)