Select rows until condition met - sql

I would like to write an Oracle query which returns a specific set of information. Using the table below, if given an id, it will return the id and value of B. Also, if B=T, it will return the next row as well. If that next row has a B=T, it will return that, and so on until a F is encountered.
So, given 3 it would just return one row: (3,F). Given 4 it would return 3 rows: ((4,T),(5,T),(6,F))
id B
1 F
2 F
3 F
4 T
5 T
6 F
7 T
8 F
Thank you in advance!

Use a sub-query to find out at what point you should stop, then return all row from your starting point to the calculated stop point.
SELECT
*
FROM
yourTable
WHERE
id >= 4
AND id <= (SELECT MIN(id) FROM yourTable WHERE b = 'F' AND id >= 4)
Note, this assumes that the last record is always an 'F'. You can deal with the last record being a 'T' using a COALESCE.
SELECT
*
FROM
yourTable
WHERE
id >= 4
AND id <= COALESCE(
(SELECT MIN(id) FROM yourTable WHERE b = 'F' AND id >= 4),
(SELECT MAX(id) FROM yourTable )
)

Related

How can I select if row contains a specific type, not contains select existing row

I have a table below. There are 3 types(A,B,C) available. I want to create general a Sql query. If row has type B or type C, the row with type B or C should be listed. If row has just type A, the row with type A should be listed.
Table;
Number
Type
1
A
1
B
2
A
3
A
3
C
4
A
5
A
6
A
6
B
6
C
Expected result when the query run;
Number
Type
1
B
2
A
3
C
4
A
5
A
6
B
6
C
How can I create the query? Thank you in advance for your help.
I would assign each type a precedence, and only return the types of the highest precedence.
Where two types can be the same precedence (because you want to return both), RANK() (rather than ROW_NUMBER()) will ensure both are assigned the same value.
WITH
precedence AS
(
SELECT
*,
RANK()
OVER (
PARTITION BY Number
ORDER BY CASE Type WHEN 'C' THEN 2
WHEN 'B' THEN 2
WHEN 'A' THEN 1
ELSE 0
END
DESC
)
AS row_precedence
FROM
your_table
)
SELECT
*
FROM
precedence
WHERE
row_precedence = 1
One option to make it a little neater could be to use APPLY (or a join on a lookup table) to derive the integers outside of the window function's code...
WITH
precedence AS
(
SELECT
*,
RANK()
OVER (
PARTITION BY Number
ORDER BY type_precedence.value DESC
)
AS row_precedence
FROM
your_table
CROSS APPLY
(
SELECT
CASE Type WHEN 'C' THEN 2
WHEN 'B' THEN 2
WHEN 'A' THEN 1
ELSE 0
END
AS value
)
AS type_precedence
)
SELECT
*
FROM
precedence
WHERE
row_precedence = 1
Demo: https://dbfiddle.uk/2Abwpa8p

SQL aggregate and filter functions

Consider following table:
Number | Value
1 a
1 b
1 a
2 a
2 a
3 c
4 a
5 d
5 a
I want to choose every row, where the value for one number is the same, so my result should be:
Number | Value
2 a
3 c
4 a
I manage to get the right numbers by using nested
SQL-Statements like below. I am wondering if there is a simpler solution for my problem.
SELECT
a.n,
COUNT(n)
FROM
(
SELECT number n , value k
FROM testtable
GROUP BY number, value
) a
GROUP BY n
HAVING COUNT(n) = 1
You can try this
SELECT NUMBER,MAX(VALUE) AS VALUE FROM TESTTABLE
GROUP BY NUMBER
HAVING MAX(VALUE)=MIN(VALUE)
You can try also this:
SELECT DISTINCT t.number, t.value
FROM testtable t
LEFT JOIN testtable t_other
ON t.number = t_other.number AND t.value <> t_other.value
WHERE t_other.number IS NULL
Another alternative using exists.
select distinct num, val from testtable a
where not exists (
select 1 from testtable b
where a.num = b.num
and a.val <> b.val
)
http://sqlfiddle.com/#!9/dd080dd/5

Select rows using a count condition inside a where clause

There is a table, MyTable(ID, Type, Date). Column 'Type' can have a value of 1 or 2.
Top 'x' rows, ordered by 'Date' and satisfying the following condition, have to be selected.('a' and 'b' are integer values)
The selected 'x' rows can contain only a maximum of 'a' Type 1 rows and 'b' Type 2 rows.(If a+b < x, then only a+b rows have to be selected.)
I might be completely wrong but I have an idea of doing this by having count() inside a WHERE clause. But I am not sure of how to do it.
How do I go about this problem?
UPDATE:
Example -
x = 5
Case 1:
a = 5, b = 5
Result: Rows 1,2,3,4,5
Case 2:
a = 4, b = 1
Result: Rows 1,2,4,6,8
Case 3:
a = 1, b = 5
Result: Rows 1,2,3,5,7
Case 4:
a = 2, b = 1
Result: Rows 1,2,4
You can do it by nesting a UNION inside another query:
select top #x *
from (
select top #a *
from table
where type = '1'
order by [date]
union
select top #b *
from table
where type = '2'
order by [date]
) t
order by [date]

Return different rows for each column in a row

I have data which is presented in multiple rows and columns with 0 or 1 values. What I'm trying to do is create a unique row for each 1, but there are sometimes multiple 1's in a row. For ex:
**A B C D**
1 0 1 1
0 0 0 1
1 1 0 0
I would like to have return six rows, all in one column like so
**RETURN**
A
C
D
D
A
B
Thanks in advance!
You can do this with a union all statement:
select val
from ((select 'A' as val from t where A = 1) union all
(select 'B' from t where B = 1) union all
(select 'C' from t where C = 1) union all
(select 'D' from t where D = 1)
) t
As a note: I hope you have other columns that you can include in the output. SQL tables are, by definition, not ordered. So, you really have no idea in your example of the original source for any given value.

SQL (TSQL) - Select values in a column where another column is not null?

I will keep this simple- I would like to know if there is a good way to select all the values in a column when it never has a null in another column. For example.
A B
----- -----
1 7
2 7
NULL 7
4 9
1 9
2 9
From the above set I would just want 9 from B and not 7 because 7 has a NULL in A. Obviously I could wrap this as a subquery and USE the IN clause etc. but this is already part of a pretty unique set and am looking to keep this efficient.
I should note that for my purposes this would only be a one-way comparison... I would only be returning values in B and examining A.
I imagine there is an easy way to do this that I am missing, but being in the thick of things I don't see it right now.
You can do something like this:
select *
from t
where t.b not in (select b from t where a is null);
If you want only distinct b values, then you can do:
select b
from t
group by b
having sum(case when a is null then 1 else 0 end) = 0;
And, finally, you could use window functions:
select a, b
from (select t.*,
sum(case when a is null then 1 else 0 end) over (partition by b) as NullCnt
from t
) t
where NullCnt = 0;
The query below will only output one column in the final result. The records are grouped by column B and test if the record is null or not. When the record is null, the value for the group will increment each time by 1. The HAVING clause filters only the group which has a value of 0.
SELECT B
FROM TableName
GROUP BY B
HAVING SUM(CASE WHEN A IS NULL THEN 1 ELSE 0 END) = 0
If you want to get all the rows from the records, you can use join.
SELECT a.*
FROM TableName a
INNER JOIN
(
SELECT B
FROM TableName
GROUP BY B
HAVING SUM(CASE WHEN A IS NULL THEN 1 ELSE 0 END) = 0
) b ON a.b = b.b