SQL statement to conditionally select related records - sql

I have a table with fields id (primary key) and fid. I want to get the record where id matches a particular value, as well as all related records that have its same fid value.
I can do this:
SELECT * FROM mytable
WHERE fid = (SELECT TOP 1 fid FROM mytable WHERE id = 'somevalue')
But I don't want the related records if the fid is a particular value (in my case an empty guid value).
Is there a way to do this in a single SQL statement? I am using SQL Server 2008 R2.
UPDATE:
Looking at the answers so far I think I may not have asked my question clearly. id and fid will never be equal. LEFT JOIN may be what I need, but I'm a bit SQL ignorant. What I'm hoping for is the following two queries as a single statement:
SELECT * FROM mytable WHERE id = 'somevalue'
SELECT * FROM mytable WHERE fid =
(SELECT TOP 1 fid FROM mytable
WHERE id = 'somevalue' AND fid != '00000000-0000-0000-0000-000000000000')

Based on your revision, the problem seems to be "select all rows where id has a certain value and all other rows with the id matches "somevalue" and the fid is not null.
The following captures this logic:
SELECT t.*
FROM mytable t left outer join
(SELECT TOP 1 fid
FROM mytable
WHERE id = 'somevalue' AND fid <> '00000000-0000-0000-0000-000000000000'
) t1
on t.fid = t1.fid
WHERE id = 'somevalue' or t1.fid is not null;
Because id is a primary key, the t1 subquery will return 0 or 1 rows. When it returns 0 rows, you will only get the original row matching 'somevalue'.

I'm not certain I understand your question, but I'll take a stab at it. What I think you're asking is if you can select all records from one table where either the id or fid fields equal a particular value, but you don't want the related fields if the particular value you're searching on equals an empty guid value. If so, here's how you can do it:
SELECT
*
FROM
mytable t1
LEFT JOIN
mytable t2 ON (t1.id = t2.fid) AND (t2.fid IS NOT NULL);
Is this what you were looking for?

I think this is what you are trying to do:
SELECT *
FROM mytable a
JOIN mytable b ON a.id = b.fid
WHERE a.id = 'somevalue';
This should return all records in a (joined with all records in b where a.id = b.fid) then filtered to show only records that have a.id = 'somevalue';

You could just add another clause to your sql statement like this:
SELECT * From mutable
WHERE fid = (SELECT TOP 1 fid FROM mytable WHERE id = 'somevalue'
AND fid != '00000000-0000-0000-0000-000000000000')
If you want more than one row, try a join as suggested by #zigdawgydawg.

Maybe this is what you are after:
select * from mytable
where id = 'somevalue'
or id = (select fid from mytable where id = 'somevalue')

Almost like zigdawgydawg's contribution, but slightly different:
SELECT * FROM mytable WHERE fid IN
(SELECT fid FROM mytable WHERE id = 'somevalue' )
AND NOT guid is null;

Related

How to find unmatched records in a single table?

I'm scraping a log file for transaction records that I am inserting into a table that will be used for several mining tasks. Each record has (among other things) an ID and a transaction type, either request or response. A request/response pair will have the same ID.
One of my tasks is to find all of the requests that do not have a corresponding response. I thought about joining the table to itself, where A.ID = B.ID AND A.type = 'req' and B.type = 'res', but that gives me the opposite of what I need.
Since the IDs will always occur either once or twice, is there a query that would select ID where there is only one occurrence of that ID in the table?
This is a very common type of query. You can try aggregating over the ID values in your table using GROUP BY, then retaining those ID which appear only once.
SELECT ID
FROM yourTable
GROUP BY ID
HAVING COUNT(*) = 1
If you also want to return the entire records for those ID occurring only once, you could try this:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT ID FROM yourTable GROUP BY ID HAVING COUNT(*) = 1
) t2
ON t1.ID = t2.ID
The straight-forward way is NOT IN:
select *
from mytable
where type = 'req'
and id not in (select id from mytable where type = 'res');
You can write about the same with NOT EXISTS, but the query becomes slightly less readable:
select *
from mytable req
where type = 'req'
and not exists (select * from mytable res where type = 'res' and res.id = req.id);
And then there are forms of aggregation you can use, e.g.:
select *
from mytable
where type = 'req'
and id in
(
select id
from mytable
group by id
having count(case when type = 'res' then 1 end) = 0
);
This will give you the ones that have Request but not respose
SELECT *
FROM your_table A LEFT OUTER JOIN
your_table B ON A.ID = B.ID
AND A.type = 'req' and B.type = 'res'
WHERE B.ID IS NULL

Setting a column value in the SELECT Statement based on a value existing in another table

I have 2 tables. One table lists all the records of items we track. The other table contains flags of attributes of the records in the first table.
For example, Table 1 has columns
Tab1ID, Name, Address, Phone
Table 2 has these columns
Tab2ID, Tab1ID, FlagName
There is a 1 to Many relationship between Table1 and Table2 linked by Tab1ID.
I'd like to create a query that has all the records from Table1 in it. However, if one of the records in Table2 has a Flagname=Retired (with a matching Tab1ID) then I want a "Y" to show up in the select column list otherwise an "N".
I think it might look something like this:
Select Name, Address, Phone, (select something in table2)
from Table1
where Tab1ID > 1;
It's the subquery in the column that has me stumped.
Pat
You can use exists:
Select t1.*,
(case when exists (select 1
from table2 t2
where t2.tab1id = t1.tab1id and t2.flagname = 'Retired'
)
then 'Y' else 'N'
end) as retired_flag
from Table1 t1;
I would do a normal join returning multiple records, but convert them to bits with case statements. Then use that as the subquery and pull the max value for each bit column.
select
name
,address
,phone
,max(retired_flag)
from (
select
table1.name
,table1.address
,table1.phone
,case when table2.flagname = 'retired' then 1 else 0 end as [retired_flag]
from table1
left join table2
on table1.tab1id = table2.tab1id
where tab1id > 1
) tbl
group by
name
,address
,phone

SQL subquery to joins -

Is it possible to remove the subquery from this SQL?
Table has 2 attributes "id" and "field"
Many field could have the same Id.
These table has many registers with the same Id and different Value
In need get all same Id values using one of them like filter.
select *
from Table
where id = (select id from Table where value = 'someValue')
I think it could be really easy but I don't know how to do.
Self Join can be done
select T.Id,T.Field
from Table T
INNER JOIN Table TT
ON T.ID = TT.ID
AND TT.Value = 'someValue'
Not sure if you over simplified your example too much but you could make this a little simpler.
select *
from Table
where value = 'someValue'
This should work
select T1.* from Table T1 JOIN Table T2 ON T1.id = T2.id AND T2.value = 'someValue'
Edited (Correct Answer):
What I assume your problem is:
You have a value. Let´s pretend it´s "testValue". Now you want to get the id of this value and find all other datasets with the same id.
What has to be cleared is that, "ID" is not the Primary Key and is not Unique.
You should be able to solve this by a simple self join:
select t.* from Table t right join Table tt on tt.id = t.id where tt.value = 'someValue';
So because of the join you will get a result that returns simply the table. With the where clause you shrink the result to your value. You should get the set of ids.
Old Answer:
This should do the trick:
select * from Table a inner join Table2 b on a.id = b.id where b.value = 'someValue';
You mentioned only one table in your question. I think this must be a mistake. If not, you have to change only the Table2 in my query. But that would have no sense as you could do a simple query, too:
select * from Table where value = 'someValue';
this would be the result of the first query with a self join.

Query a table that have 2 cols with multiple criteria

I have a table with the following structure and Example data:
Now I want to query the records that have value equals to # and #.
For example according to the above image, It should returns 1 and 2
id
-----
1
2
Also if the parameters were #, # and $ It should give us 1. Because only the records with id 1 have all the given values.
id
-----
1
You can use a group by and having to get the distinct Id's that contain a distinct count of the number of items you're looking for
SELECT Id
FROM Table
WHERE Value IN ('#','$')
GROUP BY Id
HAVING COUNT(DISTINCT Value) = 2
SELECT Id
FROM Table
WHERE Value IN ('#','$','#')
GROUP BY Id
HAVING COUNT(DISTINCT Value) = 3
SQL Fiddle you can use this link to test
There's several ways to do this.
The subquery method:
SELECT DISTINCT Id
FROM Table
WHERE Id IN (SELECT Id FROM Table WHERE Value = '#')
AND Id IN (SELECT Id FROM Table WHERE Value = '#');
The correlated subquery method:
SELECT DISTINCT t.Id
FROM Table t
WHERE EXISTS (SELECT 1 FROM Table a WHERE a.Id = t.Id and a.Value = '#')
AND EXISTS (SELECT 1 FROM Table b WHERE b.Id = t.Id and b.Value = '#');
And the INTERSECT method:
SELECT Id FROM Table WHERE Value = '#'
INTERSECT
SELECT Id FROM Table WHERE Value = '#';
Best performance will depend on RDBMS vendor, size of table, and indexes. Not all RDBMS vendors support all methods.
Maybe a multiple self join like this?
select
distinct t1.id
from
table t1
join table t2 on (t1.id=t2.id)
join table t3 on (t1.id=t3.id)
...
where
t1.value='#' and
t2.value='#' and
t3.value='$' and
...

SQL - using a value in a nested select

Hope the title makes some kind of sense - I'd basically like to do a nested select, based on a value in the original select, like so:
SELECT MAX(iteration) AS maxiteration,
(SELECT column
FROM table
WHERE id = 223652
AND iteration = maxiteration)
FROM table
WHERE id = 223652;
I get an ORA-00904 invalid identifier error.
Would really appreciate any advice on how to return this value, thanks!
It looks like this should be rewritten with a where clause:
select iteration,
col
from tbl
where id = 223652
and iteration = (select max(iteration) from tbl where id = 223652);
You can circumvent the problem alltogether by placing the subselect in an INNER JOIN of its own.
SELECT t.iteration
, t.column
FROM table t
INNER JOIN (
SELECT id, MAX(iteration) AS iteration
FROM table
WHERE id = 223652
) tm ON tm.id = t.id AND tm.iteration = t.iteration
Since you're using Oracle, I'd suggest using analytic functions for this:
SELECT * FROM (
SELECT col,
iteration,
row_number() over (partition by id order by iteration desc) rn
FROM tab
WHERE id = 223652
) WHERE rn = 1
do it like this:
with maxiteration as
(
SELECT MAX(iteration) AS maxiteration
FROM table
WHERE id = 223652
)
select
column,
iteration
from
table
where
id = 223652
AND iteration = maxiteration
;
Not 100% sure on Oracle syntax, but isn't it something like:
select iteration, column from table where id = 223652 order by iteration desc limit 1
I would approach this problem in a slightly different way. You're basically looking for the row that has no other iterations greater than it. There are at least 3 ways I can think of to do this:
SELECT
T1.iteration AS maxiteration,
T1.column
FROM
Table T1
WHERE
T1.id = 223652 AND
NOT EXISTS
(
SELECT *
FROM Table T2
WHERE
T2.id = 223652 AND
T2.iteration > T1.iteration
)
Or...
SELECT
T1.iteration AS maxiteration,
T1.column
FROM
Table T1
LEFT OUTER JOIN Table T2 ON
T2.id = T1.id AND
T2.iteration > T1.iteration
WHERE
T1.id = 223652 AND
T2.id IS NULL
Or...
SELECT
T1.iteration AS maxiteration,
T1.column
FROM
Table T1
INNER JOIN (SELECT id, MAX(iteration) AS maxiteration FROM Table T2 GROUP BY id) SQ ON
SQ.id = T1.id AND
SQ.maxiteration = T1.iteration
WHERE
T1.id = 223652
EDIT: I didn't see the ORA error the first time reading the question and it wasn't tagged as Oracle specific. I think that there may be some differences in the syntax and use of aliases in Oracle, so you may need to tweak some of the above queries.
The Oracle error is telling you that it doesn't know what maxiteration is, because the column alias isn't available yet inside the subquery. You need to refer to it by the table alias and column name instead of the column alias I believe.
You do something like
select maxiteration,column from table a join (select max(iteration) as maxiteration from table where id=1) b using (id) where b.maxiteration=a.iteration;
This could of course return multiple rows for one maxiteration unless your table has a constraint against it.