Delete all observations of a group after a variable changes - sql

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
);

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

Complicated SQL query request

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".

sql excluding certain results

lets say i have a data set of
A B
-- --
a 1
b 1
c 1
d 1
d 2
e 1
f 1
f 2
g 1
how would i exclude a result in column B of 1, if column B has values of both 1 and 2 for the same value in column A?
i want my results to look like this
A B
-- --
a 1
b 1
c 1
d 2
e 1
f 2
g 1
Checking explicitly here for the values 1 and 2 and using the fact that there are exactly two of them. You could potentially make this less cumbersome if it's safe to assume that you always want the highest value.
select
tbl.A,
tbl.B
from
Table1 tbl
left outer join (
select
A
from
Table1
where
B in (1,2)
group by
A
having
count(B) = 2
) mlt on tbl.A = mlt.A
where
(
mlt.A is not null
and tbl.B = 2
) or (
mlt.A is null
and tbl.B = 1
)
Figure out all the A values that have both 1 and 2.
Match those to the table on the A value.
If A is in the subquery, use the B = 2 record. If it isn't, use the B = 1 record.
select
* from tbl where a IN
(
select
a from tbl
group by a
having count(*)>1
)
and b!=1
UNION ALL
select
* from tbl where a IN
(
select
a from tbl
group by a
having count(*)=1
)
For the example data and desired result, the simplest query to achieve the result would be a GROUP BY operation and an aggregate function.
SELECT d.A
, MAX(d.B) AS B
FROM my_data_set d
GROUP BY d.A
ORDER BY d.A
If we are only interested in rows that have a 1 or 2 in column B, we can add a WHERE clause
SELECT d.A
, MAX(d.B) AS B
FROM my_data_set d
WHERE d.B IN (1,2)
GROUP BY d.A
ORDER BY d.A
With the example data, the output is the same.
Both of these statements achieve the specified result. (There is only a single row returned for each distinct value in A.)
Or, for the same the example data, we can return the same result set with a more literal implementation of the specification.
To exclude rows with 1when there is a row with 2 for the same value of A, we can use a NOT EXISTS predicate and a correlated subquery.
SELECT d.A
, d.B
FROM my_data_set d
WHERE ( d.B = 2 )
OR ( d.B = 1 AND
NOT EXISTS ( SELECT 1
FROM my_data_set e
WHERE e.A = d.A
AND e.B = 2
)
)
ORDER BY d.A, d.B

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

Set value of column to 0 for only one of the duplicate records?

I have the following table, and I want to set the value of deleted column to zero for only one of the records that have duplicate names.
id name deleted
------------------------
1 a 1
2 a 1
3 a 1
4 b 1
5 c 1
6 d 1
so the output will be:
id name deleted
------------------------
1 a 0
2 a 1
3 a 1
4 b 0
5 c 0
6 d 0
If your dbms is SQL-Server( >= 2005) you can use a CTE with ROW_NUMBER:
WITH CTE AS
(
SELECT ID, Name, Deleted,
RN = ROW_NUMBER() OVER(PARTITION BY name ORDER BY ID)
FROM dbo.T
)
UPDATE CTE
SET Deleted = 0
WHERE RN = 1
DEMO
UPDATE Tbl SET deleted = 0 WHERE id IN
(SELECT MIN(id) FROM Tbl GROUP BY name)
UPDATE A
SET A.deleted = 0
FROM Tbl A
LEFT JOIN Tbl B
ON A.Name = B.Name
AND A.Id > B.Id
WHERE B.Id IS NULL