Complicated SQL query request - sql

I have a table
Number Letter KeyLetter
1 a 1
1 b 0
1 c 0
1 d 0
2 e 0
2 f 0
2 g 0
3 h 1
3 i 1
3 j 0
From it I want this:
Number Letter KeyLetter
1 a 1
2 e 0
2 f 0
2 g 0
3 h 1
3 i 1
For each set of numbers, if a letter is a KeyLetter, I want to ignore any non KeyLetters.
If a set of numbers doesn't have an entry where the letter is a KeyLetter, then show all of the entries in that set of numbers.
What SQL query would be able to do this?

Simple answer, return the rows with KeyLetter = 1, and also those with a Number not having a KeyLetter = 1.
select *
from tablename t1
where t1.KeyLetter = 1
or not exists (select * from tablename t2
where t1.Number = t2.Number
and t2.KeyLetter = 1)
Alternatively:
select t1.*
from tablename t1
join (select Number, max(KeyLetter) maxKeyLetter
from tablename
group by Number) t2
on t1.Number = t2.Number and t1.KeyLetter = t2.maxKeyLetter
Or...
select *
from tablename
where (Number, KeyLetter) in
(select Number, max(KeyLetter)
from tablename
group by Number)
The first two are Core ANSI SQL compliant. The latter one uses extension F641, "Row and table constructors".

Related

Select table adding columns with data depending on duplicates in other column

Imagine this data.
Id
Type
1
A
1
B
1
B
2
A
3
B
I want to select table and ad two columns turning it to this. How can i do it? (In teradata)
Id
Type
Id with both A+B
Id with only A
1
A
1
0
1
B
1
0
1
B
1
0
2
A
0
1
3
B
0
0
I'm not familiar with teradata but in standard SQL next query should be working:
SELECT
T.*,
CASE WHEN Cnt = 2 THEN 1 ELSE 0 END AS BOTH_TYPES_PRESENT,
CASE WHEN Cnt = 1 AND Type = 'A' THEN 1 ELSE 0 END AS ONLY_A_PRESENT
FROM T
LEFT JOIN (
SELECT Id, COUNT(DISTINCT Type) Cnt FROM T WHERE Type IN ('A', 'B') GROUP BY Id
) CNT ON T.Id = CNT.Id;
SQL online editor

Delete all observations of a group after a variable changes

I would like to delete all observations from a group after a variable changes by keeping the first occurrence of this variable, regardless of the following values.
What I have
id A
a 0
a 0
a 0
b 0
b 0
b 1
c 1
c 1
c 1
d 1
d 0
d 0
d 0
What I want
id A
a 0
a 0
a 0
b 0
b 0
b 1
c 1
d 1
SQL tables represent unordered sets. So there is no first or next value unless a column specifies the ordering. Let me assume there is one.
One way to express this uses correlated subqueries:
select t.id, t.a
from (select t.*,
(select t2.A
from <table> t2
where t2.id = t.id
order by t2.<ordering column>
fetch first 1 row only
) as first_a
from <table> t
) t
where t.<ordering_column> < (select min(t3.ordering_column)
from table t3
where t3.a <> t.first_a
);

Can SQL interleave data based on a range limit?

Consider the following data
Table 1
Key Value
--- -----
A 1
B 2
C 3
D 4
E 5
F 6
G 7
H 8
I 9
J 10
Table 2
Q MaxValue
- --------
X 3
Y 6
Z 10
I'm trying to create a join that matches table 1 with table 2 when the values of table 1 are less than or equal to MaxValue in table 2, but only where they are greater than the prior MaxValue, like so
Result
Key Value Q MaxValue
--- ----- - --------
A 1 X 3
B 2 X 3
C 3 X 3
D 4 Y 6
E 5 Y 6
F 6 Y 6
G 7 Z 10
H 8 Z 10
I 9 Z 10
J 10 Z 10
Here you see that while A-F all meet the criteria of being less than or equal to 6 (Y's MaxValue), I want A-C to be matched only to X since they already match X's criteria of being less than 3, X being the 'prior max value'.
So can this be achieved in SQL?
This isn't very pretty but it should work for you:
select Z.[Key], Z.Value, T2.Q, Z.MaxValue from
(
select Y.[Key], Y.Value, Min(MaxValue) as MaxValue from
(
select T1.*, T2.MaxValue from Table1 T1 cross join Table2 T2
) Y
where Y.Value <= Y.MaxValue
group by Y.[Key], Y.Value
) Z
inner join Table2 T2 on Z.MaxValue = T2.MaxValue
select T1.Value, T2.MaxValue from Table1 T1 cross join Table2 T2 gets all possible combinations of Value and MaxValue.
Then I'm grouping it based on Value and Key and including the condition where Y.Value <= Y.MaxValue to exclude combinations where there are overlapping MaxValues for the same Value.
Finally, I'm getting the original Q column from Table2.
Hopefully help
select [Key],
[Value],
(SELECT [Q] FROM [TB2] T2 WHERE [MaxValue] IN (SELECT MIN(MaxValue) FROM [Table1] T2 WHERE T1.[Value]<= T2.[MaxValue])) AS Q,
(SELECT MIN(MaxValue) FROM [TB2] T2 WHERE T1.[Value]<= T2.[MaxValue]) AS MaxValue
FROM [Table2] T1

Clause ORDER BY

I have a problem with a select in sql server, i have this table with 2 columns:
a 2
b 1
c 100
d 1
a 100
b 1
c 2
d 1
I want ordered it based on the first column,in this way:
a 2
a 100
b 1
b 1
c 2
c 100
d 1
d 1
But then j want the rows with secondcolumn=100 be moved at the bottom,so:
a 2
b 1
b 1
c 2
d 1
d 1
a 100
c 100
I have tried with clause ORDER BY column1 ASC, (column2=100) ASC,but it didnt work!
Thankyou and greetings.
Actually, you want the rows with 100 in the second column moved to the bottom first, and then ordered by the first column:
order by (case when col2 = 100 then 1 else 0 end),
col1
Use the CASE expression as below
SELECT *
FROM tab
ORDER BY CASE
WHEN column2 = 100 THEN 1
ELSE 0
END ASC,
column1 asc
SELECT *
FROM table1
ORDER BY
CASE
WHEN col2>=100 THEN 1
ELSE 0
END,
col1,
col2
SQLFiddle Example

SQL server: Compare rows

I need a query which can do the following operation.
I have a table with 2 columns
ID Values
1 1
1 2
1 3
1 4
2 2
2 5
2 6
if you see for ID 1 I have 1,2,3 and 4 as values and for ID 2 I have 2, 5 and 6.
I want to write a query which return the following
1(-)
4(-)
5(+)
6(+)
mean 1 and 4 are deleted and 5 and 6 are added by comparing the two ids.
Is it possible? Please let me know
Thanks
This will give you 1 & 4:
select a.values
from my_table a
where not exists (
select * from my_table b where b.values = a.values and b.ID = 2)
and a.ID = 1
and this will give you 5 & 6:
select a.values
from my_table a
where not exists (
select * from my_table b where b.values = a.values and b.ID = 1)
and a.ID = 2
Something like:
(
SELECT T.Value FROM dbo.Table T WHERE T.ID = 1
EXCEPT
SELECT T.Value FROM dbo.Table T WHERE T.ID = 2
)
UNION
(
SELECT T.Value FROM dbo.Table T WHERE T.ID = 2
EXCEPT
SELECT T.Value FROM dbo.Table T WHERE T.ID = 1
)
That will get you the list of values that are associated with 1 but not 2, and 2 but not 1. You could easily multiply the values from one of those subqueries by -1 to differentiate them, or run them as two separate queries.