Table self related query - sql

I have a table which has self relationship:
id -- parentId columns
I need a query to get the parent row where the parentId is null, but I couldn't figure out the proper way of doing this.
select * from table1 where id = parentId;
Apparently this is not working, it will just give the direct parent.
Any help

"Get the parent row where the parentId is null" makes no sense, but in case you actually meant "get the parent row until the parentId is null", then this recursive CTE should do the trick:
WITH cte AS (
SELECT * FROM table1 WHERE id = 7
UNION ALL
SELECT table1.* FROM table1 JOIN cte ON table1.id = cte.parentId
)
SELECT * FROM cte
This returns the row with id = 7 and all its ancestors recursively. Replace 7 according to your needs.

SELECT *
FROM table1 AS A
LEFT JOIN table1 as B
ON B.ID = A.parentID
WHERE B.parentID IS NULL

Related

SQL - return a value referenced by initial value from same table

I'm sure this has been asked before, but I can't seem to find any questions that help.
Here's an example of a table:
ID Name Parent ID
---------------------------
1 Ice cream 3
2 Chips 4
3 Sweets null
I'm trying to figure out how to write a single query which, given ID=1, will return me rows 1 and 3. Is this possible without making two queries?
Also, is there a way to return the information of the parent as a custom column? So, rather than returning 2 rows, returning the row where id=1 with parent_id=3 added on?
You can use union all and exists:
select * from mytable where parent_id = 3
union all
select t.*
from mytable t
where exists (select 1 from mytable t1 where t1.parent_id = t.id and t.parent_id = 3)
If you want to do this over multiple levels of hierarchy, then you would typically use a recursive query. The syntax slightly varies accross databases (and not all of them support recursion), but the idea is:
with recursive cte as (
select * from mytable where parent_id = 3
union all
select t.*
from cte c
inner join mytable t on t.parent_id = c.id
)
select * from cte
This is how a straight up noob would do it. Hold tight for someone to give a better way
SELECT ID, Name, Parent_ID
FROM table
WHERE ID = 1
UNION
SELECT ID, Name, Parent_ID
FROM table
WHERE ID = (SELECT Parent_ID FROM table WHERE ID = 1)
Are you looking for something like this?:
select child.ID, child.Name, parent.ID as ParentId, parent.Name as ParentName
from T child left outer join T parent on parent.Id = child.parent_id;

Simple Recursive Query child to last parent

I need to retrieve the last (or first) id of a child's parent.
Example:
ID PARENT_ID
----------------
1 NULL
2 1
3 2
So if I search the parent id of id=3 I would have 1 as result.
I tried this but it gives me the same id...
with
tree(id)
as
(
select id
from myTable
where id = 3
union all
select t.id
from myTable t
inner join tree on tree.id = t.father_id
)
select *
from tree;
I already saw examples here and on several websites ;)
You've got some inconsistent naming here. But anyway, your CTE needs to include the parent_id too.
Like this:
with
tree(id,parent_id)
as
(
select id, parent_id
from myTable
where id = 3
union all
select t.id, t.parent_id
from myTable t
inner join tree on t.id = tree.parent_id
)
select *
from tree;

SQL statement to conditionally select related records

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;

select a value where it doesn't exist in another table

I have two tables
Table A:
ID
1
2
3
4
Table B:
ID
1
2
3
I have two requests:
I want to select all rows in table A that table B doesn't have, which in this case is row 4.
I want to delete all rows that table B doesn't have.
I am using SQL Server 2000.
You could use NOT IN:
SELECT A.* FROM A WHERE ID NOT IN(SELECT ID FROM B)
However, meanwhile i prefer NOT EXISTS:
SELECT A.* FROM A WHERE NOT EXISTS(SELECT 1 FROM B WHERE B.ID=A.ID)
There are other options as well, this article explains all advantages and disadvantages very well:
Should I use NOT IN, OUTER APPLY, LEFT OUTER JOIN, EXCEPT, or NOT EXISTS?
For your first question there are at least three common methods to choose from:
NOT EXISTS
NOT IN
LEFT JOIN
The SQL looks like this:
SELECT * FROM TableA WHERE NOT EXISTS (
SELECT NULL
FROM TableB
WHERE TableB.ID = TableA.ID
)
SELECT * FROM TableA WHERE ID NOT IN (
SELECT ID FROM TableB
)
SELECT TableA.* FROM TableA
LEFT JOIN TableB
ON TableA.ID = TableB.ID
WHERE TableB.ID IS NULL
Depending on which database you are using, the performance of each can vary. For SQL Server (not nullable columns):
NOT EXISTS and NOT IN predicates are the best way to search for missing values, as long as both columns in question are NOT NULL.
select ID from A where ID not in (select ID from B);
or
select ID from A except select ID from B;
Your second question:
delete from A where ID not in (select ID from B);
SELECT ID
FROM A
WHERE NOT EXISTS( SELECT 1
FROM B
WHERE B.ID = A.ID
)
This would select 4 in your case
SELECT ID FROM TableA WHERE ID NOT IN (SELECT ID FROM TableB)
This would delete them
DELETE FROM TableA WHERE ID NOT IN (SELECT ID FROM TableB)
SELECT ID
FROM A
WHERE ID NOT IN (
SELECT ID
FROM B);
SELECT ID
FROM A a
WHERE NOT EXISTS (
SELECT 1
FROM B b
WHERE b.ID = a.ID)
SELECT a.ID
FROM A a
LEFT OUTER JOIN B b
ON a.ID = b.ID
WHERE b.ID IS NULL
DELETE
FROM A
WHERE ID NOT IN (
SELECT ID
FROM B)

mysql - union tables by unique field

I have two tables with the same structure:
id name
1 Merry
2 Mike
and
id name
1 Mike
2 Alis
I need to union second table to first with keeping unique names, so that result is:
id name
1 Merry
2 Mike
3 Alis
Is it possible to do this with MySQL query, without using php script?
This is not a join (set multiplication), this is a union (set addition).
SELECT #r := #r + 1 AS id, name
FROM (
SELECT #r := 0
) vars,
(
SELECT name
FROM table1
UNION
SELECT name
FROM table2
) q
This will select all names from table1 and combine those with all the names from table2 which are not in table1.
(
select *
from table1
)
union
(
select *
from table2 t2
left join table1 t1 on t2.name = t1.name
where t1.id is null
)
Use:
SELECT a.id,
a.name
FROM TABLE_A a
UNION
SELECT b.id,
b.name
FROM TABLE_B b
UNION will remove duplicates.
As commented, it all depends on what your 'id' means, cause in the example, it means nothing.
SELECT DISTINCT(name) FROM t1 JOIN t2 ON something
if you only want the names
SELECT SUM(something), name FROM t1 JOIN t2 ON something GROUP BY name
if you want to do some group by
SELECT DISTINCT(name) FROM t1 JOIN t2 ON t1.id = t2.id
if the id's are the same
SELECT DISTINCT COALESCE(t1.name,t2.name) FROM
mytable t1 LEFT JOIN mytable t2 ON (t1.name=t2.name);
will get you a list of unique names from the 2 tables. If you want them to get new ids (like Alis does in your desired results), that's something else and requires the answers to a couple of questions:
do any of the names need to maintain their previous id. And if they do, which table's id should be preferred?
why do you have 2 tables with the same structure? ie what are you trying to accomplish when you generate the unique name list?