I am looking for a method where I need to have conditions on my WHERE clause change dynamically based on the data.
Please find the mock data for my scenario.
-----------------------------------
ConditionID ConditionName
-----------------------------------
1 N/A
2 Over
3 Under
--------------------------------------------------------------
ID Amount ThresholdAmount ConditionID
--------------------------------------------------------------
1 90 100 3
2 190 100 2
3 90 100 2
4 190 100 3
5 90 100 1
I need to compare the [Amount] against the [ThresholdAmount] based on the [ConditionID]. For example if the condition is 'Over' then the [Amount] should be over the [ThresholdAmount] and if not satisfied then the record should be filtered out.
NOTE: I am just looking if there is any better approach than mine.
My Approach:
WHERE
1 = CASE
WHEN ConditionName = 'OVER' THEN
CASE WHEN ([Amount] >= [ThresholdAmount]) THEN 1 ELSE 0 END
WHEN ConditionName = 'UNDER' THEN
CASE WHEN ([Amount] <= [ThresholdAmount]) THEN 1 ELSE 0 END
WHEN ConditionName = 'N/A' THEN
1
END
Thanks,
Prakazz
I think you are using the word "Dynamically" wrong here, what you mean is not really dynamic. Search for dynamic sql to get a feel for the difference.
It's usually best to avoid case expressions in the where clause. Your where clause can be rewritten as:
WHERE (ConditionName = 'OVER' AND [Amount] >= [ThresholdAmount])
OR (ConditionName = 'UNDER' AND [Amount] <= [ThresholdAmount])
OR ConditionName = 'N/A'
By the way, you mention "if the condition is 'Over' then the [Amount] should be over the [ThresholdAmount]", but in your code you check whether Amount is 'over' or equal to ThresholdAmount. I followed what your code does.
I also extended your assumption that the database is case-insensitive.
Related
I've been searching far and wide (pun intended) but haven't found a solution yet.
So I got a table with 19 columns.
pokemon name
against_bug
against_dark
against_dragon
against_electric
against_fairy
against_fight
against_fire
against_flying
against_ghost
against_grass
against_ground
against_ice
against_normal
against_poison
against_psychic
against_rock
against_steel
against_water
Each of the "against_xxx" columns can have a value of 0,5, 1, 2 or 4.
I want each row to count how many of these columns have a value of 2 or higher in order to determine which pokemon has the most vulnerabilities.
I have no idea how to approach this.
Please look at Stu's suggestion in the comments on your question. Normalizing your table would be a great help.
Now you need to do something like this:
SELECT
pokemon,
CASE WHEN against_bug >= 2 THEN 1 ELSE 0 END +
CASE WHEN against_dark >= 2 THEN 1 ELSE 0 END +
CASE WHEN against_dragon >= 2 THEN 1 ELSE 0 END +
CASE WHEN against_electric >= 2 THEN 1 ELSE 0 END +
--[....repeat this for all your columns....]
FROM your_table
A normalized table would look like this:
pokemon
against_type
against_value
Pickachu
against_bug
1
Pickachu
against_dark
2
Pickachu
against_dragon
0.5
Pickachu
against_electric
1
Pickachu
(etc)
(etc)
Blastoid
against_bug
1
Blastoid
against_dark
2
Blastoid
against_dragon
2
Blastoid
against_electric
4
Blastoid
(etc)
(etc)
In this case you could write a much simpler query:
SELECT
pokemon,
count(*) AS number_of_vulnerabilities
FROM your_table
WHERE against_value >= 2
GROUP BY pokemon
Long story short, I have a Crosswalk table that has a column name MgrFilterRacf. There is a single line in the table I use to reference various things across a few queries, this method has worked fine for me. However, when I had to reference more than one value it gives me the error below. I am really at a loss as to why it is not working. I see a TON of topics/posts on this, but most are solutions stating to use the IN operator, which I already am. The mildly infuriating thing is that it works when I only call one value and delete the second line on the crosswalk table regardless of "In" or "=" being used.
Error for reference:
Msg 512, Level 16, State 1, Line 2
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
Crosswalk table format:
Id | AttCeilingScore | AttFloorScore | MgrFilterRacf
----+-----------------+----------------+---------------
1 | 100 | 75 | Value1
2 | NULL | NULL | Value2
Query:
--\\*Perform calculation For Attendance Score Sa*\\
Select
MgrRacf,
MgrName,
Ctc.racf as EmpRacf,
Ctc.EmpName,
Case
when AttCntSegment is null then 0
else AttCntSegment
end as AttCntSegment,
Case
when AttSumDuration is null then 0
else AttSumDuration
end as AttSumDuration,
case
when AttCntSegment > 12
then (Select AttFloorScore from tblAvs1Scoring)
when AttCntSegment is null
then (Select AttCeilingScore from tblAvs1Scoring)
when 100 - ((AttCntSegment) * 2 + PercentReduction) < (Select AttFloorScore from tblAvs1Scoring)
then (Select AttFloorScore from tblAvs1Scoring)
else 100 - ((AttCntSegment)*2+PercentReduction)
end As AttScore,
Case
when AttCntSegment is null then 100
else 100 - ((AttCntSegment)*2+PercentReduction)
end as AttScoreRaw,
'RollSixSum' as ReportTag
From
(--\\*Get Total Occurrences from Rolling 6 months per advocate*\\
SELECT
EMP_ID,
COUNT(SEG_CODE) AS AttCntSegment,
SUM(DURATION) AS AttSumDuration
FROM
tblAttendance AS Att
WHERE
(START_DATE >= Getdate() - 180)
AND (SEG_CODE NOT IN ('FLEX2', 'FMLA'))
AND (DURATION > 7)
AND START_DATE IS NOT NULL
GROUP BY
EMP_ID) As Totals
INNER JOIN
tblCrosswalkAttendanceTime AS Time ON AttSumDuration BETWEEN Time.BegTotalTime AND Time.EndTotalTime
RIGHT JOIN
tblContactListFull AS Ctc ON Ctc.employeeID = Totals.EMP_ID
WHERE
--Ctc.Mgr2racf IN ('Value1','Value2') --This works
Ctc.Mgr2racf IN (SELECT MgrFilterRacf FROM tblAvs1Scoring) --This returns the same 2 values but doesn't work, note works with only 1 value present
AND (title LIKE '%IV%' OR Title LIKE '%III%' OR title LIKE '%Cust Relat%') --Going to apply same logic here once I have a solution
AND employeestatus2 <> 'Inactive'
Specific offending lines of code:
Ctc.Mgr2racf IN (Select MgrFilterRacf from tblAvs1Scoring) --This returns the same 2 values but doesnt work, note works with only 1 value present
AND (title LIKE '%IV%' or Title like '%III%' or title LIKE '%Cust Relat%') --Going to apply same logic here once I have a solution
It's not the IN clause that’s causing the error, since IN doesn't have to return only 1 value. Instead it's likely here:
CASE ... < (Select AttFloorScore from tblAvs1Scoring)
Try select count(AttFloorScore) from tblAvs1Scoring and see if it's > 1. I am sure it is.
This can be mitigated by what ever method is appropriate for your data.
Using TOP 1
Using an aggregate like MAX()
Correlating the sub-query to limit the rows returned.
Using the appropriate WHERE clause
The error was not where I thought it was, it was at the top of the query in those sub queries. Even though there as no values present, it still needs to be told since I added a line to return the max value. Once I updated that all was well and worked.
Bottom line, check all your sub queries.
I have data which contains 1000+ lines and in this it contains errors people make. I have added a extra column and would like to find all duplicate Rev Names and give the first one a 1 and all remaining duplicates a 0. When there is no duplicate, it should be a 1. The outcome should look like this:
RevName ErrorCount Duplicate
Rev5588 23 1
Rev5588 67 0
Rev5588 7 0
Rev5588 45 0
Rev7895 6 1
Rev9065 4 1
Rev5588 1 1
I have tried CASE WHEN but its not giving the first one a 1, its giving them all zero's.
Thanks guys, I am pulling out my hair here trying to get this done.
You could use a case expression over the row_number window function:
SELECT RevName,
Duplicate,
CASE ROW_NUMER() OVER (PARTITION BY RevName
ORDER BY (SELECT 1)) WHEN 1 THEN 1 ELSE 0 END AS Duplicate
FROM mytable
SQL tables represent unordered sets. There is no "first" of anything, unless a column specifies the ordering.
Your logic suggests lag():
select t.*,
(case when lag(revname) over (order by ??) = revname then 0
else 1
end) as is_duplicate
from t;
The ?? is for the column that specifies the ordering.
im not sure about this question is already asked by anyone else or not yet because this is actually easy but my head is just still can't see the way out of this problem.
this is just like how many times that we do sampling at the material.
SELECT
TABLE01.MATERIAL_NO,
TABLE01.Sample_Tempt1,
TABLE01.Sample_Tempt2,
TABLE01.Sample_Tempt3,
TABLE01.Sample_Tempt4,
TABLE01.Sample_Tempt5
FROM
TABLE01
is it possible to create another column to show count of sample_tempt times?
i mean, if the tempt1 tempt2 data are exist, the column shows 2, when tempt2, tempt4 and tempt5 data are exist, the column show 3. and so on.
Thank you for helping me ^^
Sample :
Material no | Sample_Tempt1 | Sample_Tempt2 | Sample_Tempt3 | Sample_Tempt4 | Sample_Tempt5 |
PO1025 120 150 102
PO1026 122
For the PO1025, i want to create new column that generate "3" because the sample data that exist is only 3, for the PO1026 i want it generate "1" since the sample data that exist is only "1". quite simple right?
If "by exist" you mean "value is not NULL", then you can count the number of non-NULL values in each row as:
SELECT t1.MATERIAL_NO,
t1.Sample_Tempt1, t1.Sample_Tempt2, t1.Sample_Tempt3, t1.Sample_Tempt4, t1.Sample_Tempt5,
((case when t1.sample_temp1 is not null then 1 else 0 end) +
(case when t1.sample_temp2 is not null then 1 else 0 end) +
(case when t1.sample_temp3 is not null then 1 else 0 end) +
(case when t1.sample_temp4 is not null then 1 else 0 end) +
(case when t1.sample_temp5 is not null then 1 else 0 end)
) as NumTempts
FROM TABLE01 t1;
Note that I introduced a table alias. This makes the query easier to write and to read.
Hopefully you guy's can help.
I am writing a query from a table which has variable data specifically a completed column which can have a value of 1 or 3 the table also has two qty_ fields QTYORDERED and QTYSHIPPED
If the value is 3 in COMPLETED then QTYSHIPPED will contain a value and if the value is 1 the QTYORDERED will have a value.
What I need to do is within my query create one column which just has a QTY can someone show me how to achieve this in SQL Server
This is a simple case statement:
select (case when completed = 1 then QTYORDERED
when completed = 3 then QTYSHIPPED
end) as QTY
Note this will return NULL when completed has any other value.
You can also write this as:
select (case completed when 1 then QTYORDERED
when 3 then QTYSHIPPED
end)
However, the first form is more general, giving you more flexibility for complicated logic.
SELECT
CASE completed
WHEN 1 THEN QTYORDERED
WHEN 3 THEN QTYSHIPPED
ELSE 0 --Or add any logic when something goes wrong with "completed" value
END as Quantity
FROM ....