Efficiently Check for Existence in a Grouped Set - sql

I would like to group over one column checking over some boolean expression in another.
For instance, Say I have a single table:
MyTable
ID | ColA
----------
1 A
1 B
1 C
2 A
2 C
What I would like to do is fetch all records over some boolean selection logic. For instance fetch all IDs where a ColA exists with values A and B
Something similar to
SELECT ID, ColA
FROM MyTable
WHERE ID IN (SELECT ID FROM MyTable WHERE ColA = 'A')
AND ID IN (SELECT ID FROM MyTable WHERE ColA = 'B')
To Return the selection
ID | ColA
---------
1 A
1 B
1 C
Something like the above would work, but it doesn't seem to be all too efficient.

I would write the query using exists and then be sure that some indexes are defined:
SELECT t.ID, t.ColA
FROM MyTable t
WHERE EXISTS (SELECT 1 FROM MyTable t2 WHERE t2.ID = t.ID AND t2.ColA = 'A') AND
EXISTS (SELECT 1 FROM MyTable t2 WHERE t2.ID = t.ID AND t2.ColA = 'B') ;
The index that you want is MyTable(id, ColA).

The query you propose is not too bad for the problem you present. No, it doesn't look so efficient, but what you're asking to do cannot be performed very efficiently.
This variation is at least a little clearer, and perhaps SQL Server's query planner could do something useful with it:
SELECT ID, ColA
FROM MyTable
WHERE ID IN (
SELECT ID FROM MyTable WHERE ColA = 'A'
INTERSECT
SELECT ID FROM MyTable WHERE ColA = 'B'
)

Related

Postgres: Find a record with one specific value in the field

I have a table like this:
ID | Type
----------
1 | Web
1 | App
2 | Web
2 | Web
3 | App
I want to get the IDs that have only 'Web' as the type.
I have tried this:
SELECT id FROM tbl
GROUP BY 1
HAVING COUNT(DISTINCT type) = 1
But this gives me all IDs irrespective of type being 'Web' or not.
My expected output is this:
ID | Type
----------
2 | Web
We could use exists logic here:
SELECT DISTINCT ID
FROM yourTable t1
WHERE
t1.Type = 'Web' AND
NOT EXISTS (SELECT 1 FROM yourTable t2
WHERE t2.ID = t1.ID AND t2.Type <> 'Web');
Demo
You can use aggregation:
select id
from t
group by id
having min(type) = max(type) and min(type) = 'Web';
You can try using not exists
DEMO
select distinct id, type from tablename t1
where not exists
( select 1 from tablename t2 where t1.id=t2.id and t2.type<>'Web')
You can use SELECT id, count(*) as total FROM table_name WHERE type = 'Web' GROUP BY type HAVING total = 1)
try like below i just edited your answer by adding a where clause
SELECT id FROM tbl t1
where
t1.id in ( select id from tbl a where a.type='Web')
GROUP BY id
HAVING COUNT(DISTINCT type) = 1

Match columns 1 if data not found then search column 2 oracle query

I am trying to find a way if data is not found based on col1 of a table then search with other column value
SELECT * FROM TABLE
WHERE COL1='123'
IF NULL
THEN
SELECT * FROM TABLE
WHERE COL2='ABC';
Thanks
This a typical SQL select statement involving an OR expression.
SELECT * from TABLE WHERE Col1 = '123' or Col2 = 'ABC';
You want all rows that satisfy the first condition - but if no row matches, then you want all rows that satisfy the second condition.
I would adress this with a row limiting clause (available starting version 12c):
select *
from mytable
where 'ABC' in (col1, col2)
order by rank() over(order by case col1 = 'ABC' then 1 else 2 end)
fetch first 1 row with ties
This is more efficient than union all because it does not require two scans on the table.
You can use exists with union all :
select t.*
from table t
where col1 = 123 union all
select t.*
from table t
where col2 = 'abc' and
not exists (select 1 from table t1 where t1.col1 = 123);
If you are expecting only one row, you can use:
SELECT t.*
FROM TABLE t
WHERE COL1 = '123' OR COL2 = 'ABC'
ORDER BY (CASE WHEN COL1 = '123' THEN 1 ELSE 2 END)
FETCH FIRST 1 ROW ONLY;
With multiple possible rows in the result set, I would go for:
SELECT t.*
FROM TABLE t
WHERE COL1 = '123' OR
(COL2 = 'ABC' AND
NOT EXISTS (SELECT 1 FROM TABLE t2 WHERE t2.COL1 = '123');

Selecting certain value from row based on another value in same row

I have a following table with following data:
Table
Now I want to get all those users (distinct only) who do not have value 5 in Column B. What I mean is user1 has a value 5 in some row, then all user 1 rows are dismissed.
Following result should be produced:
user2 (because value is null)
user3 (no value 5)
How can I do that?
Perhaps the easiest way to do this would be aggregation by user:
SELECT ColumnA
FROM yourTable
GROUP BY ColumnA
HAVING COUNT(CASE WHEN ColumnB = 5 THEN 1 END) = 0;
One method is aggregation:
select columnA
from t
group by columnA
having sum(case when columnB = 5 then 1 else 0 end) = 0;
You can do this by Minus operator
SELECT distinct colA
FROM have
WHERE colB not in(5)
MINUS
SELECT distinct colA
FROM have
WHERE colB=5;
Using NOT EXISTS you can able to get the result
SELECT DISTINCT T1.ColumnA
FROM TableName T1
WHERE NOT EXISTS (
SELECT * FROM TableName T2 WHERE T2.ColumnA = T1.ColumnA AND T2.ColumnB <> 5
)
One more way -
SELECT DISTINCT T1.ColumnA
FROM TableName T1
WHERE T1.ColumnA NOT IN
(
SELECT T2.ColumnA FROM TableName T2 WHERE T2.ColumnB = 5
)

Postgres - SQL to match the first rownum

I have the following SQL to generate a row num for each record
MY_VIEW AS
( SELECT
my_id,
(case when col1 = 'A' then
1
when col1 = 'C' then
2
else
3
end) as rownum
from table_1
So I have data look like this:
my_id rownum
0001-A 1
0001-A 2
0001-B 2
Later, I want to use the smallest rownum for each unique "my_id" to do a inner join what another table_2. How should I proceed? This is what I have so far.
select * from table_2
inner join tabl_1
on table_2.my_id = table1.my_id
and row_num = (...the smallest from M_VIVE...)
In Postgres, I would recommend distinct on:
selecd distinct on (my_id) my_id
(case when col1 = 'A' then 1
when col1 = 'C' then 2
else 3
end) as rownum
from table_1
order by my_id, rownum;
However, you can just as easily do this using group by:
select my_id,
min(case when col1 = 'A' then 1
when col1 = 'C' then 2
else 3
end) as rownum
from table_1
group by my_id;
The distinct on approach allows you to include other columns. It might be a bit faster. On the downside, it is Postgres-specific.
You can use MIN() function for rownum against every my_id in table_1 and use that in the join.
You would need to make sure table_2 also has my_id field to make the join work.
select *
from
table_2
inner join
(select my_id, MIN(rownum) as minimum_rownum from tabl_1 group by my_id) t1
on table_2.my_id = t1.my_id;

Select statement for Oracle SQL

I have a table say,
column1 column2
a apple
a ball
a boy
b apple
b eagle
b orange
c bat
c ball
c cork
Now I would like to fetch column1 based on the rows that doesn't contain 'apple' and also ignore values in column1 if any of the rows have 'apple' in it. So in the table above only 'C' must be retured.
I am kind of new to Oracle SQL and I know Select column1 from table where column2 != 'apple' will not work. I need some help with this please.
You could use DISTINCT with NOT IN in following:
QUERY 1 using NOT IN
select distinct col1
from t
where col1 not in (select col1 from t where col2 = 'Apple')
QUERY 2 using NOT EXISTS
As per #jarlh comment you could use NOT EXISTS in following:
select distinct col1
from #t t1
where not exists (select 1 from #t t2 where col2 = 'Apple' and t1.col1 = t2.col1)
SAMPLE DATA
create table t
(
col1 nvarchar(60),
col2 nvarchar(60)
)
insert into t values
('a','apple')
,('a','ball')
,('a','boy')
,('b','apple')
,('b','eagle')
,('b','orange')
,('c','bat')
,('c','ball')
,('c','cork')
Assuming that column1 is NOT NULL you could use:
SELECT DISTINCT t.column1
FROM table_name t
WHERE t.column1 NOT IN (SELECT column1
FROM table_name
WHERE column2 = 'apple');
LiveDemo
To get all columns and rows change DISTINCT t.column1 to *.
Select * from tbl
Left join (
Select column1 from tbl
Where column2 like '%apple%'
Group by column1
) g on tbl.colum1 = g.column1
Where g.column1 is null
Seems to me that you need to find a summary of all colum1 values that have any reference to apple. Then list the rows that have no match to the summary list (g)
If I understand well, you need the values af column1 such that in your table does not exist a row with the same value of column1 and 'apple' in column2; you can translate this in SQL with:
Select column1
from your_table t
where not exists (
select 1
from your_table t2
where t2.column1 = t1.column1
and t2.column2= 'apple'
)
This is only one of the possible ways to get your result, soyou can rewrite it in many ways; I believe this way of writing is similar enough to the logics to clearly explain how a logic could be written in plain SQL.