Select rows where x of y columns meet criteria - sql-server-2000

Working with a table that is perhaps not terribly normalized1, like so:
ID S1 S2 S3 S4 S5 S6 S7 S8 S9
101 3 2 1 3 4 3 5 3 3
102 4 3 4 3 2 3 2 3 2
103 3 2 3 4 3 2 3 3 4
(The S# columns are scores, basically.) I need to return rows where one of two conditions are met:
Any score is 1. (This part's easy, albeit a bit tedious.)
Any three or more scores are 2. (This part's not.)
(So in the sample data, row 1 meets the first criterion, and row 2 meets the second criterion. Row 3 doesn't meet either.)
The problem is that I need to shoehorn this into an existing mechanism, which means no SUM(CASE WHEN S1 = '2' OR S2 = '2'... THEN 1 ELSE 0 END), because there's no aggregation/grouping happening: this needs to be a straight condition that I can add to an existing WHERE clause.
The other problem is that this is on SQL Server 2000, so most of the fancy-shmancy additions that are available in 2008+ are not an option.
Is there any way to do this?
1 That's called an understatement, folks. :)

One way would be
WHERE 3 <=
CASE WHEN S1 = 2 THEN 1 ELSE 0 END +
CASE WHEN S2 = 2 THEN 1 ELSE 0 END +
CASE WHEN S3 = 2 THEN 1 ELSE 0 END +
CASE WHEN S4 = 2 THEN 1 ELSE 0 END +
CASE WHEN S5 = 2 THEN 1 ELSE 0 END +
CASE WHEN S6 = 2 THEN 1 ELSE 0 END +
CASE WHEN S7 = 2 THEN 1 ELSE 0 END +
CASE WHEN S8 = 2 THEN 1 ELSE 0 END +
CASE WHEN S9 = 2 THEN 1 ELSE 0 END
Or (untested on SQL Server 2000)
WHERE 3 <= (SELECT COUNT(*)
FROM (SELECT S1
UNION ALL
SELECT S2
UNION ALL
SELECT S3
UNION ALL
SELECT S4
UNION ALL
SELECT S5
UNION ALL
SELECT S6
UNION ALL
SELECT S7
UNION ALL
SELECT S8
UNION ALL
SELECT S9)D(S)
WHERE S = 2)

Related

How to return matching column value where only all subqueries are true

I'd like to adjust the below SQL statement so that it only returns the PART_ID where all SELECT PART_ID FROM FABRICATION... conditions are met.
Currently the below statement would return part_id's 1 & 2, when the intent is to only return 1.
Thanks for any advice you can offer.
SELECT *
FROM Part
WHERE Section = 'C6x1_5/8x1/4'
AND Length BETWEEN 41.99 AND 42.01
AND Part.Part_ID IN (SELECT Part_ID FROM Fabrication
WHERE Type LIKE 'HOLE'
AND Face = 1
AND Side = 3
AND Location BETWEEN 2.99 AND 3.01
AND Offset = 2
AND Parameter_1 = 0.5625)
AND Part.Part_ID IN (SELECT Part_ID FROM Fabrication
WHERE Type LIKE 'HOLE'
AND Face = 1
AND Side = 3
AND Location BETWEEN 5.99 AND 6.01
AND Offset = 2
AND Parameter_1 = 0.5625)
Fabrication Table:
PART_ID
TYPE
FACE
SIDE
LOCATION
OFFSET
PARAMETER_1
PARAMETER_2
PARAMETER_3
1
HOLE
1
3
6
2
0.5625
0
0
1
HOLE
1
3
3
2
0.5625
0
0
2
HOLE
1
3
6
2
0.5625
0
0
2
HOLE
1
3
3
2
0.5625
0
0
2
HOLE
1
3
9
2
0.5625
0
0
3
HOLE
1
3
3
2
0.5625
0
0
You can use aggregation and HAVING to validate that there there is exactly one match for each condition and no others:
SELECT *
FROM Part
WHERE Section = 'C6x1_5/8x1/4' AND
Length BETWEEN 41.99 AND 42.01
Part.Part_ID IN (SELECT f.Part_ID
FROM Fabrication f
GROUP BY f.Part_ID
HAVING SUM(CASE WHEN f.Type = 'HOLE' AND f.Face = 1 AND f.Side = 3 AND f.Offset = 2 AND f.Parameter_1 = 0.5625 THEN 1 ELSE 0 END) = COUNT(*) AND
SUM(CASE WHEN Location BETWEEN 2.99 AND 3.01 THEN 1 ELSE 0 END) = 1 AND
SUM(CASE WHEN Location BETWEEN 5.99 AND 6.01 THEN 1 ELSE 0 END) = 1
);
It looks like the only variance in your two queries is Location, for which you want to ensure the number of Fabrication rows meet your criteria and do not have any additional rows outside of your criteria.
One option you can try uses conditional aggregation:
select *
from Part
where Section = 'C6x1_5/8x1/4'
and Length between 41.99 and 42.01
and Part.Part_ID in (
select PART_ID
from Fabrication
where
TYPE = 'HOLE'
and FACE = 1
and SIDE = 3
and offset = 2
and PARAMETER_1 = 0.5625
group by PART_ID
having Count(*)=Sum(case when LOCATION between 2.99 and 3.01 then 1 end) + Sum(case when LOCATION between 5.99 and 6.01 then 1 end)
)

How to merge grouped records into one row in MS SQL

How can I merge grouped records (byt SurvId) into one single row where Column Mode is part of the column name?
This
ID SurvId Mode A B
---------------------------------------
1 1 Unrestricted 1 3
2 1 Restricted 5 2
3 2 Unrestricted 6 4
4 2 Restricted 4 1
Into this
SurvId UnrestictedA UnrestrictedB RestrictedA RestrictedB
------------------------------------------------------------
1 1 3 5 2
2 6 4 4 1
You can use conditional aggregation:
select survid,
max(case when mode = 'Unrestricted' then A end) as unrestricted_a,
max(case when mode = 'Restricted' then A end) as restricted_a,
max(case when mode = 'Unrestricted' then B end) as unrestricted_b,
max(case when mode = 'Restricted' then B end) as restricted_b
from t
group by survid;

Facing problem with case when statement in a single row fetch for oracle sql

I have a single row of data as follows:
dyn1 dyn2 dyn3 chg
1 0 1 768
Now i want to write a case condition like
Case when dyn1 = 1 then 7 When dyn2=1 then 7 When dyn3=1 then 7 End
Now for this above records it is not checking the dyn3 value as it is getting dyn1 value as true.
How to handle this code?
I'll suppose that you need to return those values in a select statement, maybe this could be helpful to you.
SELECT CASE WHEN dyn1 = 1 THEN 7 END,
CASE WHEN dyn2 = 1 THEN 7 END,
CASE WHEN dyn3 = 1 THEN 7 END
FROM (SELECT 1 AS dyn1, 1 AS dyn2, 1 AS dyn3 FROM dual);
Or, maybe you want it in an only column
SELECT CASE WHEN dyn1 = 1 THEN 7 END ||
CASE WHEN dyn2 = 1 THEN 7 END ||
CASE WHEN dyn3 = 1 THEN 7 END
from (select 1 as dyn1, 1 as dyn2, 1 as dyn3 from dual);

Best way to by column and aggregation on another column

I want to create a rank column using existing rank and binary columns. Suppose for example a table with ID, RISK, CONTACT, DATE. The existing rank is RISK, say 1,2,3,NULL, with 3 being the highest. The binary-valued is CONTACT with 0,1 or FAILURE/SUCESS. I want to create a new RANK that will order by RISK once a certain number of successful contacts has been exceeded.
For example, suppose the constraint is a minimum of 2 successful contacts. Then the rank should be created as follows in the two instances below:
Instance 1. Three ID, all have a min of two successful contacts. In that case the rank mirrors the risk:
ID risk contact date rank
1 3 S 1 3
1 3 S 2 3
1 3 F 3 3
1 3 F 4 3
2 2 S 1 2
2 2 S 2 2
2 2 F 3 2
2 2 F 4 2
3 1 S 1 1
3 1 S 2 1
3 1 S 3 1
Instance 2. Suppose ID=1 has only one successful contact. In that case it is relegated to the lowest rank, rank=1, while ID=2 gets the highest value, rank=3, and ID=3 maps to rank=2 because it satisfies the constraint but has a lower risk value than ID=2:
ID risk contact date rank
1 3 S 1 1
1 3 F 2 1
1 3 F 3 1
1 3 F 4 1
2 2 S 1 3
2 2 S 2 3
2 2 F 3 3
2 2 F 4 3
3 1 S 1 2
3 1 S 2 2
3 1 S 3 2
This is SQL, specifically Hive. Thanks in advance.
Edit - I think Gordon Linoff's code does it correctly. In the end, I used three interim tables. The code looks like that:
First,
--numerize risk, contact
select A.* ,
case when A.risk = 'H' then 3
when A.risk = 'M' then 2
when A.risk = 'L' then 1
when A.risk is NULL then NULL
when A.risk = 'NULL' then NULL
else -999 end as RISK_RANK,
case when A.contact = 'Successful' then 1
else NULL end as success
Second,
-- sum_successes_by_risk
select A.* ,
B.sum_successes_by_risk
from T as A
inner join
(select A.person, A.program, A.risk, sum(a.success) as sum_successes_by_risk
from T as A
group by A.person, A.program, A.risk
) as B
on A.program = B.program
and A.person = B.person
and A.risk = B.risk
Third,
--Create table that contains only max risk category
select A.* ,
B.max_risk_rank
from T as A
inner join
(select A.person, max(A.risk_rank) as max_risk_rank
from T as A
group by A.person
) as B
on A.person = B.person
and A.risk_rank = B.max_risk_rank
This is hard to follow, but I think you just want window functions:
select t.*,
(case when sum(case when contact = 'S' then 1 else 0 end) over (partition by id) >= 2
then risk
else 1
end) as new_risk
from t;

T/SQL - Trying to convert col to rows

DDL/DML http://www.sqlfiddle.com/#!6/2d69c/1
Desired Output:
Country_ State_ FamilyTag ChildTag Name HouseHold IsReachable Rank_
US CA Family A Child A Ch A1 1 1 1
US CA Family B Child A Ch A1 1 1 2
US CA Family C Child A Ch A1 1 1 3
US CA Family B Child B Ch B1 3 1 1
US CA Family A Child B Ch B1 3 1 2
US CA Family C Child B Ch B1 3 1 3
US CA Family C Child C Ch C1 2 1 1
US CA Family A Child C Ch C1 2 1 3
US CA Family B Child C Ch C1 2 1 2
"Child[n]Tag" field data should go in "ChildTag" field.
"Child[n]Name" field data should go in "Name" field.
"ChildFamilyTag[n]" field data should go in "FamilyTag" field.
"ChildFamilyRank[n]" field data should go in "Rank_" field.
"Child[n]HouseHold" field data should go in "Household" field.
"Child[n]IsReachable" field data should go in "IsReachable" field.
Getting NULLs instead of above desired output...please help
Note:
# of "ChildFamily%" columns can vary (right now, it has 3 sets...but could be 2,3, and etc.
Also, my example shows only one record, there could be more. :)
Thank you
p.s. if you need me to post code here, kindly let me know.
Your query is pretty much along the way there. But you are missing conditions in the case statements. Adding new conditions seems pointless, when some arithmetic on N solves the problem:
SELECT *
FROM (SELECT Country_
, State_
, (CASE WHEN N = 1 THEN 1
WHEN N = 2 THEN 2
WHEN N = 3 THEN 3
WHEN N = 4 THEN 4
WHEN N = 5 THEN 5
WHEN N = 6 THEN 6
WHEN N = 7 THEN 7
WHEN N = 8 THEN 8
WHEN N = 9 THEN 9 END) AS ParentNo
, (CASE WHEN N = 1 THEN [Child1FamilyTag1]
WHEN N = 2 THEN [Child1FamilyTag2]
WHEN N = 3 THEN [Child1FamilyTag3]
WHEN N = 4 THEN [Child2FamilyTag1]
WHEN N = 5 THEN [Child2FamilyTag2]
WHEN N = 6 THEN [Child2FamilyTag3]
WHEN N = 7 THEN [Child3FamilyTag1]
WHEN N = 8 THEN [Child3FamilyTag2]
WHEN N = 9 THEN [Child3FamilyTag3] END) AS FamilyTag
, (CASE WHEN (N + 2) / 3 = 1 THEN [Child1Tag]
WHEN (N + 2) / 3 = 2 THEN [Child2Tag]
WHEN (N + 2) / 3 = 3 THEN [Child3Tag] END) AS ChildTag
, (CASE WHEN (N + 2) / 3 = 1 THEN [Child1Name]
WHEN (N + 2) / 3 = 2 THEN [Child2Name]
WHEN (N + 2) / 3 = 3 THEN [Child3Name] END) AS Name
, (CASE WHEN (N + 2) / 3 = 1 THEN [Child1HouseHold]
WHEN (N + 2) / 3 = 2 THEN [Child2HouseHold]
WHEN (N + 2) / 3 = 3 THEN [Child3HouseHold] END) AS HouseHold
, (CASE WHEN (N + 2) / 3 = 1 THEN [Child1IsReachable]
WHEN (N + 2) / 3 = 2 THEN [Child2IsReachable]
WHEN (N + 2) / 3 = 3 THEN [Child3IsReachable] END) AS IsReachable
, (CASE WHEN (N % 3) = 1 THEN [Child1FamilyRankA]
WHEN (N % 3) = 2 THEN [Child1FamilyRankB]
WHEN (N % 3) = 0 THEN [Child1FamilyRankC]
END) AS Rank
FROM Temp TI CROSS JOIN
(SELECT 1 AS N
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 7
UNION ALL
SELECT 8
UNION ALL
SELECT 9) N
) T;
Here is the SQL Fiddle.
EDIT (in response to comment):
Your original query is
, (CASE WHEN N = 1 THEN [Child1FamilyRankA]
WHEN N = 2 THEN [Child1FamilyRankB]
WHEN N = 3 THEN [Child1FamilyRankC]
WHEN N = 4 THEN [Child2FamilyRankA]
WHEN N = 2 THEN [Child2FamilyRankB]
WHEN N = 3 THEN [Child2FamilyRankC]
WHEN N = 7 THEN [Child3FamilyRankA]
WHEN N = 2 THEN [Child3FamilyRankB]
WHEN N = 3 THEN [Child3FamilyRankC] END) AS Rank_
I don't know why the values are arranged as they are for this rank_, but you can keep using them. I think I replaced them with something else.