Retrieving a set of all Nth children - sql

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.

Related

H2 SQL Sequence count with duplicate values

I have a table of IDs, with some duplicates and I need to create a sequence based on the IDs. I'm trying to achieve the following.
[ROW] [ID] [SEQID]
1 11 1
2 11 2
3 12 1
4 13 1
5 13 2
I'm using an old version of the H2 DB which doesn't have use of windows functions so I have todo this using straight SQL. I have tried joining the table on itself but I'm not getting the result I want as the duplicate values cause issues, any ideas? I have unique identifier in row number, but not sure how to use this to achieve what I want?
SELECT A.ID, COUNT(*) FROM TABLE A
JOIN TABLE B
ON A.ID = B.ID
WHERE A.ID >= B.ID
GROUP BY A.ID;
Use a subquery that counts the seqid:
select
t.row, t.id,
(select count(*) from tablename where id = t.id and row <= t.row) seqid
from tablename t
It's not as efficient as window functions but it does what you expect.
See the demo (for MySql but it's standard SQL).
Results:
| row | id | seqid |
| --- | --- | ----- |
| 1 | 11 | 1 |
| 2 | 11 | 2 |
| 3 | 12 | 1 |
| 4 | 13 | 1 |
| 5 | 13 | 2 |

Query returned with an extra column in sql -ms access

So I am wondering. I fell into an interesting suggestion from another developer. So i basically have two tables I join in a query and I want the resulting table from the query to have an extra column that comes from the table on from the joint.
Example:
#table A: contains rating of players, changes randomly at any date depending
#on drop of form from the players
PID| Rating | DateChange |
1 | 2 | 10-May-2014 |
1 | 4 | 20-May-2015 |
1 | 20 | 1-June-2015 |
2 | 4 | 1-April-2014|
3 | 4 | 5-April-2014|
2 | 3 | 3-May-2015 |
#Table B: contains match sheets. Every player has a different match sheet
#and plays different dates.
MsID | PID | MatchDate | Win |
1 | 2 | 10-May-2014 | No |
2 | 1 | 15-May-2015 | Yes |
3 | 3 | 10-Apr-2014 | No |
4 | 1 | 21-Apr-2015 | Yes |
5 | 1 | 3-June-2015 | Yes |
6 | 2 | 5-May-2015 | No |
#I am trying to achieve this by running the ms-access query: i want to get
#every players rating at the time the match was played not his current
#rating.
MsID | PID | MatchDate | Rating |
1 | 2 | 10-May-2014 | 4 |
2 | 1 | 15-May-2015 | 2 |
3 | 3 | 10-Apr-2014 | 4 |
4 | 1 | 21-Apr-2015 | 4 |
5 | 1 | 3-June-2015 | 20 |
6 | 2 | 5-May-2015 | 3 |
This is what I have tried below:
Select MsID, PID, MatchDate, A-table.rating as Rating from B-table
left Join A-table
on B-table.PID = A-table.PID
where B-table.MatchDate > A-table.Datechange;
any help is appreciated. The solution can be in Vba as long as it returns something like a view/table I can manipulate using other queries or report.
Think of this in terms of sets of data... you need a set that lists the MAX dateChange for each player's and match date.
Soo...
SELECT MAX(A.DateChange) MDC, A.PID, B.Matchdate
FROM B-table B
INNER Join A-table A
on B.PID = A.PID
and A.DateChange <= B.MatchDate
GROUP BY A.PID, B.Matchdate
Now we take this and join it back to what you've done to limit the results in table A and B to ONLY those with that date player and matchDate (my inline table C)
SELECT B.MsID, B.PID, B.MatchDate, A.rating as Rating
FROM [B-table] B
INNER JOIN [A-table] A
on B.PID = A.PID
INNER JOIN (
SELECT MAX(Y.DateChange) MDC, Y.PID, Z.Matchdate
FROM [B-table] Z
INNER Join [A-table] Y
on Z.PID = Y.PID
and Y.DateChange <= Z.MatchDate
GROUP BY Y.PID, Z.Matchdate) C
on C.mdc = A.DateChange
and A.PID = C.PId
and B.MatchDate = C.Matchdate
I didn't create a sample for this using your data so it's untested but I believe the logic is sound...
Now Tested! SQL Fiddle using SQL server though...
My results don't match yours exactly. I think you're expected results are wrong though for MSID 4 given rules defined.

Exclude a group when one row match in another table

I have two tables :
the first one called "card" with one column "id".
| id |
| 1 |
| 2 |
| 3 |
| .. |
The second table is named "waste" with two columns "card_id" and "waste_type".
| card_id | waste_type |
| 1 | 1 |
| 1 | 3 |
| 2 | 2 |
| 2 | 1 |
And i want to select only the card where there is no waste_type = 2
The query should look like this :
SELECT c.id FROM card c
JOIN waste w
ON c.id = w.card_id
WHERE waste_type <> 2
I want this result :
id
1
But i get :
id
1
2
How can i do that ? Thank you so much in advance !
You should use a not exists clause for that.
select c.id
from card c
where not exists (select null from waste w
where w.card_id = c.id
and w.waste_type = 2)
With your query, I would guess you rather retrieve
1
1
2

Determine whether a specific entry has any children in a hierarchical database

I'm working on a recursive query for a hierarchal table in psql. While I'm able to produce an ordered, hierarchal list, I cannot figure out how to determine whether a parent has any children.
My code at the moment:
WITH RECURSIVE q AS (
SELECT h, 1 AS level, ARRAY[ordering] AS ordered_path, ARRAY[id] AS breadcrumb
FROM report h
WHERE parent IS NULL
UNION ALL
SELECT hi, q.level + 1 AS level, ordered_path || ordering, breadcrumb || id FROM q
JOIN report hi ON hi.parent = (q.h).id )
SELECT (q.h).id, (q.h).parent, (q.h).name, array_to_json(breadcrumb) AS breadcrumbs,
row_number() OVER (order by ordered_path) AS flat_order
FROM q
ORDER BY ordered_path
Which produces the following table:
id | parent | name | ordering | trail | rownum
----+--------+-----------------------+----------+--------------+--------
1 | | Entry 1 | 1 | [1] | 1
2 | 1 | Entry 2 | 1 | [1,2] | 2
15 | 2 | Entry 3 | 1 | [1,2,15] | 3
159 | 2 | Entry 4 | 2 | [1,2,159] | 4
16 | 2 | Entry 5 | 3 | [1,2,16] | 5
Essentially, I'd like a column that shows if a specific entry has any children. In this example, Entry 5 has no children.
The format of the original table is:
id | name | type | parent | ordering
-----+-----------------------------------------+---------+--------+----------
186 | Entry 1 | page | 172 | 23
154 | Entry 2 | page | 63 | 3
169 | Entry 3 | page | 163 | 3
Thanks!
You could use a correlated subquery as an extra field:
exists (select 1 from report where parent = q.id) as has_children
It's not necessarily the most efficient — though tbh, given the query, I'm can't think of anything better off the top of my head. But it'll work.
sql below fill find the child and will count you can change how you want the output by using case statements I tested the code, seems to be working
select x.Col1, count(y.Col1) as child from Table1 x
inner join Table2 y on x.Col1 = y.Col1
group by x.Col1

Set-based way to calculate family ranges in SQL?

I have a table that contains parents and 0 or more children for each parent, with a flag indicating which records are parents. All of the members of a given family have the same parent id, and the parent always has the lowest id in a given family. Also, each child has a value associated with it. (Specifically, this is a database of emails and attachments, where each parent is an email and the children are the attachments.)
I have two fields I need to calculate:
Range = {lowest id in family} - {highest id in family} [populated for all members]
Value-list = {delimited list of the values of each child, in id order} [only for parent]
So, given this:
Id | Parent| HasChildren| Value | Range | Value-list
----------------------------------------|-----------
1 | 1 | 1 | | |
2 | 1 | 0 | a | |
3 | 1 | 0 | b | |
4 | 4 | 1 | | |
5 | 4 | 0 | c | |
6 | 6 | 0 | | |
I would like to end up with this:
Id | Parent| HasChildren| Value | Range | Value-list
----------------------------------------|-----------
1 | 1 | 1 | | 1-3 | a;b
2 | 1 | 0 | a | 1-3 |
3 | 1 | 0 | b | 1-3 |
4 | 4 | 1 | | 4-5 | c
5 | 4 | 0 | c | 4-5 |
6 | 6 | 0 | | 6-6 |
How can I do this efficiently? Ideally, I'd like to do this with just set-based logic, without cursors, or even stored procedures. Temporary tables are fine.
I'm working in T-SQL, if that makes a difference, though I'd be curious to see platform agnostic answers.
The following SQLFiddle Solution should do the job for you, however as #Allan mentioned, you might want to revise your database structure.
Using CTE's:
Note: my query uses table1 as name of Your table
with cte as(
select parent
,ValueList= stuff(( select ';' +isnull(t2.Value, '')
from table1 t2
where t1.parent=t2.parent
order by t2.value
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 2, '')
from table1 t1
group by parent
),
cte2 as (select parent
, min(id) as firstID
, max(id) as LastID
from table1
group by parent)
select *
,(select FirstID from cte2 t2 where t2.parent=t1.parent)+'-'+(select LastID from cte2 t2 where t2.parent=t1.parent) as [Range]
,(select ValueList from cte t2 where t1.parent=t2.parent and t1.[haschildren]='1') as [Value -List]
from table1 t1