SELECT if any row within ID contains specific value - sql

I have a DB with IDs and their classification (and much more columns that are useless for now),
Due to differences in unused columns, one ID may have different classifications like:
ID Classification
1001 A
1001 A
1002 A
1002 A
1002 B
What I need is to group IDs and put classification by the rule "If any of the lines within this ID is 'B'-classified, then the group (single row with this ID) is 'B'-classified, else - 'A'-classified.
So that ID 1001 = A, but ID 1002 = B.
I am aware of WHERE tab.field = ANY() function, but I have reciprocal situation - left part of comparison should be ANY, while right part should be hardcoded. I kind of assume, that as comparison result is boolean, than place of left\right parts is irrelevant, but I cannot figure out query-subquery relations.
Please help

You can use the count window function to do this.
select distinct id
,case when count(case when classification='B' then 1 end) over(partition by id) >=1 then 'B' else 'A' end as classified
from t

For the special case where the classification of interest is also the letter with the last alphabetical position, you can simply do:
SELECT
ID,
MAX(Classification)
FROM tab
GROUP BY ID;

You can simply find max of classification for each id:
select id,
max(classification)
from your_table
group by id;

Related

Filter with SQL Server by Group ID

I have two tables and I need to filter the data by filter id depends on the relation to to filter group id.
For example I have this two tables:
Table 1:
ItemID
FilterID
3
122
3
123
3
4
17
123
Table 2:
FilterID
FilterGroupID
122
5
123
5
4
1
If I search by filter id = 123 than all item id with this filter need to be returned.
If I search two or more different filter id that have different group id I need to get only the item ids that have all filter id and group id.
Desired output:
first input: 123 -> return item id =3 and item id = 17
second input: 123,4 -> return item id = 3 because filter id 123 belong to group id 5 and filter id 4 belong to group id 1 and item id 3 is the only one that has this two filters.
third input: 122,123 -> return item id =3 and item id = 17 because both filter id belong to same group.
I am getting a little lost with this query and I will be glad to get some help.
I’ll try to simplify it: Let’s say we have group filter of size and group filter of color. If I filter by size S or M than I need to get all items with this sizes. If I want to add color like blue than the answer will cut the result by: item with size S or M and Color blue. So filter from different group may cut some results
It seems that you want to get every ItemID which has at least one matching filter from each FilterGroupID within your filter input. So within each group you have or logic, and between groups you have and logic
If you store your input in a table variable or Table-Valued parameter, then you can just use normal relational division techniques.
This then becomes a question of Relational Division With Remainder, with multiple divisors.
There are many ways to slice this cake. Here is one option
Join the filter input to the groups, to get each filter's group ID
Use a combination of DENSE_RANK and MAX to get the total distinct groups (you can't use COUNT(DISTINCT in a window function so we need to hack it)
You can change this step to use a subquery instead of window functions. It may be faster or slower
Join the main table, and filter out any ItemIDs which do not have their total distinct groups the same as the main total
SELECT
t1.ItemID
FROM (
SELECT *,
TotalGroups = MAX(dr) OVER ()
FROM (
SELECT
fi.FilterID,
t2.FilterGroupID,
dr = DENSE_RANK() OVER (ORDER BY t2.FilterGroupID)
FROM #Filters fi
JOIN Table2 t2 ON t2.FilterID = fI.FilterID
) fi
) fi
JOIN Table1 t1 ON t1.FilterID = fi.FilterID
GROUP BY
t1.ItemID
HAVING COUNT(DISTINCT FilterGroupID) = MAX(fi.TotalGroups);
db<>fiddle

Select Rows and Flag Column if value found

i need help for creating a query
Current situation:
The output table contains Ids and levels. Each Id can appear several times.
Problem:
Now I want to know if level 1 appears for an id, if so I want to mark it as number 1. If an Id has only level 2 or zero just mark it as number 0.
The output can be taken from the table below.
Use aggregation:
select id,
max(case when level = 1 then 1 else 0 end) as flag
from t
group by id;

Oracle SQL - Multiple return from case

I may be trying it wrong. I am looking for any approach which is best.
Requirement:
My Query joins 4-5 tables based on few fields.
I have a column called product id. In my table there are 1.5 million rows. Out of those only 10% rows has product ids with the following attribute
A300X-%
A500Y-%
300,500, 700 are valid model numbers. X and Y are classifications. My query picks all the systems.
I have a check as follows
CASE
WHEN PID LIKE 'A300X%'
THEN 'A300'
...
END AS MODEL
Similarly
CASE
WHEN PID LIKE 'A300X%'
THEN 'X'
...
END AS GENRE
I am looking for the best option from the below
How do I Combine both case statement and add another[third] case which will have these two cases. i.e
CASE
WHEN desc in ('AAA')
First Case
Second Case
ELSE
don't do anything for other systems
END
Is there any regex way of doing this? Before first - take the string. Look for X, Y and also 300,500,700.
Is there any other way of doing this? Or doing via code is the best way?
Any suggestions?
EDIT:
Sample desc:
AAA,
SoftwARE,
sw-app
My query picks all the desc. But the case should be running for AAA alone.
And Valid models are
A300X-2x-P
A500Y-5x-p
A700X-2x-p
A50CE-2x-P
I have to consider only 300,500,700. And the above two cases.
Expected result:
MODEL GENRE
A300 X
A500 Y
A300 Y
Q: How do I Combine both CASE statement expressions
Each CASE expression will return a single value. If the requirement is to return two separate columns in the resultset, that will require two separate expressions in the SELECT list.
For example:
DESC PID model_number genre
---- ---------- ------------ ------
AAA A300X-2x-P 300 X
AAA A500Y-5x-p 500 Y
AAA A700X-2x-p 700 X
AAA A50CE-2x-P (NULL) (NULL)
FOO A300X-2x-P (NULL) (NULL)
There will need to be an expression to return the model_number column, and a separate expression to return the genre column.
It's not possible for a single expression to return two separate columns.
Q: and add another[third] case which will have these two cases.
A CASE expression returns a value; we can use a CASE expression almost anywhere in a SQL statement where we can use a value, including within another CASE expression.
We can also combine multiple conditions in a WHEN test with AND and OR
As an example of combining conditions and nesting CASE expressions ditions...
CASE
WHEN ( ( t.PID LIKE '_300%' OR t.PID LIKE '_500%' OR t.PID LIKE '_700%' )
AND ( t.DESC = 'AAA' )
)
THEN CASE
WHEN ( t.PID LIKE '____X%' )
THEN 'X'
WHEN ( t.PID LIKE '____Y%' )
THEN 'Y'
ELSE NULL
END
ELSE NULL
END AS `genre`
There are other expressions that will return an equivalent result; the example shown here isn't necessarily the best expression. It just serves as a demonstration of combining conditions and nesting CASE expressions.
Note that to return another column model we would need to include another expression in the SELECT list. Similar conditions will need to be repeated; it's not possible to reference the WHEN conditions in another CASE expression.
Based on your sample data, logic such as this would work:
(CASE WHEN REGEXP_LIKE(PID, '^A[0-9]{3}[A-Z]-')
THEN SUBSTR(PID, 1, 4)
ELSE PID
END) AS MODEL
(CASE WHEN REGEXP_LIKE(PID, '^A[0-9]{3}[A-Z]-')
THEN SUBSTR(PID, 5, 1)
ELSE PID
END) AS GENRE
This assumes that the "model number" always starts with "A" and is followed by three digits (as in your example data). If the model number is more complicated, you may need regexp_substr() to extract the values you want.

Removing duplicate rows by selecting only those with minimum length value

I have a table with two string columns: Name and Code. Code is unique, but Name is not. Sample data:
Name Code
-------- ----
Jacket 15
Jeans 003
Jeans 26
I want to select unique rows with the smallest Code value, but not in terms of numeric value; rather, the length of the string. Of course this does not work:
SELECT Name, Min(Code) as Code
FROM Clothes
GROUP BY Name, Code
The above code will return one row for Jeans like such:
Jeans | 003
That is correct, because as a number, 003 is less than 26. But not in my application, which cares about the length of the value, not the actual value. A value with a length of three characters is greater than a value with two characters. I actually need it to return this:
Jeans | 26
Because the length of 26 is shorter than the length of 003.
So how do I write SQL code that will select row that has the code with the minimum length, not the actual minimum value? I tried doing this:
SELECT Name, Min(Len(Code)) as Code
FROM Clothes
GROUP BY Name, Code
The above returns me only a single character so I end up with this:
Jeans | 2
;WITH cte AS
(
SELECT Name, Code, rn = ROW_NUMBER()
OVER (PARTITION BY Name ORDER BY LEN(Code))
FROM dbo.Clothes
)
SELECT Name, Code
FROM cte
WHERE rn = 1;
SQLfiddle demo
If you have multiple values of code that share the same length, the choice will be arbitrary, so you can break the tie by adding an additional order by clause, e.g.
OVER (PARTITION BY Name ORDER BY LEN(Code), CONVERT(INT, Code) DESC)
SQLfiddle demo
Try this
select clothes.name, MIN(code)
from clothes
inner join
(
SELECT
Name, Min(Len(Code)) as CodeLen
FROM
clothes
GROUP BY
Name
) results
on clothes.name = results.name
and LEN(clothes.code) = results.CodeLen
group by clothes.name
It sounds like you are trying to sort on the numeric value of the Code field. If so, the correct approach would be to cast it to INT first, and use that for sorting/min functions (in a subquery), then select the original code in your main query clause.

Complex SQL query on one table

Have forgotten SQL queries as have not used it for a long time.
I have a following requirement.
Have a table called match where I keep my competitor details with respect to matches my team have played against them. So some important fields are like this
match_id
competior_id
match_winner_id
ismatchtied
goals_scored_my_team
goals_scored_comp
From this table I want to get the head to head information for all my competitors.
like this
Competitor Matches Wins Losses Draws
A 10 5 4 1
B 8 3 2 1
Draw information I can get from ismatchtied is set to 'Y' or 'N'.
I want to get all the info from one query. I can get all the info from executing queries separately and do complex logic processing in my server code. But my performance will take a hit.
Any help will be hugely appreciated.
cheers,
Saurav
You could use conditional aggregation, involving CASE expressions inside aggregate functions, like this:
SELECT
competitor_id,
COUNT(*) AS Matches,
COUNT(CASE WHEN goals_scored_my_team > goals_scored_comp THEN 1 END) AS Wins,
COUNT(CASE WHEN goals_scored_my_team < goals_scored_comp THEN 1 END) AS Losses,
COUNT(CASE WHEN goals_scored_my_team = goals_scored_comp THEN 1 END) AS Draws
FROM matches
GROUP BY
competitor_id
;
Every CASE above will evaluate to NULL when the condition isn't satisfied. And since COUNT(expr) omits NULLs, every COUNT(CASE ...) in the above query will effectively only count rows that match the corresponding WHEN condition.
So, the first COUNT counts only rows where my team scored more against the competitor, i.e. where my team won. In a similar way, the second and the third CASEs get the numbers of losses and draws.
SELECT m4.competior_id, COUNT(*) as TotalMathces,
(select count(*) from match m1 where goals_scored_my_team>goals_scored_comp AND m1.competior_id=m4.competior_id) as WINS,
(select count(*) as WIN from match m2 where goals_scored_comp>goals_scored_my_team AND m2.competior_id=m4.competior_id) as LOSES,
(select count(*) as WIN from match m3 where goals_scored_my_team=goals_scored_comp AND m3.competior_id=m4.competior_id) as DRAWS
FROM match m4 group by m4.competior_id;