SQL to identify duplicates in a tree-like structure - sql

I am looking for a solution for this (MS SQL 2008, btw):
ID | ParentID | Feature_1 | Feature_2
+-----+------------+------------+----------+
1 | NULL | A | B
2 | 1 | A | B
3 | 1 | A | C
4 | 2 | A | C
Whenever a child (a record with a ParentID) has the same set of features (Feature_1 and Feature_2) than its parent, I want to ignore it, essentially not show it in my select *.
So the result set should be
ID | ParentID | Feature_1 | Feature_2
+-----+------------+------------+----------+
1 | NULL | A | B
3 | 1 | A | C
4 | 2 | A | C
Note that ID=2 is dropped, but ID=4 is displayed because it has a different set of features than its parent had.
Any help would be much appreciated!

SELECT
Child.ID,
Child.ParentID,
Child.Feature_1,
Child.Feature_2
FROM
MyTable AS Child
LEFT OUTER JOIN MyTable AS Parent
ON Child.ParentID = Parent.ID
WHERE
Parent.Feature_1 <> Child.Feature_1
OR Parent.Feature_2 <> Child.Feature_2
OR Child.ParentID IS NULL
ORDER BY
Child.ID

SELECT *
FROM table A
WHERE a.ParentID IS NULL OR NOT EXISTS (SELECT 1
FROM table b
WHERE a.ParentID = b.ID
AND a.Feature_1 = b.Feature_1 AND a.Feature_2 = b.Feature_2)

Related

How do I query a master-detail result having only the last detail row in MS-Access?

I have the following master table
.------------------.
| id | parent_name |
.------------------.
| 1 | Mike |
| 2 | Sarah |
| 3 | Danial |
| 4 | Alex |
.------------------.
And have the following child-table details:
.------------------------------------------.
| id | parent_id | child_name | birth year |
.------------------------------------------.
| 1 | 1 | mandy | 2000 |
| 2 | 1 | mark | 2003 |
| 3 | 1 | mathew | 2005 |
| 4 | 2 | sandy | 1998 |
| 5 | 2 | sharon | 2006 |
| 6 | 3 | david | 2001 |
.------------------------------------------.
In the example above, I delibretaly choose names of children with the first letter matching their parents' names just to make it easier to understand the relationship, even though each child is connected to his/her parent using the parent_id.
What I would like to have is a list of all parents (4 rows) and to have a matching 4 rows from the children table, selecting only the last born child of each respectful parent.
.-------------------------------.
| id | parent_name | last_child |
.-------------------------------.
| 1 | Mike | mathew |
| 2 | Sarah | sharon |
| 3 | Danial | david |
| 4 | Alex | (null) |
.-------------------------------.
In oracle, this is easy:
SELECT
p.id,
p.parent_name,
c.child_name last_child
FROM
parents_table p,
children_table c
WHERE
p.id = c.parent_id
AND c.birth_year = (SELECT MAX(birth_year) FROM children_table where parent_id = p.id)
But I am struggling to generate the same result in MS Access.. MS Access does not accept sub-queries (for select the child having the maximum birth year for the same parent).
Is there a better way to get the result in MS Access?
Access certainly does support subqueries, but you're using a crossjoin, so you will never get a null there.
Instead, left join and perform a subquery in the FROM clause.
Your query would fail identically in Oracle, by the way. There are no relevant differences between Access and Oracle here.
SELECT
p.id,
p.parent_name,
c.child_name last_child
FROM
parents_table p
LEFT JOIN
(
SELECT *
FROM children_table c
WHERE c.birth_year = (SELECT MAX(c2.birth_year) FROM children_table c2 WHERE c2.parent_id = c.parent_id)
) c
ON p.id = c.parent_id
Access sometimes performs better with an EXISTS, so a rewrite to that would be:
SELECT
p.id,
p.parent_name,
c.child_name last_child
FROM
parents_table p
LEFT JOIN
(
SELECT *
FROM children_table c
WHERE EXISTS(SELECT 1 FROM children_table c2 WHERE c2.parent_id = c.parent_id HAVING c.birth_year = MAX(c2.birth_year))
) c
ON p.id = c.parent_id
If you just want the child's name, you can use a correlated subquery:
select p.*,
(select top 1 child_name
from children_table as ct
where ct.parent_id = p.id
order by ct.birth_year desc, ct.child_name
) as youngest_child_name
from parents_table as p;
This can take advantage of an index on children_table(parent_id, birth_year desc, child_name). With the index, I would expect this to be quite fast.

How to fetch record from Table A which has not any approved status true on Table B

I have three table A , B and C and it structure is shown below
Table A
----------------------------
| id | Text_message_to_show|
----------------------------
| 1 | first demo message |
----------------------------
| 2 | second demo message |
----------------------------
Table B
------------------------------------
| id | request_id | approved_status |
------------------------------------
| 101 | 1 | 2 |
------------------------------------
| 102 | 1 | 1 |
------------------------------------
| 103 | 2 | 2 |
------------------------------------
| 104 | 2 | 2 |
------------------------------------
Table c
------------------------------------
| id | request_id | approved_status |
------------------------------------
| 501 | 1 | 2 |
------------------------------------
| 502 | 2 | 1 |
------------------------------------
Table B and Table C has foreign key request_id column which is reference id column of table A.Table Table A-> Table B has one to many relaionship and Table A->Table C has one to one relationship Now I have question is how to wrie sql query such that to fetch Table A record where no approved_status for request_id should be 2 in Table B And also Table C approved_status should not be 2
You can use not exists like following.
select *
from tablea ta
where not exists (
select 1
from tableb tb
where ta.id = tb.request_id
AND tb.approved_status = 2
)
You can use left join as follows:
Select a.*
From a
Left Join b on a.id = b.request_id and b.approved_status = 2
Left join c on a.id = c.request_id and c.approved_status = 2
Where coalesce(b.id,c.id) is null
select * from tablea ta
where not exists
(
select id from tableb tb where ta.id=tb.request_d and tb.approved_status = 2
)
This may help you out.

select Parent Child Rows in same Table

I have table structure like below
id |parent|value
1 | 0 | a |
2 | 1 | b |
3 | 4 | c |
4 | 0 | d |
5 | 0 | e |
I want to display only rows that have a relation parent child
like:
id |parent|value
1 | 0 | a |
2 | 1 | b |
3 | 4 | c |
4 | 0 | d |
every child should have a parent
every parent should have at least one child
This is my code but it does not work properly:
select a.id, a.parent,a.value
from myTable a inner join myTable b
on a.id = b.parent
union
select b.id, b.child,b.value
from myTable a inner join myTable b
on a.id = b.parent;
SELECT
*
FROM
yourTable
WHERE
parent != 0
OR EXISTS (SELECT *
FROM yourTable children
WHERE chilren.parent = yourTable.id
)
The first condition checks if the row points to a parent, and the second condition checks if the row has any children.

Using CASE for a specific situation - How TO

I'm trying to find the proper SQL for the following situation:
Supposed we have two tables:
TABLE A
ID int,
TEXT varchar(200)
TABLE B
ID int,
A_NO int,
B_NO int
Fields named "ID" on both tables can be join to link tables.
The following SQL:
SELECT
A.ID,
B.A_NO,
B.B_NO
FROM
A
LEFT JOIN
B
ON A.ID = B.ID
ORDER BY A.ID, B.A_NO, B.B_NO
gives the following results:
Now, the problem.
What is asked for is to have in the column B_NO a value = 1 for the MIN value of column A_NO and a value = 0 for all the others row with the same A_NO value.
The results below are expected:
Please note that, in this example, we can find two rows for each B_NO value but it is possible to have more than 2 rows.
I have tried to reproduce these results by using a CASE but with no success.
Thanks for you help in advance,
Bouzouki.
Try this using CTE and ROW_NUMBER(); (DEMO)
Please note: I have considered myT as your joined query of A and B tables for demo purpose. So replace myT with as yours A LEFT JOIN B ON A.ID = B.ID.
;with cte as (
select id, a_no, b_no,
row_number() over(partition by id,b_no order by a_no) rn
from myT
)
select id,a_no, case when rn=1 then b_no else 0 end b_no
from cte
order by a_no
--RESULTS FROM DEMO TABLE
| ID | A_NO | B_NO |
-------------------------
| 1031014 | 1 | 1 |
| 1031014 | 2 | 0 |
| 1031014 | 3 | 2 |
| 1031014 | 4 | 0 |
| 1031014 | 5 | 3 |
| 1031014 | 6 | 0 |
| 1031014 | 7 | 4 |
| 1031014 | 8 | 0 |
| 1031014 | 9 | 5 |
| 1031014 | 10 | 0 |
something like
select ID, a_no, b_no,
case when a_no = min_a_no then b_no else 0 end as new_b_no
from
a left join b on a.id = b.id left join
(Select ID, B_no, min(a_no) as min_a_no
from a left join b on a.id = b.id
group by id, b_no) m on a.id = m.id and b.b_no = m.b_no
ORDER BY A.ID, B.A_NO

Retrieving a set of all Nth children

The situation:
MySQL 5.0
2 tables
1-to-many parent-child foreign key relationship (A.ID = B.PARENT_ID)
What's needed:
A SELECT query that yields the set of all the Nth children (sorted by their IDs)
Also need a form for use in UPDATE
Example:
Table B
| ID | PARENT_ID |
------------------
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
| 6 | 2 |
| 7 | 2 |
| 8 | 3 |
| 9 | 3 |
| 10 | 4 |
Desired Result Set for, e.g., all 2nd children
| A.ID | B.ID | B.PARENT_ID |
-----------------------------
| 1 | 2 | 1 |
| 2 | 5 | 2 |
| 3 | 9 | 3 |
Perhaps something to do with a feature of GROUP BY that I am not seeing?
My mind has totally become stuck seeing the solution to this problem procedurally. All help much appreciated.
Don is correct - MySQL doesn't have ranking functionality. But luckily, it does allow you to initialize & reference a variable so you can code the logic for ranking.
This will return the rows for the 2nd children:
SELECT x.aid,
x.bid,
x.parent_id
FROM (SELECT a.id 'aid',
b.id 'bid',
b.parent_id,
CASE WHEN b.parent_id = #parent_id THEN #rownum := #rownum +1 ELSE #rownum := 1 END AS rownum,
#parent_id := b.parent_id
FROM TABLE_A a
JOIN TABLE_B b ON b.parent_id = a.id
JOIN (SELECT #rownum := 0, #parent_id := NULL) r
ORDER BY b.parent_id, b.id) x
WHERE x.rownum = 2
Change the WHERE x.rownum = ? to whatever Nth level children you want. The ORDER BY in the subquery is important to make sure the ranking comes out properly.
It's not clear to me how you want to use this for an UPDATE query - provide more detail & I'll update to suit your needs.
MySQL does not have a mechanism to do this Nth relation. Oracle has an extension and maybe others.
What is the context?
If you are building a HTML form for update, generate the data with for loop and put the ID's in the table for simple update upon submit.